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实现跨进程聊天程序的更多相关文章

  1. Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

    在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...

  2. java swing+socket实现多人聊天程序

    swing+socket实现多人聊天程序 1.准备工作 先看效果: 客户端项目结构图: 服务端项目结构图: 2.运行原理 服务端 先开一个线程serverListerner,线程中开启一个Server ...

  3. Android IPC机制(三)使用AIDL实现跨进程方法调用

    上一篇文章中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户 ...

  4. Android IPC机制全解析<一>

    概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...

  5. 深入理解Android IPC机制之Binder机制

    Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...

  6. Android IPC机制基础

    概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...

  7. android IPC 机制 (开发艺术探索)

    一.IPC 机制介绍 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程和线程 ...

  8. Android IPC机制—Binder的工作机制

    进程和线程的关系 IPC机制即为跨进程通信,是inter-Process Communication的缩写.是指两个进程之间进行通信.在说进程通信之前,我们的弄明白什么是线程,什么是进程.进程和线程是 ...

  9. Android IPC机制之AIDL

    什么是AIDL AIDL:Android Interface Definition Language,即Android接口定义语言. Android系统中的进程之间不能共享内存,因此,需要提供一些机制 ...

随机推荐

  1. dedecms中arclist标签做分页以及分页点击模块样式错乱问题

    in 使用织梦建站,通常会调用到一个文章列表,dedecms官网list标签没有提供typeid的属性,首页或多列表部分情况下使用分页并不方便,这就需要用arclist标签实现一个分页功能:以下还是展 ...

  2. Linux下安装、启动、停止mongodb

    1.下载完安装包,并解压 tgz(以下演示的是 64 位 Linux上的安装) curl .tgz # 下载 tar .tgz # 解压 mv mongodb/ /usr/local/mongodb ...

  3. Hulu大规模容器调度系统Capos

    Hulu是美国领先的互联网专业视频服务平台,目前在美国拥有超过2000万付费用户.Hulu总部位于美国洛杉矶,北京办公室是仅次于总部的第二大研发中心,也是从Hulu成立伊始就具有重要战略地位的分支办公 ...

  4. 静态和实例方法区别-java

    静态方法和实例方法的区别主要体现在两个方面:   在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式.而实例方法只有后面这 ...

  5. nohup & expect & netstat学习

    1.nohup 用途:不挂断地运行命令,通常加上‘&’命令,& 放在命令后面表示设置此进程为后台进程.分为两种情况,如下: 在不使用密码的情况下使用nohup,只需按如下形式即可: n ...

  6. python json模块出现Invalid control character这个异常的原因

    今天在查看日志的时候,发现一个问题,觉得很奇怪,跑了一个多月的代码突然报错了,还是个没见过的错误. 所以今晚抽空记录下. 一.重现错误 说好的传过来的数据是json格式的呢?怎么会突然报错了呢? 二. ...

  7. go基础系列:数组

    了解Python.Perl.JavaScript的人想必都知道它们的数组是动态的,可以随需求自动增大数组长度.但Go中的数组是固定长度的,数组一经声明,就无法扩大.缩减数组的长度.但Go中也有类似的动 ...

  8. C# 隐式转换关键字 implicit

    implicit 关键字用于声明隐式的用户定义类型转换运算符. 如果可以确保转换过程不会造成数据丢失,则可使用该关键字在用户定义类型和其他类型之间进行隐式转换. 引用摘自:implicit(C# 参考 ...

  9. 第一册:lesson eighty five。

    原文:Paris in the spring. A:Hello,Ken. B:Hi,George. A:Have you just been to the cinema? B:Yes,I have. ...

  10. __EF批量删除

    控制器: [HttpPost] public int Delete_All(string Id) { List<int> IDS = new List<int>();//创建l ...