Socket的分析与使用

来源:互联网 发布:c语言 socket服务端 编辑:程序博客网 时间:2024/06/05 01:00

Socket可以说是android中跨进程通信的一种方式,有时候也可以用于简单的网络传输,对于套接字连接,需要了解ServerSocket和Socket这两个流式套接字,这两个类的具体实现是有PlainSocketImpl实现的,它是SocketImpl的子类,同时SocketImpl也是一个是实现了SocketOption的抽象类。所以要了解Socket怎么用,就需要对这些都了解。下面介绍。



一、ScoketOptions



SocketOptions是为Scoket定义的一个用于设置或者获取Socket各种属性(比如 连接超时,缓冲区大小......)的接口。这里面包含了很多重要的Socket属性信息以及两个get,set方法,了解它对于以后使用Socket进行连接通讯,可以更加得心应手。现在按照笔者认为的便于大家理解的顺序来讲解。


①、设置SocketOptions里的各数据项的值:


public void setOption(int optID, Object val) throws SocketException;


用于将val值设置给指定的optID对象。


②、获取SocketOptions的各项数据值:


 public Object getOption(int optID) throws SocketException;


获取SocketOptions中指定optID的值。


接下来你会看到一堆静态常量,你可能会奇怪的是,final定义的常量为什么可以重新设置值。这里先打个预防针,因为SocketOptions定义的常量都是默认的常量值,也就是说你完全可以不理这些,你自己直接使用系统的值,但如果你要设置某个具体数据的值,可以通过setOptions来设置,这个方法的具体实现是在AcstractPlainSocketImpl里面,是根据optID来设置optID对应的局部变量的值。因此,我们调用此方法并不是直接修改optID的静态常量值,这点从ScoketOptions是一个接口就可以看出。下面介绍各个optID代表的意思。



③、public static final int SO_LINGER = 128


表示如果在关闭Socket的时候仍然有缓冲数据未发送,那么Socket在关闭之前应该等待的时间,128表示128秒。此值可以设为0,表示无论缓冲区是否还有数据未发送,当前的Socke都会马上关闭,并且close方法会立即执行。此值应该介于0-65535之间,大于65535将当作65535来对待。如果在指定的等待时间内,数据发送完毕,那么socket会正常关闭,否则将会强制关闭。



④、public static final int SO_TIMEOUT = 4102


表示连接超时时间(即在读取操作的时间不可以超过此时间值),如果是0表示没有连接超时时间,此值不可以是负值,单位是毫秒。



⑤、public static final int TCP_NODELAY = 1


指示数据是马上发送还是先缓存之后在发送,如果是马上发送会导致效率底下,缓存发送会有很高的效率。



⑥、public static final int SO_KEEPALIVE = 8


表示Socket是否发送检测连接是否存活的消息。如果在链接过程中,一直未得到对方的应答,socket会一直尝试连接,直到连接成功。


SocketOptions定义了连接的基本属性,接下来解析SocketImpl这个抽象类,此类定义了socket基本的方法,了解了这些方法的含义,有助于更好的了解Socket,因为Socket内部的操作是通过SocketImpl的子类来实现的。



二、SocketImpl



SocketImpl是一个抽象类,是所有流式套接字的基础,流式套接字主要有两个ServerSocket和Socket,前者一般用于服务端后者用于客户端,作为服务端通常会嵌入两种流式套接字,分别为ServerSocket表示用于监听客户端的对象,一旦连接成功会返回一个Socket,代表客户端的连接对象。而SocketImpl封装了一些上述流式套接字会用到的基本方法,下面来了解:



①、端口,ip

 protected InetAddress address
 protected int port;
protected int localport


其中address,port表示远程连接对象的主机嗲之和端口,localport表示绑定本地设备的端口。


②、监听:


protected abstract void accept(SocketImpl newSocket) throws IOException;


在有新的连接请求进来之前,会一直处于阻塞等待的状态,此方法用于监听客户端连接。


③、绑定:


protected abstract void bind(InetAddress address, int port) throws IOException;


将socket对象绑定到指定的本地主机地址和端口号。


④、关闭连接:


protected abstract void close() throws IOException;


关闭socket连接对象,此方法会导致导致socket对象不可再连接访问。


⑤、建立连接:


  protected abstract void connect(String host, int port) throws IOException;


此方法用于将socket对象连接到远程主机地址和端口号。


⑥、获取远程地址:


 protected InetAddress getInetAddress()


获取当前socket对象的远程连接对象的地址。


