12.Java实现P2P聊天软件(服务器端实现)
经过前面一段时间的学习,我们学习到了如何利用Socket实现各种功能。
下面,激动人心的时刻到了。
我们要对前面学习提纯的Demo进行综合利用,做出一款真正的聊天软件,这个聊天软件不仅可以发送消息,也可以发送图片,音频和视频等文件。
而且我们要做出一款P2P的聊天软件,即客户端与客户端的点对点通信,无需通过服务器中转消息文件!!
为了便于高效开发我们把开发分为三个阶段:
1.架构设计
2.编码实现
3.测试运行
下面正式开始吧:
1.架构设计
首先我们知道,如果想和一台机器通信,必须要约定好IP地址和端口号,那么如何做才能让客户端与客户端获取对方的ip和端口呢?
这时,服务器的作用就体现了,服务器用来存储这些信息,并实现ip和端口号的转发,这样做大大减小了服务器的压力。
架构图如下:
通过上面的架构,客户端与客户端通信方式如下:
首先,每个客户端运行时都会将自己的如下信息送至服务器进行更新:客户端唯一主键 ,当前ip地址,端口号
然后,客户端之间通过各自的唯一主键去服务器进行查询,之后建立联系进行通信:
比如client1想和client2进行通信,那么client1就去服务器查询client2当前的ip和端口号,然后通过ip和端口号连接client2,连接成功后,二者就可以实现通信了。
2.编码实现
好了,经过面的设计,我们得到了一个初步的方案,那么下面就是编码实现环节了!!
首先进行服务器的实现:
我们知道,服务器的主要功能是客户端信息登记和转发处理,所以它必须实现一对多的通信。
代码如下:
package InternetCode.Socket.JJTalk;
import InternetCode.Socket.JJTalk.ServerUtil.ServerThread;
import InternetCode.Socket.JJTalk.pojo.ClientInfo;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class JJTalkServer {
// 声明ServerSocket对象
private ServerSocket server;
// 声明Socket对象socket
private Socket socket;
//客户端注册表
private Map<String, ClientInfo> registerMaps;
/**
* 新建服务器对象并等待连接
*/
public void getServer() {
try {
server = new ServerSocket(2006);
registerMaps=new HashMap<>();
while (true) {
// 监听是否有客户端连接
System.out.println("等待连接!!");
socket = server.accept();
System.out.println("连接成功!");
// 创建并启动连接线程对象
new ServerThread(socket,registerMaps).start();
}
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
public static void main(String[] args) {
JJTalkServer jjTalkServer=new JJTalkServer();
jjTalkServer.getServer();
}
}
逻辑如下:创建服务器套接字后对其进行监听,当监听到一个时创建一个连接线程进行交互。
连接线程代码如下:
package InternetCode.Socket.JJTalk.ServerUtil;
import InternetCode.Socket.JJTalk.pojo.ClientInfo;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Map;
public class ServerThread extends Thread{
// 创建流对象
private ObjectOutputStream out = null;
// 创建流对象
private ObjectInputStream in = null;
Socket socket;
//客户端注册表
Map<String,ClientInfo> registerMaps;
public ServerThread(Socket socket, Map<String,ClientInfo>registerMaps) throws IOException {
this.socket = socket;
this.registerMaps=registerMaps;
in=new ObjectInputStream(socket.getInputStream());
out=new ObjectOutputStream(socket.getOutputStream());
}
public void run() {
ClientInfo clientInfo = null;
try {
while (true){
clientInfo=(ClientInfo) in.readObject();
if(clientInfo!=null){
if(clientInfo.isQuery().equals("N")){
registerMaps.put(clientInfo.getClientId(),clientInfo);
System.out.println("注册成功:");
System.out.println(clientInfo.toString());
}else{
ClientInfo clientInfo1=registerMaps.get(clientInfo.getClientId());
if(clientInfo1!=null){
out.writeObject(clientInfo1);
System.out.println("查询完成:");
System.out.println(registerMaps.get(clientInfo.getClientId()).toString());
}else{
clientInfo1=new ClientInfo();
clientInfo1.setActive("F");
out.writeObject(clientInfo1);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(socket + "已经退出。\n");
}
}
}
客户端信息对象如下:
package InternetCode.Socket.JJTalk.pojo;
import java.io.Serializable;
public class ClientInfo implements Serializable {
/**
* 客户端id
*/
private String clientId;
/**
* 客户端ip
*/
private String ip;
/**
* 客户端端口号
*/
private String port;
/**
* 是否活跃
*/
private String isActive;
/**
* 是否查询
*/
private String isQuery;
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public String isActive() {
return isActive;
}
public void setActive(String active) {
isActive = active;
}
public String isQuery() {
return isQuery;
}
public void setQuery(String query) {
isQuery = query;
}
@Override
public String toString() {
return "ClientInfo{" +
"clientId='" + clientId + '\'' +
", ip='" + ip + '\'' +
", port='" + port + '\'' +
", isActive='" + isActive + '\'' +
", isQuery='" + isQuery + '\'' +
'}';
}
}
线程建立后便对客户端进行消息监听,一旦有消息传来便进行判断处理,来为各个客户端服务。
测试结果图:
由于此综合Demo代码量有点大,所以客户端代码将在下篇博客展示,并给出项目地址供大家学习优化!!
作者:small-water
出处:https://www.cnblogs.com/small-water/p/17870090.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)