前面实现了服务器与多个客户端之间的通信,我们真正的目的是要实现多个客户端之间的通信

使用TCP协议实现的方案:

客户端的数据包通过服务器中转,发送到另一个客户端

java一个服务器多个客户端 java多客户端通信_java一个服务器多个客户端

 实现步骤:

1、对象序列化:(对象需要在网络上传输)

数据包(发送消息者,接收消息者,消息类型(登录、发送),发送的消息内容)

2.定义消息类型:

 消息类型:登录、发送

有兴趣的,可以自己扩展其他消息类型,如注册、退出等消息类型 

3、服务器:

启动服务器、用动态序列容器Vector保存在服务器中保存的所有线程的任务对象、每个对象创建一个线程来处理

4、客户端:

启动客户端、发送登录信息、接收消息的线程、处理消息发送的线程

 

代码实现:

1、对象序列化:

package com.lemon.communication;

import java.io.Serializable;

/**
 * 消息包
 * @author lemonsun
 */
public class Message implements Serializable {

    private String from; //发送者
    private String to;   //接收者
    private int type; //消息类型
    private String info;//消息包

    public Message() {
    }

    public Message(String from, String to, int type, String info) {
        this.from = from;
        this.to = to;
        this.type = type;
        this.info = info;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

2、定义消息类型

package com.lemon.communication;

/**
 * @author lemonsun
 */
public class MessageType {

    public static final int TYPE_LOGIN = 0x1; //登录消息类型
    public static final int TYPE_SEND = 0x2;  //发送消息的类型
}

3、服务器

package com.lemon.communication;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 服务器:
 * @author lemonsun
 */
public class Server {
    public static void main(String[] args) {
        //保存客户端处理的线程
        Vector<UserThread> vector = new Vector <>();
        //固定大小的线程池,用来处理不同客户端
        ExecutorService es = Executors.newFixedThreadPool(5);
        //创建服务器端的Socket
        try {
            ServerSocket server = new ServerSocket(8888);
            System.out.println("服务器以启动,正在等待连接...");
            while(true){
                //接受客户端的Socket,若没有,阻塞在这
                Socket socket = server.accept();
                //每来一个客户端,创建一个线程处理它
                UserThread user = new UserThread(socket,vector);
                es.execute(user);  //开启线程
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 客户端处理线程:
 */
class UserThread implements Runnable{
    private String name; //客户端的用户名称,唯一
    private Socket socket;
    private Vector<UserThread> vector;   //客户端处理线程的集合
    private ObjectInputStream oIn;    //输入流
    private ObjectOutputStream oOut;  //输出流
    private boolean flag = true;  //标记

    public UserThread(Socket socket, Vector<UserThread> vector) {
        this.socket = socket;
        this.vector = vector;
        vector.add(this);    //把当前线程也加入vector中
    }

    @Override
    public void run() {
        try {
            //1、构造输入输出流
            System.out.println("客户端:" + socket.getInetAddress().getHostAddress() + "已连接!");
            oIn = new ObjectInputStream(socket.getInputStream());
            oOut = new ObjectOutputStream((socket.getOutputStream()));
            //2、循环读取
            while(flag){
                //读取消息对象
                Message message = (Message)oIn.readObject();
                //获取消息类型,登录还是发送消息
                int type = message.getType();
                //3、判断
                switch (type){
                    //如果是发送消息
                    case MessageType.TYPE_SEND:
                        String to = message.getTo();//发送给谁
                        UserThread ut;
                        //遍历vector,找到接收信息的客户端
                        int size = vector.size();
                        for (int i = 0; i < size; i++) {
                            ut = vector.get(i);
                            //如果名字相同,且不是自己,就把信息发给它
                            if(to.equals(ut.name) && ut != this){
                                ut.oOut.writeObject(message); //发送消息对象
                            }
                        }
                        break;
                    //如果是登录
                    case MessageType.TYPE_LOGIN:
                        name = message.getFrom();//获取用户名
                        message.setInfo("欢迎您!");//设置登录成功信息
                        oOut.writeObject(message);
                        break;
                }

            }



        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

4、客户端:

package com.lemon.communication;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 客户端:
 *
 * @author lemonsun
 */
public class Client {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        //单线程池
        ExecutorService es = Executors.newSingleThreadExecutor();
        try {
            //创建客户端
            Socket socket = new Socket("localhost", 8888);
            System.out.println("服务器连接成功!");
            //构建输出输入流
            ObjectOutputStream oOut = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream oIn = new ObjectInputStream(socket.getInputStream());
            //1、客户端登录处理
            //向服务器发送登录信息(名字和消息类型)
            System.out.println("请输入名称:");
            String name = input.nextLine();
            //登录时,只自己的名字和消息类型为登录
            Message message = new Message(name, null, MessageType.TYPE_LOGIN, null);
            //发送给服务器
            oOut.writeObject(message);
            //服务器返回 欢迎信息
            message = (Message) oIn.readObject();
            //打印服务器返回的信息+当前客户端的名字
            System.out.println(message.getInfo() + message.getFrom());
            //2、启动读取消息的线程
            es.execute(new readInfoThread(oIn));  //读取线程完成

            //3、发送消息
            //使用主线程来发送消息
            boolean flag = true;
            //循环
            while(flag){
                //创建对象
                message = new Message();
                //发给谁
                System.out.println("To:");
                message.setTo(input.nextLine());
                //谁发的,从自己这发
                message.setFrom(name);
                //类型 发送消息
                message.setType(MessageType.TYPE_SEND);
                //发送的内容
                System.out.println("Info:");
                message.setInfo(input.nextLine());
                /*----到此需要发送的消息 对象 封装完毕----*/
                //发送给服务器
                oOut.writeObject(message);
            }
            
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 读取其他客户端发来消息
 */
class readInfoThread implements Runnable {
    private ObjectInputStream oIn; //输入流 用来读操作
    private boolean flag = true; //标记

    public readInfoThread(ObjectInputStream oIn) {
        this.oIn = oIn;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {

        try {
            //循环 不断读取消息
            while (flag) {
                //读取信息
                Message message = (Message) oIn.readObject();
                //输出用户名+内容
                System.out.println("[" + message.getFrom() + "]对我说:" + message.getInfo());
            }
            //没有数据就关闭
           if(oIn != null){
               oIn.close();
           }

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }


    }
}

开启服务器,开启几个客户端,就可以相互通信啦