⑦、获取输入流:


 protected abstract InputStream getInputStream()


获取当前Socket对象的输入流,用来读取数据。


⑧、获取本地绑定端口:


 protected int getLocalPort()


获取socket绑定到本地地址的端口号。


⑨、获取输出流:


protected abstract OutputStream getOutputStream()

获取socket对象的输出流,用于发送数据。


⑩、获取远程连接对象的端口号


 protected int getPort()


此方法如果是被ServerScoket调用的话,是没有意义的。


more:关闭输入流输出流:


protected void shutdownInput() throws IOException
protected void shutdownOutput() throws IOException 

关闭输入流输出流。


SocketImpl是一个抽象类,它只是定义了用于scoket通信机制的基本方法,在Android中,默认使用的是PlainSocketImpl来实现基本的通讯细节。如果有需要可以自己实现SocketImpl的通信细节,可以参考PlainSocketImpl。下面讲解Socket。



三、Socket



主要用于提供基于Tcp传输协议的客户端套接字对象。要点如下:


①、构造函数:

 public Socket()
 public Socket(String dstName, int dstPort) 

两个构造函数都是使用了默认的SocketImpl的子类PlainSocketImpl的基本实现机制。后者还指定了远程连接对象的地址和端口。

②、关闭:

public synchronized void close()

调用了这个方法之后,就再也不可以连接到这个Socket对象了,除非重新创建一个实例。

③、获取远程连接对象的地址:

  public InetAddress getInetAddress()

此对象会返回远程连接对象的地址,如果返回null说明当前socket还未连接到远程对象。


④、输入流输出流:

public InputStream getInputStream()

public OutputStream getOutputStream()

获取输入流和输出流,输入流用来读取数据,输出流用来发送数据。


⑤、关闭输入流输出流:

public void shutdownInput() 

public void shutdownOutput()

前者用于关闭输入流,一旦关闭了,将不再接收任何远程对象发送的信息。如果在关闭了输入流之后又进行读取数据的操作会导致异常。
后者用于关闭输出流,一旦关闭,将不再发送任何数据。


⑥、绑定本地地址


 public void bind(SocketAddress localAddr)

用于将socket绑定到本地地址和本地端口,如果localAddr为null的话,将会从本地地址寻找任一可使用端口作为绑定端口。


⑦、远程连接


 public void connect(SocketAddress remoteAddr, int timeout) 

连接到指定地址的远程对象,timeout表示连接超时时间,可以传值0,0表示无穷大的连接超时时间。


综述,对于ScoketOptions里面提到的很多属性,可以同个scoket.setXXX的方法来设置。可以看到scoket封装了很多基本的方法,使用socket通信的时候,我们只需要关注scoket(如果需要自定义SokcetImpl,请参考plainSocketImpl)这一块就行了。下面再来了解ServerSocket.



四、ServerSocket



ServerSocket表示一个用于等待客户端连接的服务端对象,一旦有客户端连接成功,就可以进行一些适当的回应。同时,ServerSocket的socket通信机制也是又ScoketImpl来实现的,默认的是PlainSockImpl。ServerSocker用法上和Socket有很大相似之处,但也有不用的地方,比如ServerSocket拥有自己的阻塞队列用于接收客户端请求。下面讲解:


①构造函数:


public ServerSocket(int port) throws IOException

 public ServerSocket(int port, int backlog) throws IOException 

前者用于创建一个用指定端口指定的ServerSocket对象,后者顺带制订了阻塞队列的长度,这个值默认是50.一旦接受的客户端连接对象数量超过了这个值,那么所有超过这个值得连接对象都会被拒绝。


②、监听客户端连接:


 public Socket accept() throws IOException


用于监听客户端连接,此方法会导致线程阻塞,直到有新的连接进来,并且会返回一个socket对象,用于和客户端通信。


③、关闭监听:


public void close() throws IOException


调用此方法后,任何尝试建立连接都会失败。


ServerSocket很大程度上和Socket相似,这里就介绍这么多,接下来用一个例子讲解。


ServiceClient:


