2021-09-28 Socket聊天室模拟服务器代码:

31 篇文章 0 订阅
25 篇文章 8 订阅

制作一个在同局域网下能够互相连接并实现群聊的程序:

 以下是Client:

package FYX_Month02_Week02.day02.No3_Socket;

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

/**
 * 2021.09.28,星期二
 * Author: BaoYang
 * StudyPoint: 客户端聊天室
 */
public class Client {
    /*
    java.net.Socket:套接字
    Socket封装了TCP协议的通讯细节,使得我们使用它可以与服务端建立网络连接,
    并通过它获取两个流(一个输入流,一个输出流),然后使用这两个流的读写操作完成与服务端的数据交互。
     */
    private Socket socket;
    //构造方法:初始化客户端
    public Client(){
        try {
            /*
            实例化Socket时通常需要传入两个参数:
            参数1:服务端的地址信息(IP地址,如果连接本机可以用localhost)。
            参数2:服务端打开的服务端口,即服务端ServerSocket申请的端口。
             */
            System.out.println("正在连接服务器……");
            socket = new Socket("192.168.43.215",2222);
            System.out.println("服务器连接成功!"+socket.getInetAddress());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void start(){
        //客户端启动后,向县启动一个线程用来读取服务器端发送的消息
        ServerHandler handler = new ServerHandler();
        Thread t = new Thread(handler);
        t.start();

        try(OutputStream out = socket.getOutputStream();
        PrintWriter pw =
                new PrintWriter(new BufferedWriter(new OutputStreamWriter(out,"UTF-8")),true);) {
            /*
            Socket提供的方法:[OutputStream] getOutputStream()
            该方法会获取一个字节输出流,通过这个输出流写出的字节数据会通过网络发送给对方。
            */
             Scanner sc = new Scanner(System.in);
            System.out.println("开始聊天吧!单独输入88|拜拜则退出聊天室~");
            while (true){
                String line = sc.next();
                if(line.equals("88")|line.equals("拜拜")){
                    System.out.println("本次聊天已结束,感谢使用聊天室~");
                    break;
                }
                pw.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private class ServerHandler implements Runnable{
        @Override
        public void run() {
                /*
                Socket提供的方法:[InputStream] getInputStream():
                通过该方法获取的字节输入流取的时远端计算机发送过来的数据。
                这里相当于是读取当前服务端中这个Socket对应的远端(客户端)那边Socket获取的输出流写出的字节数据。
                 */
            try (
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
            ){
                String message;
                while ((message= br.readLine())!=null){
                    System.out.println(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }



        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.start();

    }
}

以下是Server:

package FYX_Month02_Week02.day02.No3_Socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.stream.Stream;

/**
 * 2021.09.28,星期二
 * Author: BaoYang
 * StudyPoint:
 */
public class Server {
    /*
    运行在服务器端的ServerSocket主要有两个作用:
    1、向系统申请服务端口,客户端的Socket就是通过这个端口与服务器建立连接的。
    2、监听服务端口,一旦一个客户通过该端口建立连接会自动创建一个Socket,服务端就可以通过这个Socket与客户端交互了。

    如果我们把Socket比喻为电话,那么ServerSocket相当于是某客服中心的总机。
     */
    private ServerSocket serverSocket;
    /*
    创建一个线程安全的集合,其中add,remove等方法上都直接使用了synchronized修饰,
    锁个线程是不能同事调用一个集合的这些方法的,保证同步执行。
     */
    private List<PrintWriter> c = Collections.synchronizedList(new ArrayList<>());

    public Server() {
        try {
            System.out.println("正在启动服务端……");
            /*
            实例化时需要指定服务器端口,如果该端口被当前系统其他应用程序占据时,会抛出异常;
             */
            serverSocket = new ServerSocket(222);
            System.out.println("服务端连接成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //构造方法:初始化客户端

    public void start() {
        try {
            /*
            ServerSocket提供的方法:
            [Socket] accept():是一个阻塞方法,调用后程序进入阻塞状态,直到一个客户端
            实例化Socket与当前服务端建立连接,此时accept方法会立即返回一个Socket实例,服务端通过它就可以与客户端交互了。
            ---
            可以理解为这个动作相当于是总机的“接电话”操作。
             */
            int i = 1;
            while (true) {
                System.out.println("等待客户端连接……");
                Socket socket = serverSocket.accept();
                System.out.println("第" + i + "个客户端连接成功!" + socket.getInetAddress());
                i++;
                //启动一个线程用来与客户端交互:
                //1、创建线程任务:
                ClientHandler handler = new ClientHandler(socket);
                //2、创建一个线程执行该任务
                Thread t = new Thread(handler);
                //3、启动线程
                t.start();

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

    /*
    该线程任务用于与指定的客户端进行交互
    每个连接服务器的客户端都是通过线程进行交互的。
    即:一个客户端靠一个线程进行交互
     */
    private class ClientHandler implements Runnable {
        private Socket socket;
        private String address;//处理当前线程处理的客户端地址
        public ClientHandler(Socket socket) {
            this.socket = socket;
            //通过socket获取远端计算机地址信息(对于服务器而言,远端就是客户端)
            this.address = socket.getInetAddress().getHostAddress();
        }

        @Override
        public void run() {
            PrintWriter pw =null;
            try {
                /*
                Socket提供的方法:[InputStream] getInputStream():
                通过该方法获取的字节输入流取的时远端计算机发送过来的数据。
                这里相当于是读取当前服务端中这个Socket对应的远端(客户端)那边Socket获取的输出流写出的字节数据。
                 */
                pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8")), true);
                c.add(pw);
                System.out.println(address+"上线了,当前在线人数:"+c.size());
                //广播上线通知:
                sendMessage(address+"上线了,当前在线人数:"+c.size());
                BufferedReader bw = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                String line;
                while ((line = bw.readLine()) != null) {
                    System.out.println(address + "说:" + line);
                    //将消息回复给所有客户端
                    sendMessage(address + "说:" + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //下线时:移除对象并且广播
                try {
                    c.remove(pw);
                    sendMessage(address+"下线了,当前在线人数:"+c.size());
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 将消息转发给所有客户端
     * @param message:要发送的内容
     */
    public void sendMessage(String message){
        c.forEach(pw -> pw.println(message));
    }





    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值