前言:在Android中,应用进程间的通信有很多种,包括Socket、AIDL、广播等,基于socket通讯的方式有两种,一个是TCP的方式,一种是UDP的方式,两种通讯方式各有特点。本篇文章带领大家看一下关于Socket通讯的实践过程(附源码demo)
一. TCP方式,主要分为以下几个步骤:
这边利用TCP方式做了一个简单的服务端 + 多客户端 (一对多),实现群聊的功能,效果如下:
服务端聊天记录:
客户端聊天记录:
服务端:
public class TcpServerActivity extends AppCompatActivity {
private static final String TAG = "TcpServerActivity";
Button sendBtn;
EditText messageEdt;
HandlerThread handlerThread;
Handler handler;
ServerSocket serverSocket;
List<Socket> socketClients = new ArrayList<>();
volatile boolean isStop = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcp_server);
messageEdt = findViewById(R.id.message_edt);
sendBtn = findViewById(R.id.message_send_btn);
initListener();
//server开启接受客户端的消息
new ServerThread().start();
//初始化handlerThread
handlerThread = new HandlerThread("Thread-TcpServer");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
class ServerThread extends Thread {
@Override
public void run() {
try {
//1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
serverSocket = new ServerSocket(8888);
//2.调用accept()等待客户端连接
Log.d(TAG, "服务端" + IPUtils.getIPAddress(TcpServerActivity.this) + "已就绪,等待客户端接入.....");
while (!isStop) {
if (null != serverSocket) {
Socket socket = serverSocket.accept();
//每次客户端连接后,新启一个线程
new ReceiveThread(socket).start();
//添加到socket集合中
socketClients.add(socket);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ReceiveThread extends Thread {
private Socket socket;
private BufferedReader bufferedReader;
private BufferedWriter bufferedWriter;
public ReceiveThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
super.run();
String clientIP = socket.getInetAddress().getHostAddress();
Log.d(TAG, "客户端" + clientIP + "已经上线---");
//3.获取输出流,准备反馈信息给客户端
try {
String message = "新用户已经上线," + "目前聊天室人数为:" + socketClients.size();
sendMessageToAllClient(message);
//4.获取输入流,读取客户端信息
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
String info = "";
while ((info = bufferedReader.readLine()) != null) {
Log.d(TAG, "用户" + clientIP + "说:" + info);
if (info.contains("下线")) {
socketClients.remove(socket);
}
//需要把消息发给所有非当前用户的人
sendMessageToOtherClient(socket, info);
}
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//服务端主动发消息给客户端
public void startSend(String text) {
handler.post(new Runnable() {
@Override
public void run() {
sendMessageToAllClient(text);
Log.d(TAG, "服务端" + IPUtils.getIPAddress(TcpServerActivity.this) + "说: " + text);
}
});
}
public void sendMessageToAllClient(String message) {
BufferedWriter bufferedWriter = null;
try {
if (null != socketClients && socketClients.size() > 0) {
for (Socket socket : socketClients) {
bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write(message);
//切记一定要添加换行符,否则服务端无法读取到换行符,readLine一直在读
bufferedWriter.write("\n");
bufferedWriter.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendMessageToOtherClient(Socket currentSocket, String message) throws IOException {
BufferedWriter bufferedWriter = null;
if (null != socketClients && socketClients.size() > 0) {
for (Socket socket : socketClients) {
if (socket != currentSocket) {
bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write(message);
//切记一定要添加换行符,否则服务端无法读取到换行符,readLine一直在读
bufferedWriter.write("\n");
bufferedWriter.flush();
}
}
}
}
public void close() {
try {
if (null != socketClients && socketClients.size() > 0) {
for (Socket socket : socketClients) {
//关闭socket
if (null != socket) {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
socket = null;
}
}
}
//关闭serverSocket连接
if (null != serverSocket) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* server发送消息给client
*/
private void initListener() {
sendBtn.setOnClickListener(view -> {
String text = messageEdt.getText().toString().trim();
if (!TextUtils.isEmpty(messageEdt.getText().toString().trim())) {
Toast.makeText(this, "已发送!", Toast.LENGTH_SHORT).show();
startSend(text);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "服务端已经退出啦!");
close();
isStop = true;
}
}
客户端:
public class TcpClientActivity extends AppCompatActivity {
private static final String TAG = "TcpClientActivity";
Button sendBtn;
EditText messageEdt;
MyRunnable myRunnable;
HandlerThread handlerThread;
Handler handler;
BufferedReader bufferedReader;
BufferedWriter bufferedWriter;
Socket socket;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcp_client2);
messageEdt = findViewById(R.id.message_edt);
sendBtn = findViewById(R.id.message_send_btn);
initListener();
myRunnable = new MyRunnable();
new Thread(myRunnable).start();
//初始化handlerThread
handlerThread = new HandlerThread("Thread-TcpClient");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
class MyRunnable implements Runnable {
@Override
public void run() {
try {
Log.d(TAG, "客户端尝试连接服务器---");
//1.创建客户端Socket,指定服务器地址和端口
socket = new Socket("10.5.212.203", 8888);
if (null != socket) {
String serverAddress = socket.getInetAddress().getHostAddress();
String ip = IPUtils.getIPAddress(TcpClientActivity.this);
Log.d(TAG, "服务器连接成功,当前用户ip:" + ip + ",当前服务器ip:" + serverAddress);
//2.获取输出流,向服务器端发送信息表明已上线
bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String message = "我来了";
Log.d(TAG, "我说:" + message);
sendMessageToServer(message);
//3.获取输入流,不断从服务器接收内容
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
String info = "";
while ((info = bufferedReader.readLine()) != null) {
Log.d(TAG, "服务端" + serverAddress + "说:" + info);
}
bufferedReader.close();
} else {
Log.d(TAG, "TCP客户端连接服务器失败!");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void sendMessageToServer(String message) {
try {
bufferedWriter.write(message);
//切记一定要添加换行符,否则服务端无法读取到换行符,readLine一直在读
bufferedWriter.write("\n");
bufferedWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public void startSend(String text) {
//在统一新的线程中发送,避免耗时异常,注意每次调用sendMessageToServer
handler.post(new Runnable() {
@Override
public void run() {
sendMessageToServer(text);
Log.d(TAG, "我说:" + text);
}
});
}
public void close() {
try {
//关闭输入流连接
if (null != bufferedReader) {
bufferedReader.close();
}
//关闭输出流连接
if (null != bufferedWriter) {
bufferedWriter.close();
}
//关闭socket
if (null != socket) {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
socket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* client发送消息给server
*/
private void initListener() {
sendBtn.setOnClickListener(view -> {
if (!TextUtils.isEmpty(messageEdt.getText().toString().trim())) {
Toast.makeText(this, "已发送!", Toast.LENGTH_SHORT).show();
String text = messageEdt.getText().toString().trim();
startSend(text);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
startSend("我下线了,拜拜!");
close();
Log.d(TAG, "我下线了,拜拜!");
}
}
工具类:
public class IPUtils {
public static String getIPAddress(Context context) {
NetworkInfo info = ((ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
if (info != null && info.isConnected()) {
if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//当前使用2G/3G/4G网络
try {
//Enumeration<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
} else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//当前使用无线网络
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String ipAddress = intIP2StringIP(wifiInfo.getIpAddress());//得到IPV4地址
return ipAddress;
}
} else {
//当前无网络连接,请在设置中打开网络
}
return null;
}
/**
* 将得到的int类型的IP转换为String类型
*
* @param ip
* @return
*/
public static String intIP2StringIP(int ip) {
return (ip & 0xFF) + "." +
((ip >> 8) & 0xFF) + "." +
((ip >> 16) & 0xFF) + "." +
(ip >> 24 & 0xFF);
}
}
二:UDP方式:
发送方:
public class SendTest {
private static InetAddress mAddress;
private static DatagramSocket socket = null;
private static String ip = "255.255.255.255"; //发送给整个局域网
private static final int sendPort = 9999; //发送方和接收方需要端口一致
public static void SendUtils (final Context context, final String content) {
//初始化socket
try {
socket = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
}
try {
mAddress = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
e.printStackTrace();
}
//创建线程发送信息
new Thread() {
private byte[] sendBuf;
public void run() {
try {
sendBuf = content.getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
DatagramPacket recvPacket1 = new DatagramPacket(sendBuf, sendBuf.length, mAddress, sendPort);
try {
socket.send(recvPacket1);
socket.close();
Log.e("zziafyc", "已将内容发送给了AIUI端内容为:" + content);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
接收端:
public class ReceiveTest {
private DatagramSocket socket;
private static final int PhonePort = 9999;//手机端口号
private DatagramPacket packet;
private volatile boolean stopReceiver;
private void receiveMessage() {
new Thread() {
public void run() {
try {
socket = new DatagramSocket(PhonePort);
} catch (SocketException e) {
e.printStackTrace();
}
byte[] receBuf = new byte[1024];
packet = new DatagramPacket(receBuf, receBuf.length);
while (!stopReceiver) {
try {
socket.receive(packet);
String receive = new String(packet.getData(), 0, packet.getLength(), "utf-8");
Log.e("zziafyc", "收到的内容为:" + receive);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
边发送边接收:
public class SendAndReceiveTest {
private static InetAddress mAddress;
private static DatagramSocket socket = null;
private static String ip = "255.255.255.255"; //发送给整个局域网
private static int port = 8899; //发送方和接收方需要端口一致
public static void sendSearchGateWay(Context context, final String content, final String sid, int port) {
//初始化socket
try {
socket = new DatagramSocket();
//设置超时时间
socket.setSoTimeout(5000);
} catch (SocketException e) {
e.printStackTrace();
}
try {
mAddress = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
e.printStackTrace();
}
//创建线程发送信息
new Thread() {
private byte[] sendBuf;
public void run() {
try {
sendBuf = content.getBytes("utf-8");
} catch (Exception e) {
e.printStackTrace();
}
DatagramPacket packet = new DatagramPacket(sendBuf, sendBuf.length, mAddress, port);
try {
//已发送完成
socket.send(packet);
byte[] receive = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receive, receive.length);
while (true) {
socket.receive(receivePacket);
String result = new String(receivePacket.getData(), 0, receivePacket.getLength(), "utf-8");
Log.d("result: ", result);
if (!TextUtils.isEmpty(result) && result.contains(sid)) {
break;
}
}
} catch (SocketTimeoutException timeOutException) {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}