您的位置:首页 > 移动开发 > Android开发

Android IPC机制(五)用Socket实现跨进程聊天程序

2017-08-06 12:11 501 查看
相关文章:

Android IPC机制(一)开启多进程

Android IPC机制(二)用Messenger进行进程间通信

Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

Android IPC机制(四)用ContentProvider进行进程间通信

1.Socket简单介绍

Socket也称作“套接字“,是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。

它分为流式套接字和数据包套接字。分别相应网络传输控制层的TCP和UDP协议。TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。它使用三次握手协议建立连接,而且提供了超时重传机制,具有非常高的稳定性。UDP协议则是是一种无连接的协议,且不正确传送数据包进行可靠性保证,适合于一次传输少量数据。UDP传输的可靠性由应用层负责。在网络质量令人十分不惬意的环境下。UDP协议数据包丢失会比較严重。

可是因为UDP的特性:它不属于连接型协议,因而具有资源消耗小。处理速度快的长处,所以通常音频、视频和普通数据在传送时使用UDP较多。



从上图我们也能够看出,不同的用户进程通过Socket来进行通信,所以Socket也是一种IPC方式,接下来我们用TCP服务来实现一个简单的聊天程序。

2.实现聊天程序服务端

配置

首先我们来实现服务端,当然要使用Socket我们须要在AndroidManifest.xml声明例如以下的权限:

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


我们须要实现一个远程的Service来当作聊天程序的服务端。AndroidManifest.xml文件里配置service:

<service
android:name=".SocketServerService"
android:process=":remote" />


实现Service

接下来我们在Service启动时,在线程中建立TCP服务。我们监听的是8688端口,等待client连接,当client连接时就会生成Socket。

通过每次创建的Socket就能够和不同的client通信了。当client断开连接时。服务端也会关闭Socket并结束结束通话线程。服务端首先会向client发送一条消息:“您好,我是服务端”,并接收client发来的消息,将收到的消息进行加工再返回给client。

package com.example.liuwangshu.moonsocket;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.text.TextUtils;
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;

public class SocketServerService extends Service {
private boolean isServiceDestroyed = false;

@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}

private class TcpServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket;
try {
//监听8688端口
serverSocket = new ServerSocket(8688);
} catch (IOException e) {

return;
}
while (!isServiceDestroyed) {
try {
// 接受client请求,而且堵塞直到接收到消息
final Socket client = serverSocket.accept();
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 {
// 用于接收client消息
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// 用于向client发送消息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
out.println("您好。我是服务端");
while (!isServiceDestroyed) {
String str = in.readLine();
Log.i("moon", "收到client发来的信息" + str);
if (TextUtils.isEmpty(str)) {
//client断开了连接
Log.i("moon", "client断开连接");
break;
}
String message = "收到了client的信息为:" + str;
// 从client收到的消息加工再发送给client
out.println(message);
}
out.close();
in.close();
client.close();
}

@Override
public void onDestroy() {
isServiceDestroyed = true;
super.onDestroy();
}
}


3.实现聊天程序client

clientActivity会在onCreate方法中启动服务端,并开启线程连接服务端Socket。为了确保能连接成功,採用了超时重连的策略。每次连接失败时都会又一次建立连接。连接成功后。client会收到服务端发送的消息:“您好,我是服务端”,我们也能够在EditText输入字符并发送到服务端。

package com.example.liuwangshu.moonsocket;

import android.content.Intent;
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.Button;
import android.widget.EditText;
import android.widget.TextView;
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 SocketClientActivity extends AppCompatActivity {
private Button bt_send;
private EditText et_receive;
private Socket mClientSocket;
private PrintWriter mPrintWriter;
private TextView tv_message;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket);
initView();
Intent service = new Intent(this, SocketServerService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectSocketServer();
}
}.start();

}

private void initView() {
et_receive= (EditText) findViewById(R.id.et_receive);
bt_send= (Button) findViewById(R.id.bt_send);
tv_message= (TextView) this.findViewById(R.id.tv_message);
bt_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String msg = et_receive.getText().toString();
//向server发送信息
if(!TextUtils.isEmpty(msg)&&null!=mPrintWriter) {
mPrintWriter.println(msg);
tv_message.setText(tv_message.getText() + "\n" + "client:" + msg);
et_receive.setText("");
}
}
});

}

private void connectSocketServer() {
Socket socket = null;
while (socket == null) {
try {
//选择和server同样的端口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 {
// 接收server端的消息
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();
}
}

}


布局非常easy(activity_socket.xml):

<?

xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/tv_message"
android:layout_width="match_parent"
android:layout_height="400dp" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal">

<EditText
android:id="@+id/et_receive"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
/>

<Button
android:id="@+id/bt_send"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="向server发消息" />
</LinearLayout>
</RelativeLayout>


4.执行聊天程序

执行程序,我们能够看到client和服务端是两个进程:



client首先会收到服务端的信息:”您好,我是服务端”。接下来我们向服务端发送“我想要怒放的生命”。

这时候服务端收到了这条信息并返回给client加工后的这条信息:



github源代码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: