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 ...
随机推荐
- pat 团体赛练习题集 L2-008. 最长对称子串
对给定的字符串,本题要求你输出最长对称子串的长度.例如,给定"Is PAT&TAP symmetric?",最长对称子串为"s PAT&TAP s&quo ...
- Prometheus监控学习笔记之在 HTTP API 中使用 PromQL
0x00 概述 Prometheus 当前稳定的 HTTP API 可以通过 /api/v1 访问. 0x01 API 响应格式 Prometheus API 使用了 JSON 格式的响应内容. 当 ...
- php定界符<<<EOF讲解
Heredoc技术.可用来输出大段的html和javascript脚本 1.PHP定界符的作用就是按照原样,包括换行格式什么的,输出在其内部的东西: 2.在PHP定界符中的任何特殊字符都不需要转义: ...
- Windows下用cmd命令实例讲解yii2.0 的控制台定时任务
Yii中的资源是和Web页面相关的文件,可为CSS文件,JavaScript文件,图片或视频等,资源放在Web可访问的目录下,直接被Web服务器调用. 有时候有些功能需要做到计划任务中去,因此就需要y ...
- fjwc2019 D2T3 排序(堆)
#183. 「2019冬令营提高组」排序 贴一段ppt 考虑模拟出这个算法进行k轮(即外层的i循环到k)时的序列,之后再暴力模拟零散的步. 考虑这个算法在01序列上的表现,k轮后实际上就是将最开始的不 ...
- bzoj5421:收藏家
bzoj5421 贴一张图 关于对问题的转化: 当两个人交换收藏品时,显然我们进行这个操作是为了得到更优解. 那么一个收藏品是有用的,另一个被换走的收藏品可以当做直接扔掉了. 所以只要保留一个就可以了 ...
- centOS 7 gitlab安装
https://www.cnblogs.com/chenfool/p/7689438.html 配置阿里巴巴 yum 源 wget -O /etc/yum.repos.d/CentOS-Base.re ...
- centos7 yum install timeout
https://yum.dockerproject.org/repo/main/centos/7/repodata/repomd.xml: [Errno 12] Timeout on https:// ...
- ssh-copy-id命令解析
ssh-copy-id命令可以把本地主机的公钥复制到远程主机的authorized_keys文件上, ssh-copy-id命令也会给远程主机的用户主目录(home)和~/.ssh, 和~/.ssh/ ...
- C# 字典常用方法
/* ######### ############ ############# ## ########### ### ###### ##### ### ####### #### ### ####### ...