socket 简单的学习多人聊天的实现

本文是一个关于Java socket编程的教程,通过实例演示如何创建一个简单的多人聊天系统。作者首先介绍了基本的客户端与服务端聊天流程,接着讨论了如何实现群聊功能,包括用while循环监听客户端、线程处理并发通信以及解决客户端之间消息同步的问题。文章提供了完整的服务端和客户端代码,建议读者通过调试来理解整个聊天系统的运作机制。
摘要由CSDN通过智能技术生成

前言

新手驾到,请多多指教。心血来潮,忽然想了解下socket,那最好的方法就是写个demo然后记录下来。前面可能会有些bug,因为是在写博客的时候有些是直接修改的,不是直接复制代码的。完整的代码在最后面。

先来简要说下 客户端 与 服务端 聊天(详细看下代码注释吧)

服务端

// An highlighted block
	try {
            ServerSocket ss = new ServerSocket(10086);  // 创建服务器 端口号 10086

            Socket s = ss.accept(); // 监听是否有客户端加入

            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            //读取客户端发送来的消息
            String mess = br.readLine(); // br.readLine()读取客户端的输入
            System.out.println("客户端:" + mess);

            // 输入数据传输到当前连接的客户端
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            bw.write(mess + "\n"); //  把数据写到客户端去  \n 必须要加,不然没有消息
            bw.flush();

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

客户端

try {
            Socket s = new Socket("127.0.0.1", 10088);// 创建客户端new Socket(服务端IP, 服务端端口号)
            
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            //向服务器端发送一条消息
            bw.write("测试客户端和服务器通信,服务器接收到消息返回到客户端\n");
            bw.flush();

            //读取服务器返回的消息
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            String mess = br.readLine();
            System.out.println("服务器:" + mess);


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

从上面发现,服务端和客户端都差不多,获取消息,输出啊什么的。主要注意的是每次输出的时候需要加上 \n ,服务端带有监听是否有客户端加入这个功能,建议debug下,能够发现他们的流程是这样的。

  • 开启服务端,进入监听方法accept()然后等待 客户端加入,程序等待中。。。
  • 开启客户端,发送消息给服务端,然后进入readLine()方法,去获取服务端的输入的值,程序等待中。。。
  • 进入服务端,readLine()方法获取到客户端输入的内容,out.println打印,然后write()输入消息给客户端,程序结束。
  • 进入客户端, readLine()方法获取到服务端输入的值,读取打印,程序结束。

可以加入下面这段代码,让客户端和服务端不断的在聊天

	boolean fals = true;
	while (fals){

                // 输入数据
                System.out.println("请输入:");
                String str = sc.nextLine();
                if ("end".equals(str)){
                    fals = false;
                }
                
				// 读取数据
                String info = br.readLine();
                System.out.println("服务端:" + info);
                bw.write(str + "\n");
                bw.flush();
            }

下面,我们来进行 群聊功能

首先,要群聊,服务端肯定需要不断的监听客户端,那么我们用 while(true),不断的监听。然后就能获取到对应的 客户端 Socket,既然有多个,那么我们就用一个集合来 存储它们。

然后我们要用到线程,为什么呢?
从上面的流程中可以看出,服务端和客户端是一直在进行获取消息,传入消息这种不断的更替交流中,你另外一个客户端根本不可能插入进来,因为服务端一直在和最先加入的客户端交流中。

服务端代码

public static Map<String, Socket> sockets = new HashMap<>();// 这是一个群聊 可以用List创建多个群聊

    private Socket socket;

    private BufferedReader br;

    private BufferedWriter bw;

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


    @Override
    public void run() {

        try {

			Scanner sc = new Scanner(System.in);
            String str = "";
            while (!"end".equals(str)) {// 这里可以写成true ,不过下面关闭资源会报错,因为写成死循环了,暂时先这样
                // 先读取默认传过来的数据
                br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                // 读取客户端数据
                String info = br.readLine();
                System.out.println("客户端(" + socket.getPort() + "):" + info);


                // 传给所有人数据
                String str = sc.nextLine();
                for (Map.Entry<String ,Socket> entry : sockets.entrySet()) {
                bw = new BufferedWriter(new OutputStreamWriter(entry.getValue().getOutputStream()));

                // 输入数据
                bw.write(str + "\n");
                bw.flush();
                }
            }

            bw.close();
            br.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(10086);

            while (true) {
                Socket socket = serverSocket.accept();
                sockets.put(socket.getPort() + "", socket);
                Thread thread = new Thread(new ThreadServerSocket(socket), socket.getPort() + "-线程:");
                thread.start();
            }


        } catch (IOException e) {
            System.out.println("创建失败~");
        }

    }

客户端

	try {
            Socket socket = new Socket("localhost", 10086);

            Scanner sc = new Scanner(System.in);
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));


            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));


            boolean fals = true;
            while (fals){

                // 输入数据
                System.out.println("请输入:");
                String str = sc.nextLine();

                if ("end".equals(str)){
                    fals = false;
                }
                bw.write(str + "\n");
                bw.flush();
                
				// 读取数据
                String info = br.readLine();
                System.out.println("服务端:" + info);
            }


            
            br.close();
            bw.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

