Socket编程(三)---仿QQ多人聊天实例

上篇文章我们实现了一个简单的socket实例。实例的功能为当一个客户端连接服务端的时候,服务端打印客户端的连接信息,并向客户端发送一组数据并在服务端接收数据和打印,数据内容为服务器当前时间。
这次的实例,目的是为了模仿QQ上和好友聊天的功能,也就是用户登录后,点击用户头像,发送信息,好友就会接受到信息。

1.服务端

public class ChatServer {
    //用于保存客户端连接的Map
    public static Map<Integer, Socket> users = new HashMap<>();

    public static void main(String[] args) {
        ServerSocket server = null;
        System.out.println("服务端应用启动.....");
        try {
            server = new ServerSocket(7456);// 绑定本地的端口号
            Socket connection = null;
            while (true) {
                connection = server.accept();// 接收一个连接,在没有客户端访问的时候,accept会一直阻塞
                DataInputStream reader = new DataInputStream(connection.getInputStream());
                int uid = reader.readInt();//用户在连接的时候会先发送一个表示ID的数据
                // 获取客户端发来的数据
                System.out.println("一个连接建立:" + connection.getInetAddress() + ",用户ID:" + uid);
                // 在服务器用户列表中加入一个连接
                users.put(uid, connection);
                ServerThread thread = new ServerThread(reader);
                thread.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (server != null)
                    server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这里的服务端使用了线程来实现数据的读写,和上篇文章的服务端()处理方式相比,上篇文章在和客户端建立一次连接并处理完一次事情后就断开了连接,这样无法实现多次数据的发送。在学习过程中,借鉴过网友的代码,代码类似下面的:

            //此代码与主要代码无关,只是用来分析问题
            ServerSocket server = new ServerSocket(7456);
            while (true) {
                Socket socket = server.accept();
                DataInputStream in = new DataInputStream(socket.getInputStream());
                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                while (true) {
                    // 读取来自客户端的信息
                    String accpet = in.readUTF();
                    System.out.println(accpet);
                    // 服务器端发给客户端
                    System.out.println("服务器:" + accpet);
                    out.writeUTF("服务器:" + accpet);
                }
            }

通过上面代码,是可以实现双向通信,而且网上只要搜socket仿qq之类的,出来都是类似模式的。这样的交互模式,是可以让初学者体会到双向交互,但是这个交互是客户端和服务端之间。而且,最大的不足还有:通过上面代码我们可以发现,创建一个连接后进入while()就不会跳出,也就是说,一次只能有一个连接。所以,在我们这边使用线程来实现,在每次创建后使用线程来处理和每个客户端的数据交互。这样就可以实现多人聊天。线程实现代码如下:

public class ServerThread extends Thread {

    private DataInputStream reader;

    public ServerThread(DataInputStream reader) {
        this.reader = reader;
    }

    @Override
    public void run() {
        try {
            while (true) {
                String data = reader.readUTF();//获取客户端数据
                System.out.println(data);
                String[] ss = data.split(",");//数据发送格式
                Integer id = Integer.valueOf(ss[0]);//,前表示用户想法送信息的ID
                String msg = ss[1];//用户发送的数据内容

                Map<Integer, Socket> users = ChatServer.users;
                Socket toP = users.get(id);//根据ID从所有连接用户中获取对应连接
                DataOutputStream writer = new DataOutputStream(toP.getOutputStream());
                writer.writeUTF(msg);//发送数据
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.服务端

服务端为了实现用户在发送数据的同时也可以接收到别的用户发送来的数据,这里实现了读写的分离-也就是用户端在启动后会相应启动两个线程来实现用户对连接中数据的读和写。

//处理读的线程
public class ReadThread extends Thread {

    private Socket socket;

    public ReadThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try (DataInputStream reader = new DataInputStream(socket.getInputStream())) {
            while (true) {
                String data = reader.readUTF();
                System.out.println(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
//处理写的线程
public class WriteThread extends Thread {

    private Socket socket;

    public WriteThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        Scanner scanner = new Scanner(System.in);
        try (DataOutputStream writer = new DataOutputStream(socket.getOutputStream())) {
            while (true) {
                String msg = scanner.nextLine();
                writer.writeUTF(msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
//创建连接的类ChatClient 
public class ChatClient {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket("localhost", 7456);// 连接地址和端口号
            DataOutputStream writer = new DataOutputStream(socket.getOutputStream());
            int id = (int) (Math.random() * 1000);
            System.out.println("用户" + id + "与服务器成功建立连接......");
            // 向客户端发送本连接ID,ID为随机数产生
            writer.writeInt(id);
            //创建读写线程
            WriteThread wt = new WriteThread(socket);
            ReadThread rt = new ReadThread(socket);
            // 开启读写分离的线程
            wt.start();
            rt.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.测试和总结

服务端应用启动.....

在启动两个客户端后

用户565与服务器成功建立连接......
用户638与服务器成功建立连接......
服务端应用启动.....
一个连接建立:/127.0.0.1,用户ID:565
一个连接建立:/127.0.0.1,用户ID:638

之后,我们在ID565的控制台中向ID638发送数据,由于这里的限制,我使用英文的,来分隔,逗号前表示想访问的ID,逗号后面则是真正的聊天内容。其实,QQ聊天也是一样,在我们点击一个用户头像发送数据时,发送的数据也不单单就我们输入框的值,也会包含对方信息以及你的信息。

用户565与服务器成功建立连接......
638,你好,我是565

在638的控制台中,我们可以看到565发送来的数据

用户638与服务器成功建立连接......
你好,我是565

多人聊天的思路和功能基本实现,如果能用Swing做个窗口,那就更好了!

源码位置:https://github.com/oDevilo/Java-Network

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值