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