多客户端之间的通信
服务器可以与多个客户端实现通信了,那我们真正的目的是要实现多个客户端之间的通信,使用TCP协议实现的方案是:

客户端的数据包通过服务器中转,发送到另一个客户端,如下图所示:

java在线聊天的聊天记录如何保存_Java基础


java在线聊天的聊天记录如何保存_即时聊天简易版_02


这个数据包 包含的内容

java在线聊天的聊天记录如何保存_即时聊天简易版_03


客户端A要发送消息给客户端B

首先发送给服务器中处理客户端A的线程A1

A1检索到A要发送给B 所以A1联系B1 通过B1回传给客户端B 完成通信然后吧 这个服务器中的线程A1 B1就有了联系 所以不能再单独放了

把他们统一的管理起来 用 集合 的方式 Vector

java在线聊天的聊天记录如何保存_java在线聊天的聊天记录如何保存_04


MessageType

package com.vince.communication;

/**
 * Created by vince on 2017/6/7.
 */
public final class MessageType {
    public static final int TYPE_LOGIN = 0x1;//登录消息类型
    public static final int TYPE_SEND = 0x2;//发送消息的类型
}

消息包 Message
一会我们要发送这个消息对象 怎么发送 这里待会我们使用流发送 所以对象要序列化

package com.vince.communication;

import java.io.Serializable;

/**
 * Created by vince on 2017/6/7.
 * 消息包
 */
public class Message implements Serializable{
    private String from;//发送者
    private String to;//接收者
    private int type;//消息类型
    private String 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;
    }

    public Message() {
    }

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

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

服务器端

package com.vince.communication;

import javax.security.sasl.SaslClient;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
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;

/**
 * Created by vince on 2017/6/7.
 * 服务器端
 */
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 = 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 ois;
    private ObjectOutputStream oos;
    private boolean flag = true;

    public UserThread(Socket socket,Vector<UserThread> vector){
        this.socket = socket;
        this.vector = vector;
        vector.add(this);
    }
    @Override
    public void run() {
        try{
            System.out.println("客户端"+socket.getInetAddress().getHostAddress()+"已连接");
            ois = new ObjectInputStream(socket.getInputStream());
            oos = new ObjectOutputStream(socket.getOutputStream());

            while (flag){
                //读取消息对象
                Message msg  = (Message)ois.readObject();
                int type = msg.getType();
                switch (type){
                    case MessageType.TYPE_SEND:
                        String to = msg.getTo();
                        UserThread ut;
                        int size = vector.size();
                        for (int i = 0; i < size; i++) {
                            ut = vector.get(i);
                            if (to.equals(ut.name) && ut!=this){
                                ut.oos.writeObject(msg);
                                break;
                            }
                        }
                        break;
                    case MessageType.TYPE_LOGIN:
                        name = msg.getFrom();
                        msg.setInfo("欢迎你:");
                        oos.writeObject(msg);
                        break;
                }
            }
            ois.close();
            oos.close();
        }catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }
    }
}

客户端

package com.vince.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;

/**
 * Created by vince on 2017/6/7.
 */
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 oos = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
            //向服务器发送登录信息
            System.out.println("请输入名称:");
            String name = input.nextLine();
            Message msg = new Message(name, null, MessageType.TYPE_LOGIN, null);
            oos.writeObject(msg);
            msg = (Message) ois.readObject();
            System.out.println(msg.getInfo() + msg.getFrom());

            //启动读取消息的线程
            es.execute(new ReadInfoThread(ois));

            //使用主线程来实现发送消息
            boolean flag = true;
            while(flag){
                msg = new Message();
                System.out.println("To:");
                msg.setTo(input.nextLine());
                msg.setFrom(name);
                msg.setType(MessageType.TYPE_SEND);
                System.out.println("Info:");
                msg.setInfo(input.nextLine());
                oos.writeObject(msg);
            }

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

//读取消息
class ReadInfoThread implements Runnable {
    private ObjectInputStream in;

    public ReadInfoThread(ObjectInputStream in){
        this.in = in;
    }
    private boolean flag = true;

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

    @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();
        }
    }
}

先启动服务器

java在线聊天的聊天记录如何保存_java在线聊天的聊天记录如何保存_05


再启动两个客户端

java在线聊天的聊天记录如何保存_客户端_06


java在线聊天的聊天记录如何保存_Java基础_07


java在线聊天的聊天记录如何保存_java在线聊天的聊天记录如何保存_08


java在线聊天的聊天记录如何保存_Java基础_09


第一个客户端 起名字·bin

java在线聊天的聊天记录如何保存_客户端_10


第二个客户端叫vince

java在线聊天的聊天记录如何保存_java在线聊天的聊天记录如何保存_11


第一个客户端 bin 给vince发消息

java在线聊天的聊天记录如何保存_java_12


说 good good study day day up

java在线聊天的聊天记录如何保存_Java基础_13


然后第二个客户端vince收到消息

java在线聊天的聊天记录如何保存_java_14


然后给bin回消息

java在线聊天的聊天记录如何保存_客户端_15


java在线聊天的聊天记录如何保存_客户端_16


bin就收到了

java在线聊天的聊天记录如何保存_Java基础_17

启动客户端后 向服务器发送登录信息
第二步 启动接收线程 专门用来接收消息的线程
接下来使用主线程实现循环发送消息的过程

对服务器来说 服务器启动后 要读取

使用Vector来保存服务器当中的所有的用户线程任务Runnable(UserThread线程 )

每对应一个socket 即客户端的连接 就启动一个UserThread线程 每次都添加到Vector里面去
(这个添加的方法是 把对象vector添加到UserThread里面去 所以在构造方法 把UserThread自己添加进Vector里面)

然后开始启动线程 es.execute(user);

然后启动线程的第一步 run方法里面的
第一步 先把相关的流先构造完
然后就是循环 不断读取消息对象
读取的消息对象 取出类型进行判断
如果是登录信息 就返回一个欢迎你的消息
客户端就读取到欢迎信息 并输出出来 这是服务器端和客户端第一次交互

如果是发送消息的话
就取出要发送的对象
在集合Vector里面循环去找
找出所有的线程 判断要发送的人的名字跟线程保存的名字相不相同 (因为登陆的时候就把名字保存到线程了 所以他找的也是已经登陆的用户) 并且不是自己 毕竟这个循环会找到自己
找到了 就写到线程里面去 线程就会把消息发送出去