手把手教你用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天
目录
基本实现
-
实现客户端和服务端之间的通信
-
实现服务端转接客户端消息,并发送给其他局域网在线成员
-
实现服务端接收客户端消息,并判断相应类型,做出对应应答
-
实现客户端消息发送者 发送时间 当前在线用户基本可视化
问题分析
-
服务端开发
- 在IntelliJ IDEA中创建一个Java项目。
- 实现一个简单的TCP服务器,能够接收客户端消息并回显(或广播)消息给所有已连接的客户端。
-
客户端开发
- 在Android Studio中创建一个Android项目。
- 实现一个简单的TCP客户端,能够发送消息到服务端并显示从服务端接收到的消息。
-
网络通信
- 确保服务端和客户端在同一局域网内,并且客户端可以正确连接到服务端。
- 处理多线程问题,确保服务端可以同时处理多个客户端连接。
服务端
Idea:
结构预览

在Idea中创建一个名为Server的类
Server类
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class Server {
// 定义一个集合容器存储所有登陆进来的客户端,以便群发消息给他们
// 定义一个Map集合,键是存储客户端的管道,值是这个管道的名称
public static final Map<Socket, String> onLineSockets = new HashMap<>();
public static void main(String[] args) throws Exception {
System.out.println("服务端启动");
// 1. 创建服务端ServerSocket对象,绑定端口号,监听客户端连接
ServerSocket serverSocket = new ServerSocket(9999);
while (true) {
System.out.println("等待客户端连接....");
Socket socket = serverSocket.accept();
new ServerReader(socket).start();
System.out.println("一个客户端上线了.... IP:" + socket.getInetAddress().getHostAddress());
}
}
}
代码解读
- 定义一个Map集合(所有局域网用户共享集合)存储所有登陆进来的客户端,以便群发消息给他们,键是存储客户端的管道,值是这个管道的名称(说白了就是前一个是主键,后一个)
public static final Map<Socket, String> onLineSockets = new HashMap<>();
- 创建服务端ServerSocket对象,绑定端口号,监听客户端连接
ServerSocket serverSocket = new ServerSocket(9999);
- 端口号选择建议范围**(1024~65535)**,其中绝大多数没有被使用
while (true) {
System.out.println("等待客户端连接....");
Socket socket = serverSocket.accept();
new ServerReader(socket).start();
System.out.println("一个客户端上线了.... IP:" + socket.getInetAddress().getHostAddress());
}
-
使用无限循环,持续监听新的连接
-
**serverSocket.accept()**会堵塞线程,等待连接请求,直到收到一个新的请求,并与客户端建立新的通信管道用来传输数据
-
**new ServerReader(socket).start()**在建立新的管道后,会建立一个新线程用来与管道对应的客户端通信,这样就能实现多客户端之间通信
-
**socket.getInetAddress().getHostAddress()**用于获取新建连接的客户端Ip,并在Server类终端打印,便于服务端查看连接信息
在Idea中创建一个名为ServerReader的类
注:本文所有读取和发送都使用特殊流(DataInputStream与DataOutputStream)
ServerReader类
import java.io.*;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
public class ServerReader extends Thread {
private Socket socket;
public ServerReader(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
while (true) {
int type = dis.readInt(); // 1 2
switch (type) {
case 1:
String nickname = dis.readUTF();
// 登陆成功,将客户端socket存入在线集合
Server.onLineSockets.put(socket, nickname);
// 更新全部客户端的在线人数列表
updateClientOnLineUserList();
break;
case 2:
String msg = dis.readUTF();
sendMsgToAll(msg);
break;
default:
System.out.println("未知的消息类型: " + type);
break;
}
}
} catch (Exception e) {
System.out.println("客户端断开连接 IP:" + socket.getInetAddress().getHostAddress() + " 时间: " + LocalDateTime.now());
Server.onLineSockets.remove(socket); // 把下线的客户端socket从在线集合中移除
updateClientOnLineUserList();
}
}
// 给全部在线socket推送当前客户端发来的消息
private void sendMsgToAll(String msg) {
StringBuilder sb = new StringBuilder();
String name = Server.onLineSockets.get(socket);
// 获取当前时间
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EEE a");
String nowStr = dft.format(now);
String msgResult = sb.append(name).append(" ").append(nowStr).append("\r\n").append(msg).append("\r\n").toString();
// 推送给全部客户端
for (Socket clientSocket : Server.onLineSockets.keySet()) {
try {
DataOutputStream dos = new DataOutputStream(clientSocket.getOutputStream());
dos.writeInt(2); // 1代表告诉客户端接下来是在线人数列表信息 2代表发的是群聊消息
dos.writeUTF(msgResult);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 更新全部客户端的在线人数列表
private void updateClientOnLineUserList() {
// 拿到全部在线客户端的用户名称,把这些名称转发给全部在线socket管道
Collection<String> onLineUsers = Server.onLineSockets.values();
for (Socket clientSocket : Server.onLineSockets.keySet()) {
try {
DataOutputStream dos = new DataOutputStream(clientSocket.getOutputStream());
dos.writeInt(1); // 1代表告诉客户端接下来是在线人数列表信息 2代表发的是群聊消息
dos.writeInt(onLineUsers.size());
for (String onLineUser : onLineUsers) {
dos.writeUTF(onLineUser);
}
dos.flush


3636

被折叠的 条评论
为什么被折叠?



