Socket编程——多对多客户端连接 (有问题需解决)

Socket

概念

客户端多对多的实现的原理是把服务器作为一个中转站,中转站会会根据客户端的连接数量开辟用户线程,对接连接进来的客户端,客户端首先发送数据包给对接的用户线程, 数据包的内容包括

数据名 Value
from 信息起点
to 信息终点
info 发送信息
type 发送信息类型

把数据包包装成一个java类如下 Message.java:

package chat;

import java.io.Serializable;

public class Message implements Serializable {
    private String from;
    private String to;
    private String info;
    private int type;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", info='" + info + '\'' +
                ", type=" + type +
                '}';
    }

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

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

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

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

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

    public String getFrom() {
        return from;
    }

    public String getTo() {
        return to;
    }

    public String getInfo() {
        return info;
    }

    public int getType() {
        return type;
    }
}

服务器端收到数据包后,从to中获取要传送到的客户端,并且把数据包发送给与其对接的服务器端线程,最后服务器端线程会将信息发送给对应的客户端

原理图示

客户端1 服务器 客户端2 服务器线程1已开启,收到数据包信息,找到与to对应的服务器线程2发送数据包 info已收到,info显示 服务器线程2已开启,收到数据包信息,找到与to对应的服务器线程2发送数据包 info已收到,info显示 客户端1 服务器 客户端2

问题待解决

运行服务器和两个客户端时命令台输出如下,我添加了一个输出语句在客户线程接收到输入流后输出 Message包内容,和一个在客户端传送包内容之前输出Message包内容

传入登录数据包,服务器端和输出:

Message{from=‘bin’, to=‘null’, info=‘null’, type=1}

这里成功传入fromtype

第一次传输信息,info设置为服务器端输出:

Message{from=‘bin’, to=‘lili’, info=‘123’, type=2}

源代码

ChatServer.java:

package chat;

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;

public class ChatServer {
    public static void main(String[] args) {
        ExecutorService ex = Executors.newFixedThreadPool(5); //创建线程池,用于启动线程
        Vector<ClientThread> vectors = new Vector<>();  //创建集合,把线程放入集合用于储存和遍历所有线程
        try{
            ServerSocket server = new ServerSocket(6666);
            System.out.println("服务器已创建,等待连接...");
            while (true) {
                Socket socket = server.accept();  //等待客户端连接
                System.out.println(socket.getInetAddress().getHostAddress()+"已连接");  //获取并输出客户端IP地址
                ClientThread ct = new ClientThread(socket,vectors);  //创建客户线程,用于对接客户端
                ex.execute(ct);  //启动客户线程  下跳ClientThread
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

class ClientThread implements Runnable{
    private String name;  //等于 message.getFrom
    private Socket socket; 
    private Vector<ClientThread> vectors;
    private ObjectOutputStream oos;
    private ObjectInputStream ois;
    private boolean flag = true;
    public ClientThread(Socket socket,Vector<ClientThread> vectors){
        this.socket = socket;
        this.vectors = vectors;
        vectors.add(this);
    }
    @Override
    public void run() {
        try {
            oos = new ObjectOutputStream(socket.getOutputStream());
            ois = new ObjectInputStream(socket.getInputStream());
            
            while (flag){
                Message message = (Message) ois.readObject();
                System.out.println(message.toString());
                int type = message.getType();
                /*
                判断传送过来的数据包的类型
                是MessageType.TYPE_LOGIN则为登录
                是MessageType.TYPE_SEND则为信息
                 */
                switch (type){
                    case MessageType.TYPE_LOGIN:
                        name = message.getFrom();
                        message.setInfo("欢迎");
                        oos.writeObject(message);
                        break;
                    case MessageType.TYPE_SEND:
                        String to = message.getTo();
//                        ClientThread ct;
//                        int size = vectors.size();
//                        for(int i = 0 ; i<size ; i++){
//                            ct = vectors.get(i);
//                            if(to.equals(ct.name) && ct != this){
//                                ct.oos.writeObject(message);
//                                break;
//                            }
//                        }
                        for(ClientThread ct :vectors){
                            if(to.equals(ct.name) && ct != this){
                                ct.oos.writeObject(message);
                                break;
                            }
                        }
                        break;
                }
            }
            ois.close();
            oos.close();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

ChatClient.java

package chat;

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;

public class ChatClient {
    public static void main(String[] args) {
        ExecutorService es = Executors.newSingleThreadExecutor();  //创建单线程池
        Scanner in = new Scanner(System.in);
        Boolean flag = true;
        try {
            Socket socket = new Socket("localhost", 6666);
            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            System.out.println("请输入您的昵称");
            String name = in.nextLine();
            Message message = new Message(name,null,null,MessageType.TYPE_LOGIN);
            oos.writeObject(message);

            message = (Message) ois.readObject();
            System.out.println(message.getInfo()+":"+message.getFrom());
            es.execute(new ReadInfo(ois));

            message.setType(MessageType.TYPE_SEND);


            System.out.println("[To Who ?]:");
            message.setTo(in.nextLine());
            System.out.println("成功建立与" + message.getTo() + "的连接...请开始会话(输入quit退出)");
            while (flag) {
                message.setInfo(in.nextLine());
                System.out.println(message.toString());
                oos.flush();
                oos.writeObject(message);
            }


//            while (flag) {
//                System.out.println("To:");
//                message.setTo(in.nextLine());
//                message.setType(MessageType.TYPE_SEND);
//                System.out.println("info:");
//                message.setInfo(in.nextLine());
//                oos.writeObject(message);
//            }
        }catch (IOException |ClassNotFoundException e){
            e.printStackTrace();
        }
    }
}


class ReadInfo implements Runnable{
    private ObjectInputStream in;
    private boolean flag = true;
    public void setFlag(boolean flag){
        this.flag = flag;
    }
    public ReadInfo(ObjectInputStream in){
        this.in = in;
    }
    @Override
    public void run() {
        try {
            while (flag){
                    Message message = (Message) in.readObject();
                    System.out.println("["+message.getFrom()+"] :"+message.getInfo());

            }
            if(in!=null){
                in.close();
            }
        }catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_33021025/article/details/90077115