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代码量有点大,所以客户端代码将在下篇博客展示,并给出项目地址供大家学习优化!!

客户端博客地址如下:点击跳转

以下是Java实现P2P聊天通信的步骤: 1. 创建一个服务器,用于客户端之间的通信。服务器需要监听客户端的连接请求,并将客户端的信息存储在一个列表中。 2. 创建一个客户端,用于与其他客户端进行通信。客户端需要向服务器注册自己的信息,包括唯一主键、IP地址和端口号。 3. 当客户端想要与其他客户端进行通信时,它需要向服务器查询目标客户端的信息,包括IP地址和端口号。 4. 客户端通过目标客户端的IP地址和端口号连接到目标客户端,建立通信连接。 5. 一旦连接建立成功,客户端之间就可以通过套接字进行通信,发送和接收消息、图片、音频和视频等文件。 以下是Java实现P2P聊天通信的代码示例: 1. 服务器代码 ```java import java.io.*; import java.net.*; import java.util.*; public class Server { private static List<ClientInfo> clients = new ArrayList<ClientInfo>(); public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8888); System.out.println("Server started."); while (true) { Socket socket = serverSocket.accept(); System.out.println("New client connected: " + socket.getInetAddress().getHostAddress()); ClientInfo clientInfo = new ClientInfo(socket); clients.add(clientInfo); Thread thread = new Thread(new ServerHandler(clientInfo, clients)); thread.start(); } } catch (IOException e) { e.printStackTrace(); } } } class ServerHandler implements Runnable { private ClientInfo clientInfo; private List<ClientInfo> clients; public ServerHandler(ClientInfo clientInfo, List<ClientInfo> clients) { this.clientInfo = clientInfo; this.clients = clients; } public void run() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(clientInfo.getSocket().getInputStream())); while (true) { String message = reader.readLine(); System.out.println("Received message from " + clientInfo.getId() + ": " + message); if (message.startsWith("REGISTER")) { String[] parts = message.split(" "); clientInfo.setId(parts[1]); clientInfo.setIp(clientInfo.getSocket().getInetAddress().getHostAddress()); clientInfo.setPort(Integer.parseInt(parts[2])); System.out.println("Registered client " + clientInfo.getId() + " at " + clientInfo.getIp() + ":" + clientInfo.getPort()); } else if (message.startsWith("QUERY")) { String[] parts = message.split(" "); String targetId = parts[1]; ClientInfo targetClient = null; for (ClientInfo client : clients) { if (client.getId().equals(targetId)) { targetClient = client; break; } } if (targetClient != null) { String response = "CONNECT " + targetClient.getIp() + " " + targetClient.getPort(); PrintWriter writer = new PrintWriter(clientInfo.getSocket().getOutputStream()); writer.println(response); writer.flush(); System.out.println("Sent response to " + clientInfo.getId() + ": " + response); } else { String response = "ERROR Target client not found."; PrintWriter writer = new PrintWriter(clientInfo.getSocket().getOutputStream()); writer.println(response); writer.flush(); System.out.println("Sent response to " + clientInfo.getId() + ": " + response); } } else { // Forward message to target client String[] parts = message.split(" "); String targetId = parts[0]; ClientInfo targetClient = null; for (ClientInfo client : clients) { if (client.getId().equals(targetId)) { targetClient = client; break; } } if (targetClient != null) { PrintWriter writer = new PrintWriter(targetClient.getSocket().getOutputStream()); writer.println(message); writer.flush(); System.out.println("Forwarded message from " + clientInfo.getId() + " to " + targetClient.getId() + ": " + message); } else { String response = "ERROR Target client not found."; PrintWriter writer = new PrintWriter(clientInfo.getSocket().getOutputStream()); writer.println(response); writer.flush(); System.out.println("Sent response to " + clientInfo.getId() + ": " + response); } } } } catch (IOException e) { e.printStackTrace(); } } } class ClientInfo { private String id; private String ip; private int port; private Socket socket; public ClientInfo(Socket socket) { this.socket = socket; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public Socket getSocket() { return socket; } } ``` 2. 客户端代码 ```java import java.io.*; import java.net.*; public class Client { private static String id; private static String ip; private static int port; public static void main(String[] args) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); System.out.print("Enter your ID: "); id = reader.readLine(); Socket socket = new Socket("localhost", 8888); System.out.println("Connected to server."); PrintWriter writer = new PrintWriter(socket.getOutputStream()); writer.println("REGISTER " + id + " 0"); writer.flush(); BufferedReader serverReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String response = serverReader.readLine(); System.out.println("Received response from server: " + response); if (response.startsWith("CONNECT")) { String[] parts = response.split(" "); ip = parts[1]; port = Integer.parseInt(parts[2]); System.out.println("Connected to client " + id + " at " + ip + ":" + port); Thread thread = new Thread(new ClientHandler(socket)); thread.start(); while (true) { String message = reader.readLine(); Socket targetSocket = new Socket(ip, port); PrintWriter targetWriter = new PrintWriter(targetSocket.getOutputStream()); targetWriter.println(id + " " + message); targetWriter.flush(); targetSocket.close(); } } else { System.out.println("Failed to connect to client " + id); } } catch (IOException e) { e.printStackTrace(); } } } class ClientHandler implements Runnable { private Socket socket; public ClientHandler(Socket socket) { this.socket = socket; } public void run() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); while (true) { String message = reader.readLine(); System.out.println("Received message: " + message); } } catch (IOException e) { e.printStackTrace(); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值