Android IPC机制(五)用Socket实现跨进程聊天程序
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端口,等待客户端连接,当客户端连接时就会生成Socket。通过每次创建的Socket就可以和不同的客户端通信了。当客户端断开连接时,服务端也会关闭Socket并结束结束通话线程。服务端首先会向客户端发送一条消息:“您好,我是服务端”,并接收客户端发来的消息,将收到的消息进行加工再返回给客户端。
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 {
// 接受客户端请求,并且阻塞直到接收到消息
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 {
// 用于接收客户端消息
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// 用于向客户端发送消息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
out.println("您好,我是服务端");
while (!isServiceDestroyed) {
String str = in.readLine();
Log.i("moon", "收到客户端发来的信息" + str);
if (TextUtils.isEmpty(str)) {
//客户端断开了连接
Log.i("moon", "客户端断开连接");
break;
}
String message = "收到了客户端的信息为:" + str;
// 从客户端收到的消息加工再发送给客户端
out.println(message);
}
out.close();
in.close();
client.close();
} @Override
public void onDestroy() {
isServiceDestroyed = true;
super.onDestroy();
}
}
3.实现聊天程序客户端
客户端Activity会在onCreate方法中启动服务端,并开启线程连接服务端Socket。为了确保能连接成功,采用了超时重连的策略,每次连接失败时都会重新建立连接。连接成功后,客户端会收到服务端发送的消息:“您好,我是服务端”,我们也可以在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();
//向服务器发送信息
if(!TextUtils.isEmpty(msg)&&null!=mPrintWriter) {
mPrintWriter.println(msg);
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();
}
} }
布局很简单(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="向服务器发消息" />
</LinearLayout>
</RelativeLayout>
4.运行聊天程序
运行程序,我们可以看到客户端和服务端是两个进程:
客户端首先会收到服务端的信息:”您好,我是服务端”,接下来我们向服务端发送“我想要怒放的生命”。这时候服务端收到了这条信息并返回给客户端加工后的这条信息:
https://github.com/henrymorgen/MoonSocket
Android IPC机制(五)用Socket实现跨进程聊天程序的更多相关文章
- Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用
在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...
- java swing+socket实现多人聊天程序
swing+socket实现多人聊天程序 1.准备工作 先看效果: 客户端项目结构图: 服务端项目结构图: 2.运行原理 服务端 先开一个线程serverListerner,线程中开启一个Server ...
- Android IPC机制(三)使用AIDL实现跨进程方法调用
上一篇文章中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户 ...
- Android IPC机制全解析<一>
概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...
- 深入理解Android IPC机制之Binder机制
Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...
- Android IPC机制基础
概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...
- android IPC 机制 (开发艺术探索)
一.IPC 机制介绍 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程和线程 ...
- Android IPC机制—Binder的工作机制
进程和线程的关系 IPC机制即为跨进程通信,是inter-Process Communication的缩写.是指两个进程之间进行通信.在说进程通信之前,我们的弄明白什么是线程,什么是进程.进程和线程是 ...
- Android IPC机制之AIDL
什么是AIDL AIDL:Android Interface Definition Language,即Android接口定义语言. Android系统中的进程之间不能共享内存,因此,需要提供一些机制 ...
随机推荐
- 树莓派MQTT客户端搭建
树莓派安装和实现MQTT协议 下载Mosquitto 更新软件源:sudo apt-get update 下载g++编译器:sudo apt-get install g++ 安装:sudo apt- ...
- Nginx编译安装lua-nginx-module
lua-nginx-module 模块可以将Lua的强大功能嵌入NGINX服务器. 下载Nginx源码 如果已安装Nginx,需要查看当前安装版本的编译参数: $ /usr/local/nginx/s ...
- 编码(2)从字节理解Unicode(UTF8/UTF16)
https://www.cnblogs.com/zizifn/p/4716712.html 从字节理解Unicode(UTF8/UTF16) 如果你不知道或者不了解什么是Unicode/UTF8/UT ...
- Bootstrap-3-Typeahead
是Bootstrap-3-Typeahead,不是Twitter open source的typeahead,两者用法有差异.外加如果配合原生的Bootstrap3 的话推荐还是用这个.(当然Twit ...
- [总结] Min-Max容斥学习笔记
min-max 容斥 给定集合 \(S\) ,设 \(\max(S)\) 为 \(S\) 中的最大值,\(\min(S)\) 为 \(S\) 中的最小值,则: \[\max(S)=\sum_{T\in ...
- 翻译:SET子句(已提交到MariaDB官方手册)
本文为mariadb官方手册:SET的译文. 原文:https://mariadb.com/kb/en/set/我提交到MariaDB官方手册的译文:https://mariadb.com/kb/zh ...
- 入侵感知系列之webshell检测思路
Webshell检测 背景: 在B/S架构为主流的当下,web安全成了攻防领域的主战场,其中上传webshell是所有web黑客入侵后一定会做的事,所以检测网站中是否有webshell程序是判断被 ...
- flex属性
一.flex属性的归纳 flex-direction flex-wrap flex-flow justify-content align-items align-content 1.1 flex-di ...
- [心得] 如何利用liquibase進行資料庫版本控制 - 基礎觀念
前言 - 會寫這篇除了是要記錄一下使用的過程之外,也是發現到網路上找來的教學幾乎都是跟其它環境做結合 比較沒有單純利用command進行的流程.也沒有整體觀念的介紹,所以將我所理解的整理分享給大家. ...
- element-ui el-input只显示下划线
只需要增加样式 .el-input__inner { width: 220px; border-top-width: 0px; border-left-width: 0px; border-right ...