IPC 之 Socket 的使用
一、概述
我们知道在开发中,即时通讯、设备间的通信都是使用 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 的使用的更多相关文章
- Linux IPC BSD socket编程基础
头文件 #include<unistd.h> #include <sys/types.h> #include <sys/socket.h> #include< ...
- [置顶] 深入理解android之IPC机制与Binder框架
[android之IPC机制与Binder框架] [Binder框架.Parcel.Proxy-Stub以及AIDL] Abstract [每个平台都会有自己一套跨进程的IPC机制,让不同进程里的两个 ...
- Android IPC 结篇
一.概述 Android 的 IPC 方式有 Bundle .共享文件.AIDL .Messenger .ContentProvider .Socket ,我们在实现进程间通信时要选择哪一种方式来实现 ...
- Android利用LocalSocket实现Java端进程与C端进程之间的IPC
Android是建立在Linux之上的OS,在涉及到安全.网络协议.文件加密等功能时,往往需要通过C语言调用底层API来实现,而如何发出指令让C端执行我们想要的功能,并且在执行之后有返回结果呢,这就需 ...
- 大型项目必备IPC之Binder机制原理(一)
阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680 摘要 Binder是Android系统进程间通信(IPC)方式之一.Li ...
- [转]Android Binder设计与实现 - 设计篇
摘要 Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有管道,system V IPC,socket等IPC手段,却还要倚赖Binder来实现进程间通信,说明Binder ...
- [转载] Android Bander设计与实现 - 设计篇
本文转载自: http://blog.csdn.net/chenxiancool/article/details/17454593 摘要 Binder是Android系统进程间通信(IPC)方式之一. ...
- 记一次Redis和NetMQ的测试
Redis是一个高速缓存K-V数据库,而NetMQ是ZeroMQ的C#实现版本,两者是完全不同的东西. 最近做游戏服务器的时候想到,如果选择一个组件来做服务器间通信的话,ZeroMQ绝对是一个不错的选 ...
- Android Bander设计与实现 - 设计篇
转自:http://blog.csdn.net/universus/article/details/6211589#t7 Binder Android IPC Linux 内核 驱动 摘要 Binde ...
随机推荐
- Kattis之旅——Inverse Factorial
题目意思就是已知n的阶乘,求n. 当输入的阶乘小于10位数的时候,我们可以用long long将字符串转化成数字,直接计算. 而当输入的阶乘很大的时候,我们就可以利用位数去大概的估计n. //Asim ...
- Python实现京东自动登录
配置好webdriver,用的是Chrome的 import cv2 import time import numpy as np from selenium import webdriver fro ...
- SQL语句的优化方法
减少对数据库的查询次数 尽量使用相同的或非常类似的SQL语句进行查询 避免不带任何条件的SQL语句的执行 sql语句用大写 别名的使用(1.5倍)
- oracle No more data to read from socket之ora-07445排查解决
今天下午,原来一个部门的同事找过来,说有个即将上线的环境偶尔会出现 No more data to read from socket错误,版本是oracle 11.2.0.1,如下: 经查,这个问题原 ...
- mysql 5.7/percona server/mariadb 10.2安装与服务器参数优化
建议使用percona server linux generic版,从https://www.percona.com/downloads/Percona-Server-LATEST/下载,现在不在推荐 ...
- 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. ...
- Linux 系统下安装 python-skimage
Linux 系统下安装 python-skimage 安装必须的依赖 // python-mumpy // python-scipy // python-matplotlib $ sudo apt-g ...
- 20155201 网络攻防技术 实验五 MSF基础应用
20155201 网络攻防技术 实验五 MSF基础应用 一.实践内容 一个主动攻击实践,如ms08_067 一个针对浏览器的攻击,如ms11_050 一个针对客户端的攻击,如Adobe 成功应用任何一 ...
- 图片上传前 压缩,base64图片压缩 Exif.js处理ios拍照倒置等问题
曾写过在前端把图片按比例压缩不失真上传服务器的前端和后台,可惜没有及时做总结保留代码,只记得js利用了base64位压缩和Exif.js进行图片处理,还有其中让我头疼的ios拍照上传后会倒置等诸多问题 ...
- [转] Java中的final、static、this、super
final 关键字 final关键字主要用在三个地方:变量.方法.类. 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改:如果是引用类型的变量,则在对其初始化之后便 ...