socket进程通信

socket是通过网络来进行进程间通信的。socket也称为“套接字”,是网络通信中的概念,分为流式套接字和用户数据报套接字两种,分别对应网络传输控制中TCP和UDP协议。TCP协议是面向连接的协议,提供稳定的双向通信功能,TCP连接的建立需要经过“经过三次握手”,还提供了超时重传的机制,具有很高的稳定性。而UDP是无连接的,提供不稳定的单身通信功能,UDP也可以实现双向通信功能。在性能上,UDP具有更好的效率,其缺点是不保证数据能够正确传输,尤其在网络拥塞的情况。

接下来介绍的是一个聊天程序,两个进程可以通过socket来实现信息的传输,socket本身可以传递任意字节流,这里仅传输文本,很显然这是一种IPC方式。

首先要声明权限:

1
2
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>

建立一个远程TCP服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public class TCPServerService extends Service {
private boolean isServiceDestoryed = false;
private String[] mDefinedMessages = new String[]{"你好", "what's you name?", "我很聪明"};
@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
isServiceDestoryed = true;
super.onDestroy();
}
private class TcpServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
System.err.println("建立tcp服务失败,port:8688");
e.printStackTrace();
return;
}
/**
* 此处循环接收客户端请求(有客户端连接就可做出反应,可同时和多个客户端连接)
* 每次有客户端连接就会生成一个新的socket
*/
while (!isServiceDestoryed) {
try {
//接收客户端请求
final Socket client = serverSocket.accept();
System.out.println("accepted");
new Thread() {
@Override
public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException {
//用于接收客户端消息
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
//用于向客户端发送消息
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(client.getOutputStream())), true);
out.print("welcome!");
while (!isServiceDestoryed) {
String str = in.readLine();
System.out.println("msg from client:" + str);
if (str == null) {
//客户端断开连接
break;
}
int i = new Random().nextInt(mDefinedMessages.length);
String msg = mDefinedMessages[i];
out.print(msg);
System.out.println("send :" + msg);
}
System.out.println("client out");
//关闭流
out.close();
in.close();
client.close();
}
}

接下来是客户端Activity,在onCreate里开户一个线程去连接服务端socket,为了能够确定连接成功,这里采用了超时重连的策略,为了降低重连的开销加入了休眠机制sleep。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.example.administrator.myapplication.socket;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.TextView;
import com.example.administrator.myapplication.R;
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.Socket;
public class SocketActivity extends AppCompatActivity {
//接收到了消息
private static final int MSG_RECEIVE = 1;
//连接服务端
private static final int MSG_CONNECTED = 2;
private TextView msgTextView;
private AutoCompleteTextView input;
private Button sendBtn;
private PrintWriter printWriter;
private Socket mClientSocket;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RECEIVE:
msgTextView.setText(msgTextView.getText() + "\n" + String.valueOf(msg.obj));
break;
case MSG_CONNECTED:
sendBtn.setEnabled(true);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket);
msgTextView = (TextView) findViewById(R.id.textView);
input = (AutoCompleteTextView) findViewById(R.id.input);
sendBtn = (Button) findViewById(R.id.button);
Intent service = new Intent(this, TCPServerService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectTCPServer();
}
}.start();
}
private void connectTCPServer() {
Socket socket = null;
while (socket == null) {
try {
socket = new Socket("localhost", 8688);
mClientSocket = socket;
printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
handler.sendEmptyMessage(MSG_CONNECTED);
System.out.println("连接成功!");
} catch (IOException e) {
e.printStackTrace();
SystemClock.sleep(1000);
System.out.println("连接失败!重试中。。。");
}
}
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!SocketActivity.this.isFinishing()){//当前activity没有关闭
String msg = bufferedReader.readLine();
System.out.println("接收:" + msg);
if (msg != null) {
handler.obtainMessage(MSG_RECEIVE, msg).sendToTarget();
}
}
System.out.println("quit...");
printWriter.close();
bufferedReader.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void onClick(View view) {
int id = view.getId();
switch (id){
case R.id.button:
final String msg = input.getText().toString();
if(!TextUtils.isEmpty(msg) && printWriter != null){
printWriter.println(msg);
input.setText("");
msgTextView.setText(msgTextView.getText() + "\n" + String.valueOf(msg));
}
}
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}

注意:socket不用时记得关闭!!!

这里客户端成功连接服务端后,用while循环来不断去读取服务端发来的消息,只有当activity退出时,退出循环并关闭socket。

参考:android开发艺术探索