Socket 也称为 “嵌套字”,是计算机网络中的概念,分为流式嵌套字(TCP)和用户数据报嵌套字(UDP)。
不同用户进程通过 Socket 进行通信也是一种 IPC 方式。
在使用 Socket 通信前应在 AndroidManifest 中声明权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
1、服务端
我们需要一个 Service 作为服务端,声明如下:
<service
android:name=".socket.SocketService"
android:enabled="true"
android:exported="true"
android:process=":remote" />
Socket 服务端在 Service 启动时,会建立 TCP 连接并监听 8688 端口。
public class SocketService extends Service {
private static final String TAG = "SocketService";
private boolean isDestroyed = false;
private String[] messages = new String[]{
"你好啊,哈哈",
"请问你叫什么名字呀?",
"今天北京天气不错啊",
"你知道吗?我可是可以和多个人同时聊天的哦",
"给你讲个笑话吧:据说爱笑的人运气不会太差,不知道真假。"
};
public SocketService() {
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new TCPServer()).start();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
isDestroyed = true;
super.onDestroy();
}
private class TCPServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
e.printStackTrace();
}
while (!isDestroyed) {
try {
final Socket client = serverSocket.accept();
Log.d(TAG, "accept");
new Thread() {
@Override
public void run() {
responseClient(client);
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void responseClient(Socket client) {
try {
// 接收客户端消息
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// 响应客户端消息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
Log.d(TAG, "欢迎来到聊天室!");
out.println("欢迎来到聊天室!");
while (!isDestroyed) {
String line = in.readLine();
Log.d(TAG, "message from Client: " + line);
if (line == null) break;
int i = new Random().nextInt(messages.length);
String message = messages[i];
out.println(message);
Log.d(TAG, "response to Client: " + message);
}
out.close();
in.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
当与客户端建立连接后,新建 Socket 客户端,接收消息并作出响应。
2、客户端
客户端部分首先启动 Socket 服务,并且在连接失败后会不断重新尝试连接。
public class SocketActivity extends AppCompatActivity {
private static final String TAG = "SocketActivity";
private Button bt_send;
private EditText et_receive;
private TextView tv_message;
private PrintWriter mPrintWriter;
private Socket mClientSocket;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket);
initView();
Intent service = new Intent(this, SocketService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectSocketServer();
}
}.start();
}
private void initView() {
et_receive = findViewById(R.id.et_receive);
bt_send = findViewById(R.id.bt_send);
tv_message = findViewById(R.id.tv_message);
bt_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String msg = et_receive.getText().toString();
//向服务器发送信息
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
Log.d(TAG, "onClick: " + msg);
new Thread(new Runnable() {
@Override
public void run() {
mPrintWriter.println(msg);
}
}).start();
tv_message.setText(tv_message.getText() + "\n" + "客户端:" + msg);
et_receive.setText("");
}
}
});
}
private void connectSocketServer() {
Socket socket = null;
while (socket == null) {
try {
//选择和服务器相同的端口8688
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
} catch (IOException e) {
SystemClock.sleep(1000);
}
}
try {
// 接收服务器端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!isFinishing()) {
final String msg = br.readLine();
if (msg != null) {
runOnUiThread(
new Runnable() {
@Override
public void run() {
tv_message.setText(tv_message.getText() + "\n" + "服务端:" + msg);
}
}
);
}
}
mPrintWriter.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
主要使用 socket.getOutputStream() 和 socket.getInputStream() 方法分别发送、接收服务端消息。
打印日志如下:
最终效果如下:
到这里把 Android IPC 通信的几种实现方式基本看了一遍,但是在 Binder 机制原理方面还有欠缺,后面再深入学习。
参考资料
1、《Android开发艺术探索》 -- 2.4.6 使用 Socket
2、Android IPC机制(五)用Socket实现跨进程聊天程序 | 刘望舒的博客
http://liuwangshu.cn/application/ipc/5-socket.html
评论 (0)