欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

文本聊天室(TCP)

程序员文章站 2022-03-26 09:25:43
以流式的Socket实现面向连接的TCP服务 一.功能要求 1.用户可以选择聊天服务器进行登录. 2.用户使用用户名登录到聊天室,这个登录名就是用户在聊天 室的昵称. 3.可以选择群聊,广播信息,使所有用户都能看到聊天信息 4.可以选择和某个用户私聊,其他用户无法得知聊天内容. 5.聊天信息要试试反 ......

文本聊天室(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 }

 

 

 

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