一、概述

  我们知道在开发中,即时通讯、设备间的通信都是使用 Socket 实现,那当然用它来实现进程间通信更是不成问题。Socket 即套接字,是一个对 TCP / IP协议进行封装 的编程调用接口(API) 。通过Socket,我们才能在 Andorid 平台上通过 TCP/IP 协议进行开发。Socket 不是一种协议,而是一个编程调用接口(API),属于传输层(主要解决数据如何在网络中传输)。

  Socket的使用类型主要有两种:

  • 流套接字(streamsocket) :基于 TCP 协议,采用 流的方式 提供可靠的字节流服务
  • 数据报套接字(datagramsocket):基于 UDP 协议,采用 数据报文 提供数据打包发送的服务

  对于 TCP 和 UDP 的讲述,这里就不做说明。

二、Socket 实现 IPC

  IPC 中两个重要角色: 客户端和服务端,示例工程仍旧是两个工程。我们先看服务端如何使用 Socket :

  1. 服务端

  服务端 Service 完整代码:

public class SocketService extends Service {

    private String[] mDefinedMsg = new String[]{"你好啊,哈哈", "请问你叫什么名字啊?", "今天北京天气不错啊", "你知道吗,我可以和多个人同时聊天", "给你讲个笑话吧,爱笑的人运气不会太差。"};

    int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES * 2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue); private boolean isServiceDestroy = false; @Override
public IBinder onBind(Intent intent) {
return null;
} @Override
public void onCreate() {
super.onCreate();
executorService.execute(new TcpServer());
} @Override
public void onDestroy() {
super.onDestroy();
isServiceDestroy = true;
} private class TcpServer implements Runnable { @Override
public void run() {
ServerSocket serverSocket = null;
try {
//接听本地8688接口
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
e.printStackTrace();
}
while (!isServiceDestroy && serverSocket != null) {
//接受客户端请求
try {
Socket client = serverSocket.accept();
executorService.execute(new TalkWithClient(client));
} catch (IOException e) {
e.printStackTrace();
}
}
}
} private class TalkWithClient implements Runnable { private Socket socket; TalkWithClient(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
//用于接收客户端信息
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//用于向客户端发送信息
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
out.println("欢迎来到聊天室");
while (!isServiceDestroy) {
String info = in.readLine();
if (info == null) { //客户端断开连接
break;
}
Log.i("CLIENT_INFO", info);
int i = new Random().nextInt(mDefinedMsg.length);
String msg = mDefinedMsg[i];
out.println(msg);
Log.i("Server_INFO", msg); }
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

  在服务端通过循环的方式轮询是否有客户端接入,并判断服务一旦停止,停止循环。 accpet() 方法在没有客户端接入时,会阻塞当前线程,等到有客户端接入时会继续执行。

 while (!isServiceDestroy && serverSocket != null) {
//接受客户端请求
try {
Socket client = serverSocket.accept();
executorService.execute(new TalkWithClient(client));
} catch (IOException e) {
e.printStackTrace();
}
}

  TalkWithClient 是一个 Runnable 实现类,用来与客户端进行交互。通过循环的方式读取客户端传递过来的信息,代码中 readLine() 方法也会阻塞,直到客户端有消息过来继续执行。

while (!isServiceDestroy) {
String info = in.readLine();
if (info == null) { //客户端断开连接
break;
}
Log.i("CLIENT_INFO", info);
int i = new Random().nextInt(mDefinedMsg.length);
String msg = mDefinedMsg[i];
out.println(msg);
Log.i("Server_INFO", msg); }

  2. 客户端

  客户端完整代码:

  

public class MainActivity extends AppCompatActivity {

    private static final int MESSAGE_SOCKET_CONNECTED = 1;
private static final int MESSAGE_RECEIVE_NEW_MSG = 2; int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES * 2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue); private Socket mClientSocket;
private PrintWriter mPrintWriter;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_SOCKET_CONNECTED:
btnSend.setEnabled(true);
break;
case MESSAGE_RECEIVE_NEW_MSG:
msgContainer.setText(String.format("%s%s", msgContainer.getText(), msg.obj));
break;
}
}
};
private TextView msgContainer;
private EditText etMsg;
private TextView btnSend; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
msgContainer = findViewById(R.id.msg_container);
etMsg = findViewById(R.id.msg);
btnSend = findViewById(R.id.btn_send);
btnSend.setEnabled(false); executorService.execute(new Runnable() {
@Override
public void run() {
connectTCPServer();
}
});
} @Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
} public void sendMsg(View view) {
final String msg = etMsg.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
executorService.execute(new Runnable() {
@Override
public void run() {
mPrintWriter.println(msg);
}
});
etMsg.setText("");
String time = formatTimes(System.currentTimeMillis());
String showMsg = "client" + time + ":" + msg + "\n";
msgContainer.setText(String.format("%s%s", msgContainer.getText(), showMsg));
}
} private void connectTCPServer() {
Socket socket = null;
while (socket == null) {
try {
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mClientSocket.getOutputStream())), true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
} catch (IOException e) {
SystemClock.sleep(1000);
//retry
}
}
//接收服务器消息
try {
BufferedReader br = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
while (!MainActivity.this.isFinishing()) {
String msg = br.readLine();
Log.i("CLIENT_RECEIVE", msg);
if (msg != null) {
String time = formatTimes(System.currentTimeMillis());
String showMsg = "server" + time + ":" + msg + "\n";
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showMsg).sendToTarget();
}
}
mPrintWriter.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
} @SuppressLint("SimpleDateFormat")
private String formatTimes(long millis) {
return "(" + new SimpleDateFormat("HH:mm:ss").format(new Date(millis)) + ")";
}
}

  连接服务端:

  connectTCPServer() 方法是用来连接服务端以及接收服务器消息,接收服务器消息仍然通过循环方式轮询。

  注意:

    Socket 实现 IPC 是网络操作,所以一定记得声明权限:

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

  最终能够进行传输数据,这里数据可以是任意数据,示例中只是传递了字符串。效果:

  示例工程代码:

   链接: https://pan.baidu.com/s/1Dq710Z4-vVsrNvgGUky09w密码: 52fv

