zoukankan      html  css  js  c++  java
  • 文本聊天室(TCP)

       

              以流式的Socket实现面向连接的TCP服务


      一.功能要求

         1.用户可以选择聊天服务器进行登录.

         2.用户使用用户名登录到聊天室,这个登录名就是用户在聊天

         室的昵称.

            3.可以选择群聊,广播信息,使所有用户都能看到聊天信息

         4.可以选择和某个用户私聊,其他用户无法得知聊天内容.

         5.聊天信息要试试反应到聊天记录中.

         6.用户登录退出时,要给其他用户发出通知.


      二.设计

        1.界面设计

          ..........发挥你的想象力.........

        2.整体设计

          聊天室整体采用C/S模式,客户端启动后,主动向服务器发出

          连接请求,建立Socket连接.服务器启动后,监听固定端口

          5210,当有客户连接请求时,便响应此请求,将此连接交由线

          程Talking类处理.

      


     

     

      1.来看服务器的代码实现

     1 package jffx.blogs.net;
     2 
     3 import java.io.*;
     4 import java.net.*;
     5 import java.util.*;
     6 
     7 /**
     8  * 代码文件:    TalkRoomServer.java
     9  * 功能描述:    管理服务器与客户端的活动连接
    10  */
    11 public class TalkRoomServer {
    12     public static void main(String[] args) {
    13         try {
    14             //服务器端serversocket, 绑定端口(5210), 随意选(1024后的)
    15             ServerSocket server = new ServerSocket(5210);
    16 
    17             /**
    18              * 容器来保存服务器与客户端的连接, 键为姓名,值为socket
    19              */
    20             Map<String, Socket> socketMap = new HashMap<>() ;
    21 
    22             while(true) {
    23                 //监听客户端的连接, accept的方式是阻塞的
    24                 try {
    25                     Socket ss = server.accept();
    26 
    27                     //创建流
    28                     //采用缓冲流,提高效率
    29                     DataInputStream in = new DataInputStream(
    30                             new BufferedInputStream(ss.getInputStream())
    31                     ) ;
    32                     DataOutputStream out = new DataOutputStream(
    33                             new BufferedOutputStream(ss.getOutputStream())
    34                     ) ;
    35 
    36                     /**
    37                      * 在客户端设计时,一个新的用户登录的同时就向服务器发送其姓名
    38                      */
    39                     String name = in.readUTF() ;
    40                     //获取IP
    41                     String IP = ss.getInetAddress().toString() ;
    42                     //显示到服务器
    43                     System.out.println(name + " : " + IP) ;
    44 
    45                     //查看已监听的客户,并向他们发送新用户登录消息
    46                     Collection<Socket> values = socketMap.values() ;
    47                     Iterator<Socket> iter = values.iterator() ;
    48                     while(iter.hasNext()) {
    49                         Socket temp = iter.next() ;
    50                         DataOutputStream write = new DataOutputStream(temp.getOutputStream()) ;
    51                         write.writeUTF("Add:" + name + IP) ;
    52                         write.flush() ;
    53                     }
    54                     //将新用户添加到容器中
    55                     socketMap.put(name, ss) ;
    56 
    57                     /**
    58                      * 向新登录的用户发送都有谁在线
    59                      */
    60                     Set<String> names = socketMap.keySet() ;
    61                     Iterator<String> iterName = names.iterator() ;
    62                     while(iterName.hasNext()) {
    63                         String loginUser = iterName.next() ;
    64                         out.writeUTF("Add:" + loginUser + IP) ;
    65                         out.flush() ;
    66                     }
    67 
    68 
    69                     /**
    70                      * 创建新线程转发用户给服务器发送的消息
    71                      *  由于需要分
    72                      *      向某个人发送消息,即:私聊
    73                      *      向所有发送消息,即:广播.
    74                      *  我们采用修改处理客户端的发送方式:
    75                      *      在消息的基础上,给前面即消息前缀加上一些表示发送目标的字符串.
    76                      *      具体看Talking.java的处理方式
    77                      */
    78                     //由于客户有可能下线,所以需要将姓名和容器都传递给线程类
    79                     new Thread(new Talking(name, ss, socketMap)).start() ;
    80 
    81                 } catch (Exception ex) {
    82                     ex.printStackTrace() ;
    83                 }
    84             }
    85         } catch (Exception ex) {
    86             ex.printStackTrace() ;
    87         }
    88     }
    89 }

     

     

     

    下面这个是单独处理每个用户的线程代码.

      大概思路是这样的:先读取这个用户的发送的消息,然后进行解析,拆分,

          分情况发送给客户端;如果由用户退出了聊天室,将用户从所在的

          Socket容器中剔除,并给所有客户发送这个用户退出的消息.

     

     

     1 package jffx.blogs.net;
     2 
     3 import java.io.*;
     4 import java.net.*;
     5 import java.util.*;
     6 
     7 /**
     8  * 代码文件:Talking.java
     9  * 功能描述:线程类转发用户的数据
    10  */
    11 public class Talking implements Runnable {
    12     String name ;
    13     Socket connecter ;
    14     Map<String, Socket> socketMap = null ;
    15     public Talking(String name, Socket socket, Map<String, Socket> socketMap) {
    16         this.name = name ;
    17         this.connecter = socket ;
    18         this.socketMap = socketMap ;
    19     }
    20 
    21     @Override
    22     public void run() {
    23         try {
    24             DataInputStream in = new DataInputStream(
    25                     new BufferedInputStream(connecter.getInputStream())
    26             ) ;
    27 
    28             while(true) {
    29                 String words = in.readUTF() ;
    30                 /**
    31                  * 我们约定,客户端发送("name@text")这种格式的消息
    32                  * 不过用户不需要处理,我们交由客户端程序在发送消息时自动加上
    33                  */
    34                 String [] tokens = words.split("@") ;
    35                 String sendName = tokens[0] ;
    36                 String text = tokens[1] ;
    37 
    38                 if("All".equals(sendName)) {
    39                     //容器的值为Socket变量
    40                     Collection<Socket> sockets = socketMap.values() ;
    41                     Iterator<Socket> iter = sockets.iterator() ;
    42                     while(iter.hasNext()) {
    43                         //创建流.并以固定格式写出
    44                         Socket sendSocket = iter.next() ;
    45                         DataOutputStream out = new DataOutputStream(sendSocket.getOutputStream()) ;
    46                         out.writeUTF("Text:" + text) ;
    47                         out.flush() ;
    48                     }
    49                 } else {        //私聊
    50                     Socket sendSocket = socketMap.get(sendName) ;
    51                     DataOutputStream out = new DataOutputStream(sendSocket.getOutputStream()) ;
    52                     out.writeUTF("Text:" + text) ;
    53                     out.flush() ;
    54                 }
    55             }
    56         } catch (Exception ex) {
    57             ex.printStackTrace() ;
    58         } finally {                     //当登陆的用户退出后,就会跳出while(true)
    59             try {
    60                 /**
    61                  * 查找登陆的用户,删除服务器与其的连接,并发送给所有的客户端
    62                  */
    63                 this.socketMap.remove(this.name) ;
    64                 Collection<Socket> sockets = this.socketMap.values() ;
    65                 Iterator<Socket> iter = sockets.iterator() ;
    66                 while(iter.hasNext()) {
    67                     Socket sender = iter.next() ;
    68                     DataOutputStream out = new DataOutputStream(sender.getOutputStream()) ;
    69                     out.writeUTF("Del:" + this.name) ;
    70                     out.flush() ;
    71                 }
    72             } catch (Exception ex) {
    73                 ex.printStackTrace() ;
    74             }
    75         }
    76     }
    77 }

     

     

    至于客户端,留给明天.呵呵...........

     

  • 相关阅读:
    Easy | LeetCode 154 | 剑指 Offer 11. 旋转数组的最小数字 | 二分法
    Easy | 剑指 Offer 17. 打印从1到最大的n位数 | 大数
    Easy | LeetCode 191 | 剑指 Offer 15. 二进制中1的个数 | 位运算
    Easy | 剑指 Offer 57
    Medium | LeetCode 15. 三数之和 | 双指针法
    jq load()方法实现html 模块化。
    vue 剪切图片上传头像,使用 cropperjs 实现
    vue 的nextTick的理解
    使用nodejs进行开发,concurrently模块,使我们同时执行多个命令。
    cookie生命周期expires 时间过期,但是cookie没有被浏览器清理的问题
  • 原文地址:https://www.cnblogs.com/jffx/p/9801498.html
Copyright © 2011-2022 走看看