package com.example.hy.second.Socket;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.os.PersistableBundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import com.example.hy.second.R;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.io.StringReader;import java.net.Socket;/** * Created by X1Carbon on 2016/6/16. */public class ServiceClient extends Activity {    private EditText etContent;    private TextView tvMsg;    private Button btSend;    private final String path = "127.0.0.1";    private Socket socket = null;    private BufferedReader reader = null;    private PrintWriter writer = null;    private final int SOCKET_CONNECTED = 1, RECEIVED_MSG = 2;    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case SOCKET_CONNECTED:                    btSend.setEnabled(true);                    break;                case RECEIVED_MSG:                    tvMsg.setText(tvMsg.getText().toString() + "来自服务端的消息:" + msg.obj +"\n");                    break;            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.socket_layout);        etContent = (EditText) findViewById(R.id.etContent);        tvMsg = (TextView) findViewById(R.id.tvMsg);        btSend = (Button) findViewById(R.id.btSend);        Intent intent = new Intent(ServiceClient.this, ServiceSocket.class);        startService(intent);        clientThread.start();    }    private Thread clientThread = new Thread(new Runnable() {        @Override        public void run() {            while (socket == null) {                try {                    Message message=handler.obtainMessage();                    socket = new Socket(path, 8887);                    reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));                    writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);                    handler.sendEmptyMessage(SOCKET_CONNECTED);                    while (!ServiceClient.this.isFinishing()) {                        readReply();                    }                } catch (Exception e) {                    e.printStackTrace();                } finally {                    CloseUtils.close(reader);                    CloseUtils.close(writer);                }            }        }    });    private void readReply() {        if (reader != null) {            try {                String msg = reader.readLine();                if(msg!=null)                     Message.obtain(handler, RECEIVED_MSG, msg).sendToTarget();            } catch (IOException e) {                e.printStackTrace();            }        }    }    public void send(View view) {        if (writer != null) {            String msg = etContent.getText().toString();            writer.println(msg);            tvMsg.setText(tvMsg.getText().toString() + "我发送了:" + msg + "\n");        } else {            Toast.makeText(ServiceClient.this, "尚未链接", Toast.LENGTH_LONG).show();        }    }    @Override    protected void onDestroy() {        super.onDestroy();        try {            socket.shutdownOutput();            socket.shutdownInput();            socket.close();        } catch (Exception e) {            e.printStackTrace();        }    }}


serviceSocket:


package com.example.hy.second.Socket;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.support.annotation.Nullable;import android.util.Log;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.util.Random;/** * Created by X1Carbon on 2016/6/16. */public class ServiceSocket extends Service {    private boolean isServiceClose = false;    @Nullable    @Override    public IBinder onBind(Intent intent) {        return null;    }    private Thread serviceThread = new Thread(new Runnable() {        @Override        public void run() {            try {                ServerSocket server = new ServerSocket(8887);                while (!isServiceClose) {                    final Socket socket = server.accept();                    new Thread(new Runnable() {                        @Override                        public void run() {                            processSocket(socket);                        }                    }).start();                }            } catch (Exception err) {            }        }    });    private synchronized void processSocket(Socket socket) {        BufferedReader reader = null;        PrintWriter writer = null;        Random random = new Random();        try {            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));            writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);            while (!isServiceClose) {                String clientMsg = reader.readLine();//当收到的信息为null说明客户端关闭连接了                Log.i("service", "收到来自客户端的消息:" + clientMsg);                if (clientMsg == null)                    break;                else {                    writer.println(clientMsg.length());                }            }        } catch (IOException e) {            e.printStackTrace();        } finally {            CloseUtils.close(reader);            CloseUtils.close(writer);            try {                socket.shutdownInput();                socket.shutdownOutput();                socket.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    @Override    public void onDestroy() {        super.onDestroy();        isServiceClose = true;    }    @Override    public void onCreate() {        super.onCreate();        serviceThread.start();    }}


closeUtils:


package com.example.hy.second.Socket;import java.io.Closeable;import java.io.IOException;/** * Created by X1Carbon on 2016/6/16. */public class CloseUtils {    public static void close(Closeable obj) {        if (obj != null)            try {                obj.close();            } catch (IOException e) {                e.printStackTrace();            }    }}


布局:


<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <EditText        android:id="@+id/etContent"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:hint="请输入内容" />    <Button        android:id="@+id/btSend"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="send"        android:enabled="false"        android:text="发送" />    <ScrollView        android:layout_width="match_parent"        android:layout_height="wrap_content">        <TextView            android:id="@+id/tvMsg"            android:layout_width="match_parent"            android:layout_height="match_parent" />    </ScrollView></LinearLayout>


运行效果:





接下来读者好好体会一下(写的有点烦,所以没怎么解释,不过应该看得懂)。




---------文章写自:HyHarden---------

--------博客地址:http://blog.csdn.net/qq_25722767-----------













---------文章写自:HyHarden---------

--------博客地址:http://blog.csdn.net/qq_25722767-----------

2 0
原创粉丝点击