多添加几个客户端,然后你会发现客户端得输入后才能看到消息,而且多个客户端时,有几个客户端输入了,服务端就得输入多少遍才能返回到客户端,客户端输入的消息别人看不到。
问题我们一个个解决:
1、有几个客户端输入了,服务端就得输入多少遍
2、客户端得输入后才能看到消息
3、客户端输入的消息别人看不到

总结下:
消息返回显示的问题,服务端输入问题,客户端消息显示问题。
我们看下服务端这边显示的值,发现聊天的信息全集中的在服务端,客户端只显示服务端的,那么,我们是不是可以把聊天信息收集起来,由服务端输入给各个客户端,不就OK了?
然后是客户端输入后才能看到消息的问题,我们是不是可以不断的循环获取服务端输入的信息,但是就不能输入了,而且会死循环。那什么办法可以让程序继续执行下去,而且又不会死循环的?

------------ 没错,线程。我们可以开启一个线程去不断的获取信息,还可以用Thread.sleep()休眠。

好,问题解决,我们接下来撸代码。

完整的代码

服务端

package com.example.test;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ThreadServerSocket implements Runnable {

    public static Map<String, Socket> sockets = new HashMap<>();// 这是一个群聊 可以用List创建多个群聊

    public static volatile List<String> strings = new ArrayList<>(); // 获取到消息集合,存储消息的

    private Socket socket;

    private BufferedReader br;

    private BufferedWriter bw;

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


    @Override
    public void run() {

        try {

            String str = "";
            while (!"end".equals(str)) {
                // 先读取默认传过来的数据
                br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                // 读取客户端数据
                String info = br.readLine();


                strings.add("客户端(" + socket.getPort() + "):" + info);
                System.out.println("客户端(" + socket.getPort() + "):" + info);


                // 传给所有人数据
                for (Map.Entry<String ,Socket> entry : sockets.entrySet()) {
                bw = new BufferedWriter(new OutputStreamWriter(entry.getValue().getOutputStream()));

                // 输入数据
                bw.write(strings + "\n");
                bw.flush();
                }
            }

            bw.close();
            br.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(10086);

            while (true) {
                Socket socket = serverSocket.accept();
                sockets.put(socket.getPort() + "", socket);
                Thread thread = new Thread(new ThreadServerSocket(socket), socket.getPort() + "-线程:");
                thread.start();
            }


        } catch (IOException e) {
            System.out.println("创建失败~");
        }

    }

}

客户端

package com.example.test;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class MyClinet {

    public static void main(String[] args) {

        try {
            Socket socket = new Socket("localhost", 10086);

            Scanner sc = new Scanner(System.in);
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));


            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            boolean fals = true;
            while (fals){

                new Thread(new ReaderInfoThread(br)).start();// 开启的 读服务端消息的线程

                // 输入数据
                System.out.println("请输入:");
                String str = sc.nextLine();

                if ("end".equals(str)){
                    fals = false;
                }

                bw.write(str + "\n");
                bw.flush();
            }


            sc.close();
            br.close();
            bw.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

读取服务端信息

package com.example.test;

import java.io.BufferedReader;
import java.io.IOException;

public class ReaderInfoThread implements Runnable {

    private BufferedReader br;

    public ReaderInfoThread(BufferedReader br){
        this.br = br;
    }

    @Override
    public void run() {
        try {
            while (true) {
                // 读取数据
                String info = br.readLine();
                System.out.println("服务端:" + info);

                Thread.sleep(1500);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

多写几个客户端,开启服务端,客户端,测试
客户端输入
在这里插入图片描述

服务端显示
在这里插入图片描述

客户端2显示
在这里插入图片描述

好,完成==========

写在最后

如果发现问题,最好多debug下,走走流程,就能发现问题出现在哪!这个是博主一点点写出来的,还有很多的不足!请大家多多关照。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值