一、概述

  我们知道在开发中,即时通讯、设备间的通信都是使用 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. Kattis之旅——Inverse Factorial

    题目意思就是已知n的阶乘,求n. 当输入的阶乘小于10位数的时候,我们可以用long long将字符串转化成数字,直接计算. 而当输入的阶乘很大的时候,我们就可以利用位数去大概的估计n. //Asim ...

  2. Python实现京东自动登录

    配置好webdriver,用的是Chrome的 import cv2 import time import numpy as np from selenium import webdriver fro ...

  3. SQL语句的优化方法

    减少对数据库的查询次数 尽量使用相同的或非常类似的SQL语句进行查询 避免不带任何条件的SQL语句的执行 sql语句用大写 别名的使用(1.5倍)

  4. oracle No more data to read from socket之ora-07445排查解决

    今天下午,原来一个部门的同事找过来,说有个即将上线的环境偶尔会出现 No more data to read from socket错误,版本是oracle 11.2.0.1,如下: 经查,这个问题原 ...

  5. mysql 5.7/percona server/mariadb 10.2安装与服务器参数优化

    建议使用percona server linux generic版,从https://www.percona.com/downloads/Percona-Server-LATEST/下载,现在不在推荐 ...

  6. com.mchange.v2.c3p0.impl.NewPooledConnection@be1839d closed by a client的正确解答

    关于c3p0在debug模式下控制台抛出的如下异常: java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE at com.mchange. ...

  7. Linux 系统下安装 python-skimage

    Linux 系统下安装 python-skimage 安装必须的依赖 // python-mumpy // python-scipy // python-matplotlib $ sudo apt-g ...

  8. 20155201 网络攻防技术 实验五 MSF基础应用

    20155201 网络攻防技术 实验五 MSF基础应用 一.实践内容 一个主动攻击实践,如ms08_067 一个针对浏览器的攻击,如ms11_050 一个针对客户端的攻击,如Adobe 成功应用任何一 ...

  9. 图片上传前 压缩,base64图片压缩 Exif.js处理ios拍照倒置等问题

    曾写过在前端把图片按比例压缩不失真上传服务器的前端和后台,可惜没有及时做总结保留代码,只记得js利用了base64位压缩和Exif.js进行图片处理,还有其中让我头疼的ios拍照上传后会倒置等诸多问题 ...

  10. [转] Java中的final、static、this、super

    final 关键字 final关键字主要用在三个地方:变量.方法.类. 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改:如果是引用类型的变量,则在对其初始化之后便 ...