Erlo

文本聊天室(TCP)

2018-10-17 00:56:56 发布   425 浏览  
页面报错/反馈
收藏 点赞

   

          以流式的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 }

 

 

 

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

 

登录查看全部

参与评论

评论留言

还没有评论留言,赶紧来抢楼吧~~

手机查看

返回顶部

给这篇文章打个标签吧~

棒极了 糟糕透顶 好文章 PHP JAVA JS 小程序 Python SEO MySql 确认