IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 2021-09-28 Socket聊天室模拟服务器讲解: -> 正文阅读

[系统运维]2021-09-28 Socket聊天室模拟服务器讲解:

Socket实现攻略:

一、创建两个类

?1、用户使用的,名为:Client

①定义私有成员属性:Socket socket;

java.net.Socket:套接字
Socket封装了TCP协议的通讯细节,使得我们使用它可以与服务端建立网络连接,并通过它获取两个流(一个输入流,一个输出流),然后使用这两个流的读写操作完成与服务端的数据交互。

②定义无参构造函数:

在构造函数中实例化对象,socket = new Socket(参数1,参数2);
实例化Socket时通常需要传入两个参数:
参数1:服务端的地址信息(IP地址,如果连接本机可以用localhost)。
参数2:服务端打开的服务端口,即服务端ServerSocket申请的端口。

? ? private Socket socket;
? ? public Client(){
? ? ? ? try {
? ? ? ? ? ? System.out.println("正在连接服务器……");
? ? ? ? ? ? socket = new Socket("192.168.43.215",2222);
? ? ? ? ? ? System.out.println("服务器连接成功!"+socket.getInetAddress());
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }

?2、服务器的,名为:Server

①定义私有成员属性:ServerSocket serverSocket

运行在服务器端的ServerSocket主要有两个作用:
? ? 1、向系统申请服务端口,客户端的Socket就是通过这个端口与服务器建立连接的。
? ? 2、监听服务端口,一旦一个客户通过该端口建立连接会自动创建一个Socket,服务端就可以通过这个Socket与客户端交互了。如果我们把Socket比喻为电话,那么ServerSocket相当于是某客服中心的总机。

②定义一个集合,用来存储可能开启的很多个客户端。

private List<PrintWriter> c = Collections.synchronizedList(new ArrayList<>());创建一个线程安全的集合,其中add,remove等方法上都直接使用了synchronized修饰, 锁个线程是不能同事调用一个集合的这些方法的,保证同步执行。

③ 定义无参构造函数:

在构造函数中实例化对象serverSocket = new ServerSocket(222);设置端口号。
实例化时需要指定服务器端口,如果该端口被当前系统其他应用程序占据时,会抛出异常。

?private ServerSocket serverSocket;
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();
? ? ? ? }
? ? }


二、服务器端需要做的事:

服务器端需要:接收客户端发送的信息,和将汇总的信息发回给客户端,实现通信功能。

1、实现客户端与服务器的互通:accept

① 不确定客户端数量,所以可以用死循环,保证一直能有新用户连接

② 客户端与服务器连接并创建启动线程,使用ServerSocket提供的方法: [Socket] accept():

这是一个阻塞方法,调用后程序进入阻塞状态,直到一个客户端实例化Socket与当前服务端建立连接,此时accept方法会立即返回一个Socket实例,服务端通过它就可以与客户端交互了。可以理解为这个动作相当于是总机的“接电话”操作。
注意:设置线程这里也要try-catch,否则会报错

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

2、定义一个属于类发送信息的方法,将服务器收到的信息发给客户端

    public void sendMessage(String message){
        c.forEach(pw -> pw.println(message));
    }

3、创建一个线程任务类ClientHandler implements Runnable,用于与指定的客户端进行交互。

每个连接服务器的客户端都是通过线程进行交互的。 即:一个客户端靠一个线程进行交互

① 私有化内部类的属性:private Socket socket

② 有参的构造方法:传递一个socket给当前赋值

private class ClientHandler implements Runnable {
        private Socket socket;
        private String address;//处理当前线程处理的客户端地址
        public ClientHandler(Socket socket) {
            this.socket = socket;
            //通过socket获取远端计算机地址信息(对于服务器而言,远端就是客户端)
            this.address = socket.getInetAddress().getHostAddress();
        }

④ 定义线程任务,重写run: Socket提供的方法:[InputStream] getInputStream():

通过该方法获取的字节输入流取的时远端计算机发送过来的数据。这里相当于是读取当前服务端中这个Socket对应的远端(客户端)那边Socket获取的输出流写出的字节数据。

⑤ 使用流完成信息的写入写出:

        @Override
        public void run() {
            PrintWriter pw =null;
            try {
                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();
                }
            }
        }

三、客户端要做的事:

1、定义一个start方法,接收用户从控制台输入的信息

① 使用流连接:Socket提供的方法:[OutputStream] getOutputStream()

该方法会获取一个字节输出流,通过这个输出流写出的字节数据会通过网络发送给对方。设定结束词语。

public void start(){
? ? try(OutputStream out = socket.getOutputStream();
? ? PrintWriter pw =new PrintWriter(new BufferedWriter(new OutputStreamWriter(out,"UTF-8")),true);) {
? ? ? ? ?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();
? ? ? ? ? ? }
? ? ? ? }
? ? }

2、定义线程任务ServerHandler implements Runnable

①设置run方法,Socket提供的方法:[InputStream] getInputStream():

通过该方法获取的字节输入流取的时远端计算机发送过来的数据。 这里相当于是读取当前服务端中这个Socket对应的远端(客户端)那边Socket获取的输出流写出的字节数据。

设置字节输出流,读取其他客户端或者自己发送给服务器后,被服务器返回的数据,如果只是使用输入流,则看不到别人的信息,功能不健全。输入自己说的,读出大家说的,这样可以实现互通。

    private class ServerHandler implements Runnable{
        @Override
        public void run() {
            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();
            }
        }

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-29 10:44:28  更:2021-09-29 10:45:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 17:54:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码