IPC 之 Socket 的使用的更多相关文章

  1. Linux IPC BSD socket编程基础

    头文件 #include<unistd.h> #include <sys/types.h> #include <sys/socket.h> #include< ...

  2. [置顶] 深入理解android之IPC机制与Binder框架

    [android之IPC机制与Binder框架] [Binder框架.Parcel.Proxy-Stub以及AIDL] Abstract [每个平台都会有自己一套跨进程的IPC机制,让不同进程里的两个 ...

  3. Android IPC 结篇

    一.概述 Android 的 IPC 方式有 Bundle .共享文件.AIDL .Messenger .ContentProvider .Socket ,我们在实现进程间通信时要选择哪一种方式来实现 ...

  4. Android利用LocalSocket实现Java端进程与C端进程之间的IPC

    Android是建立在Linux之上的OS,在涉及到安全.网络协议.文件加密等功能时,往往需要通过C语言调用底层API来实现,而如何发出指令让C端执行我们想要的功能,并且在执行之后有返回结果呢,这就需 ...

  5. 大型项目必备IPC之Binder机制原理(一)

    阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680 摘要 Binder是Android系统进程间通信(IPC)方式之一.Li ...

  6. [转]Android Binder设计与实现 - 设计篇

    摘要 Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有管道,system V IPC,socket等IPC手段,却还要倚赖Binder来实现进程间通信,说明Binder ...

  7. [转载] Android Bander设计与实现 - 设计篇

    本文转载自: http://blog.csdn.net/chenxiancool/article/details/17454593 摘要 Binder是Android系统进程间通信(IPC)方式之一. ...

  8. 记一次Redis和NetMQ的测试

    Redis是一个高速缓存K-V数据库,而NetMQ是ZeroMQ的C#实现版本,两者是完全不同的东西. 最近做游戏服务器的时候想到,如果选择一个组件来做服务器间通信的话,ZeroMQ绝对是一个不错的选 ...

  9. Android Bander设计与实现 - 设计篇

    转自:http://blog.csdn.net/universus/article/details/6211589#t7 Binder Android IPC Linux 内核 驱动 摘要 Binde ...

随机推荐

  1. VMware无法读取USB文件

    今天碰到虚拟机内的Mac OS无法读取USB,经过一番查看,是Windows的服务里面的vmware usb arbitration service服务没有启动,再点击启动的时候,报错,提示本地文件找 ...

  2. 《web前端设计基础——HTML5、CSS3、JavaScript》 张树明版 简答题简单整理

    web前端设计基础——HTML5.CSS3.JavaScript 简答题整理 第一章 (1)解释一下名词的含义:IP地址.URL.域名   iP定义了如何连入因特网,以及数据如何在主机间传输的标准. ...

  3. phpstorm 一个窗口打开多个项目

    参考:https://imshusheng.com/php/135.html 文件 -> 设置 -> 项目"XXX" -> Directories- > A ...

  4. Codeforces 675E Trains and Statistic - 线段树 - 动态规划

    题目传送门 快速的vjudge通道 快速的Codeforces通道 题目大意 有$n$个火车站,第$i$个火车站出售第$i + 1$到第$a_{i}$个火车站的车票,特殊地,第$n$个火车站不出售车票 ...

  5. Codeforces 37D Lesson Timetable - 组合数学 - 动态规划

    题目传送门 神奇的门I 神奇的门II 题目大意 有$n$组学生要上课2次课,有$m$个教室,编号为$1$到$m$.要确定有多少种不同的安排上课的教室的方案(每组学生都是本质不同的),使得它们满足: 每 ...

  6. Git和Jenkins日记之没有新提交代码

    日期:2017/3/9 今天查看Jenkins运行代码记录的日志时,发现并没有昨天新提交的代码,然后查看了Jenkins的测试项目中所有的自动化测试用例, 并没有看到昨天新提交的测试用例,又查看了gi ...

  7. HDU - 1849 Rabbit and Grass 【Nim博弈】

    Problem Description 大学时光是浪漫的,女生是浪漫的,圣诞更是浪漫的,但是Rabbit和Grass这两个大学女生在今年的圣诞节却表现得一点都不浪漫:不去逛商场,不去逛公园,不去和AC ...

  8. 深度学习课程笔记(一)CNN 卷积神经网络

    深度学习课程笔记(一)CNN 解析篇 相关资料来自:http://speech.ee.ntu.edu.tw/~tlkagk/courses_ML17_2.html 首先提到 Why CNN for I ...

  9. es索引维护的常用帖子

    Elasticsearch 新增字段

  10. 【译】第9节---EF Code First中数据注解

    原文:http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx EF Code-First ...