Android进程间通信之Socket

来源:互联网 发布:淘宝网卡包 编辑:程序博客网 时间:2024/05/29 19:57

Socket也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应于网络传输控制层中的TCP和UDP协议。

TCP协议是面向连接的协议,提供稳定的双向通信功能,TCP连接的建立需要经过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因此具有很高的稳定性。

而UDP是无连接的,提供不稳定的单向通信功能,当然UDP也可以实现双向通信功能。

在性能上,UDP具有更好的效率,其缺点是不保证数据一定能够正确传输,尤其是在网络拥塞的情况下。

TCP协议中的三次握手和四次挥手:

建立TCP需要三次握手才能完成连接,而断开连接需要四次挥手。

连接过程:

1.客户端发送连接请求报文

2.服务端接收到连接请求后回复ACK报文,并为这次连接分配资源

3.客户端接收到ACK报文后也向服务端发送ACK报文,并分配资源

经过上面三次握手,TCP连接就建立成功了。

断开过程:断开连接请求可以是客户端发起,也可以是服务端发起,这里以客户端发起为例

1.客户方发送FIN报文

2.服务端接收到FIN报文后,发送ACK给客户端,客户端接收到消息后进入FIN_WAIT状态等待服务端的FIN报文

3.服务端确定数据已经发送完成则向客户端发送FIN报文

4.客户端收到FIN报文后发送ACK报文给服务端,并进入TIME_WAIT状态,如果服务端没有收到ACK报文则可以重传。服务端收到ACK后就知道可以断开连接了。客户端等待2MSL(最大报文段生存时间)后依然没有收到回复,则证明服务端已正常关闭,客户端也可以关闭连接了。

经过上面四次挥手,连接就断开了。

为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭Socket,所以只能先回复一个ACK报文,告诉客户端收到了客户端发送的FIN报文。只有等服务端所有的报文都发送完了,服务端才能发送FIN报文,因此不能一起发送。故需要四次握手。

为什么TIME_WAIT状态需要经过2MSL才能返回CLOSE状态?

答:网络是不可靠的,有可能造成最后一个ACK丢失,所有TIME_WAIT状态就是用来重发可能丢失的ACK报文的。

例子:聊天工具,服务端每收到一个连接请求后就创建一个Socket连接,这样服务端就可以和不同的客户端通信了。服务端每收到一条消息就自动回复一条消息。

服务端代码:

public class SocketTcpService extends Service {    private boolean mIsServerRunning;    private String[] mDefaultMsgs = new String[] { "hehe", "haha", "heihei" };    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onCreate() {        super.onCreate();        mIsServerRunning = true;        new Thread(){            public void run() {                ServerSocket serverSocket = null;                try {                    // 监听本地8688接口                    serverSocket = new ServerSocket(8688);                } catch (Exception e) {                    e.printStackTrace();                }                while(mIsServerRunning) {                    try {                        // 接收客户端请求                        final Socket socket = serverSocket.accept();                        System.out.println("accept");                        new Thread() {                            public void run() {                                try {                                    responseClient(socket);                                } catch (IOException e) {                                    e.printStackTrace();                                }                            };                        }.start();                    } catch (IOException e) {                        e.printStackTrace();                    }                }            };        }.start();    }    private void responseClient(Socket socket) throws IOException {        // 接收客户端消息        BufferedReader in = new BufferedReader(new InputStreamReader(                socket.getInputStream()));        // 向客户端发送消息        PrintWriter out = new PrintWriter(new BufferedWriter(                new OutputStreamWriter(socket.getOutputStream())), true);        System.out.println("欢迎来到聊天室");        while (mIsServerRunning) {            // 读取客户端发来的消息            String str = in.readLine();            // 判断客户端断开连接的方法很多,这里用输入流是否为null判断            if (str == null) {                break;            }            int i = new Random().nextInt(mDefaultMsgs.length);            String msg = mDefaultMsgs[i];            // 服务端发送消息            out.println("服务端:" + msg);        }        System.out.println("客户端退出");        out.close();        in.close();        socket.close();    }    @Override    public void onDestroy() {        super.onDestroy();        mIsServerRunning = false;    }}


客户端代码:客户端和服务端是在同一个应用中的不同进程

public class MainActivity extends Activity {    private Socket mSocket;    private Button mButton;    private TextView mText;    private PrintWriter mWrite;    private String[] mDefaultMsgs = new String[] { "xixi", "houhou", "aa" };    private Handler mHandler = new Handler() {        public void handleMessage(android.os.Message msg) {            switch (msg.what) {            case 0:                mButton.setEnabled(true);                break;            case 1:                mText.setText((String)msg.obj);                break;            }            super.handleMessage(msg);        };    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mButton = (Button) findViewById(R.id.btn);        mText = (TextView) findViewById(R.id.text);        mButton.setOnClickListener(mClickListener);        Intent service = new Intent(this, SocketTcpService.class);        startService(service);        startConnect();    }    private OnClickListener mClickListener = new OnClickListener() {                @Override        public void onClick(View v) {            int i = new Random().nextInt(mDefaultMsgs.length);            String msg = "客户端:" + mDefaultMsgs[i];            if (!TextUtils.isEmpty(msg) && mWrite != null) {                // 客户端发消息                mWrite.println(msg);                mText.setText(mText.getText() + msg + "\n");            }        }    };    private void startConnect() {        new Thread() {            public void run() {                while (mSocket == null) {                    try {                        mSocket = new Socket("localhost", 8688);                        mWrite = new PrintWriter(                                new BufferedWriter(new OutputStreamWriter(                                        mSocket.getOutputStream())), true);                        mHandler.sendEmptyMessage(0);                        System.out.println("连接成功");                    } catch (Exception e) {                        SystemClock.sleep(1000);                        System.out.println("连接失败,重新连接");                    }                }                try {                    BufferedReader reader = new BufferedReader(                            new InputStreamReader(mSocket.getInputStream()));                    while (!MainActivity.this.isFinishing()) {                        // 读取服务端发送来的消息                        String msg = reader.readLine();                        msg = mText.getText() + msg + "\n";                        if (msg != null) {                            mHandler.obtainMessage(1,msg).sendToTarget();                        }                    }                } catch (IOException e) {                    e.printStackTrace();                }            };        }.start();    }    @Override    protected void onDestroy() {        super.onDestroy();        if (mSocket != null) {            try {                mSocket.shutdownInput();                mSocket.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

 

1 0