聊天室系统设计报告.doc

上传人:scccc 文档编号:12409840 上传时间:2021-12-03 格式:DOC 页数:53 大小:1.08MB
返回 下载 相关 举报
聊天室系统设计报告.doc_第1页
第1页 / 共53页
聊天室系统设计报告.doc_第2页
第2页 / 共53页
聊天室系统设计报告.doc_第3页
第3页 / 共53页
聊天室系统设计报告.doc_第4页
第4页 / 共53页
聊天室系统设计报告.doc_第5页
第5页 / 共53页
点击查看更多>>
资源描述

《聊天室系统设计报告.doc》由会员分享,可在线阅读,更多相关《聊天室系统设计报告.doc(53页珍藏版)》请在三一文库上搜索。

1、聊天室系统设计报告聊天室系统青岛科技大学操作 系统课 程设计学生班级计算机132班学生学号1308010204学生姓名王永远2016年 5月_日48目录设计题目:聊天室系统5一、设计目的及要求51.1设计目的512设计要求:二、技术背景2.1网络编程2.2网络通信23 Socket2.4 TCP传输25 Java的多线程机制三、需求分析31客户端3.1.1登录6788993.12多人聊天9313单人聊天932服务器端10【、总体设计10设计题目 :聊天室系统一、设计目的及要求1.1 设计目的通过该聊天室系统, 掌握网络编程的概念及基于 网络的 C/S 模式软件系统开发,掌握基于 TCP 协议

2、的 Socket 编程,掌握 Java 的多线程机制。1.2 设计要求:实现多个用户之间类似于 QQ 的聊天程序,有聊 天界面,多用户之间既可以实现群聊, 也可以单独聊 天。二、技术背景2.1 网络编程就是用来实现网络互连的不同计算机上运行的程序 间可以进行数据交换。2.2 网络通信(1)IP 地址:网络中设备的标识,不易记忆,可用 主机名要想让网络中的计算机能够互相通信, 必须为每台计 算机指定一个标识号, 通过这个标识号来指定要接受 数据的计算机和识别发送的计算机, 在 TCP/IP 协议 中,这个标识号就是 IP 地址。(2)端口号:用于标识进程的逻辑地址 物理端口 网卡口逻辑端口 我们

3、指的就是逻辑端口A: 每个网络程序都会至少有一个逻辑端口B:用于标识进程的逻辑地址,不同进程的标识C: 有效端口:065535,其中 01024 系统使用或保留 端口。(3)传输协议:通信的规则,常见的有 UDP, TCP UDP :将数据源和目的封装成数据包中,不需要建立 连接;每个数据报的大小在限制在 64k;因无连接, 是不可靠协议;不需要建立连接,速度快。TCP :建立连接,形成传输数据的通道;在连接中进 行大数据量传输; 通过三次握手完成连接, 是可靠协 议;必须建立连接,效率会稍低。2.3 Socket(1)Socket 套接字 网络上具有唯一标识的 IP 地址和端口号组合在一起

4、才能构成唯一能识别的标识符套接字。(2)Socket 原理机制A: 通信的两端都有 Socket。B: 网络通信其实就是 Socket 间的通信C:数据在两个Socket间通过IO传输。2.4 TCP 传输(1)客户端思路A 建立客户端的 Socket 服务 ,并明确要连接的服务 器。B 如果连接建立成功 ,就表明 ,已经建立了数据传输的 通道 .就可以在该通道通过 IO 进行数据的读取和写 入.该通道称为Socket流Socket流中既有读取流,也 有写入流 .C通过Socket对象的方法,可以获取这两个流D 通过流的对象可以对数据进行传输E 如果传输数据完毕 ,关闭资源(2)服务器端思路A

5、 建立服务器端的 socket 服务,需要一个端口B服务端没有直接流的操作,而是通过accept方法获 取客户端对象,在通过获取到的客户端对象的流和客 户端进行通信C 通过客户端的获取流对象的方法 ,读取数据或者写 入数据D 如果服务完成 ,需要关闭客户端 ,然后关闭服务器, 但是 ,一般会关闭客户端 ,不会关闭服务器 ,因为服务 端是一直提供服务的2.5 Java 的多线程机制 进程:每个进程都有独立的代码和数据空间 (进程上 下文),进程间的切换会有较大的开销,一个进程包 含 1-n 个线程。 线程:同一类线程共享代码和数据空间, 每个线程有 独立的运行栈和程序计数器 (PC) ,线程切换

6、开销小。多进程是指操作系统能同时运行多个任务(程 序),多线程是指在同一程序中有多个顺序流在执行。 在 java 中,每次程序运行至少启动 2 个线程。一个 是 main 线程,一个是垃圾收集线程。因为每当使用 java 命令执行一个类的时候,实际上都会启动一个 JVM ,启动 JVM 实际上就是在操作系统中启动了一 个进程。在 java 中所有的线程都是同时启动的,至 于什么时候,哪个先执行,完全看谁先得到 CPU 的 资源。三、需求分析实现聊天的功能,采用 Java Socket 编程,服务 器与客户端采用了 TCP/IP 连接方式, 在设计聊天方 案时,可将所有信息发往服务器端, 再由服

7、务器进行 处理,服务器端是所有信息的中心。3.1 客户端3.1.1 登录用户需要填写用户名、 服务器地址、 端口号才可 以进入聊天室与在线用户聊天, 此外,服务器地址默 认是本机地址,端口号默认是 5000。用户名可以中 文,英文字母或标点符号, 服务器地址必须是符合点 分十进制的合法地址, 端口号可以修改, 但服务器端 程序内默认端口号也必须修改。该聊天室不需要注 册,直接登录即可聊天, 退出后, 系统不保留用户任 何信息。若新登录用户与在线用户的用户名重名, 则系统 会自动修改用户名。 格式为:用户名 +“_客户端线程 ID ”。3.1.2 多人聊天每个在线用户都可以发送聊天信息, 服务器

8、端会 一直监听,并把每一个在线用户发送的聊天信息转发 到每一个客户端。聊天室的聊天信息格式为: 用户名 时间( yyyy-MM-dd HH:mm:ss ) 聊天信息3.1.3 单人聊天用户可以选择某一个在线用户实现单人聊天, 该 聊天信息不会在聊天室显示, 只有单聊的两个人能够 看到。单人聊天的聊天信息格式为:用户名时间(yyyy-MM-dd HH:mm )聊天信息3.2服务器端服务器端主要处理客户端的请求,包括用户的登 录,发送多人聊天信息,退出聊天室,单人聊天请求, 发送单人聊天信息,并且随时更新在线用户列表。四、总体设计4.1设计思想流程图4.2 设计思想分析 首先启动服务器,它会建立一

9、个专门用于接收客户端连接请求的“倾听Socket”,然后等待客户的连 接请求。当用户登录输入信息后,与服务器建立 Socket连接,服务器端的“倾听 Socket"收到连接 请求后,会接受连接请求,并生成一个服务器端 socket,专门负责与此客户端socket的通信。一旦连 接请求成功,客户端将信息及请求通过本方 socket 的输出流发送给服务器端相应的socket,服务端则通 过服务器端 Socket 的输入流接受客户端传输过来的 信息及请求, 分析是何请求, 然后根据请求类型, 进 行相应的处理(如登录、私聊等) 。服务器端也可以 根据需要,通过 socket 的输出流发送信

10、息和请求给 客户端。客户端和服务器端都可以通过关闭本方的socket 而结束一次通信过程。对于客户端的各种请求, 实际上都是通过在客户 端发往服务器的各种字符流区分的, 具体的方法就是 在消息的内部添加特殊字符串, 从而实现服务器对消 息请求的识别。 比如对于登陆信息, 消息中添加的内 容就是“ login ” ,而对于私聊中的消息,消息中添 加的内容就是“ single” ,其他的同理都添加了相应 内容。当然,对于客户端来说,这些都是透明的,用 户的操作并没有受到任何影响。 在服务器端, 消息被 检测分析后, 变回根据具体的目的进行处理, 比如是 私聊消息, 服务器便会根据其内部添加的信息,

11、 向目 标端转发该条消息, 当目标端接收到连接请求后, 会 主动建立一个私聊窗口,从而实现私聊。服务器端需要能同时接受多个用户的请求, 为了 实现这一点, 一般使用多线程机制来处理, 对每一个 客户端连接通信,服务器端都有一个线程专门负责处 理。 对于客户端的各种请求,内部添加的信息分别如下:clientThread 客户端线程启动 messages.add(clientThread.getId() + "clientThread");客户端线程 IDlogin 登录客户端 username+"login"+getThreadID()+"log

12、in" 用户名 +客户端线程 IDuserlist 用户列表serverThread.users.get(new Integer(threadID) + "userlist"+threadID + "userlist" 用户名 +客户端线程 IDchat 群聊username + "chat" + getThreadID() + "chat"+ mess + "chat"用户名+客户端线程ID+聊天信息serverexit 服务器退出 serverThread.messages+&qu

13、ot;serverexit"single 单聊client.username + "single" + client.getThreadID() + "single" +(int)client.clientuserid.get(index) + "single" + mess + "single"用户名+客户端线程ID+客户端线程ID+聊天信息exit 退出群聊username + "exit" + getThreadID() + "exit"用户名 +客户端线程

14、ID五、详细设计5.1 客户端设计5.1.1 登录界面5.1.2聊天室界面5.1.3单人聊天界面5.2服务器端设计六、系统测试6.1登录测试6.1.1用户名为英文字母6.1.2用户名为中文6.1.3用户名为标点符号6.1.4多个用户有重名6.1.5服务器地址不合法6.1.6端口号不合法6.1.7服务器未开启6.2群发消息测试6.3私聊测试I聊天消息zhang 201-05*1511:34我是 zhajigIvang 2016-05-1511;34我是#汕冒.发送消息16.4 用户列表显示测试 无论已经登录用户的退出, 还是新用户成功登录, 用 户列表显示部分都能正确显示。七、心得体会这段时间通

15、过不断的修改, 我终于把聊天室系统 完成了,虽然它只有简单的聊天功能, 但通过它, 我 不但巩固了以前学的知识, 而且学到了许多在课堂中 学不到的知识。 通过这次课程设计, 我更坚定了理论 与实际相结合是十分重要的想法, 即使一个人读了再 多的技术图书, 但没有相关的实践经验, 那么他也不 会真正地掌握一门技术。 只有把理论知识与实践相结 合,才会深入的了解并提高自己的独立思考能力。通过这次的课程设计,我将自己所学的 Java 语 言得到了实际的应用, 在完成的过程中也遇到了许多 困难,但通过不断地查阅资料, 最终还是解决了, 在 这个过程中, 我学会了独立思考, 同时也让我明白完 成一件事情

16、要不断开阔视野, 拓展知识面, 解放自己 的思维。 总之,在完成课程设计的过程中, 我学会了 如何克服开发中遇到的技术困难, 学会了独立面对并 解决问题。八、参考资料 叶核亚 JAVA 程序设计实用教程(第 2 版)电子工 业出版社朱福喜,路迟 JAVA 语言与面向对象程序设计 武汉 大学出版社沈文炎 Java 高级编程 机械工业出版社九、程序清单(1)Client.javapublic class Client extends Threadpublic Socket c_socket ;/ 套接字private Client_chatFrame c_chatFrame;/ 聊天室 聊天界面p

17、rivate Client_enterFrame c_enterFrame;/ 客户端 登录界面private Client_singleFrame c_singleFrame;/ 单人 聊天界面public DataInputStream dis = null;/IO 输入public DataOutputStream dos = null;/IO 输出 private boolean flag_exit = false;/ 客户端未启动标 记private int threadID;/ 聊天室客户端线程标记public Map<String, Client_singleFrame&g

18、t; c_singleFrames;/ 单人聊天,用户名为键,单聊客户端为值public List<String> username_online;/ 在线用户 public List<Integer> clientuserid;/ 用户 ID public String username = null;/ 用户名 public String chat_re;/ 通道内相关信息 /getter, setter 方法public Client_chatFrame getC_chatFrame() return c_chatFrame;public Client_singl

19、eFrame getC_singlFrame() return c_singleFrame;public void setC_singlFrame(Client_singleFrame c_singlFrame) this.c_singleFrame = c_singlFrame;public void setC_chatFrame(Client_chatFrame c_chatFrame) this.c_chatFrame = c_chatFrame;public Client_enterFrame getC_enterFrame() return c_enterFrame;public v

20、oid setC_enterFrame(Client_enterFrame c_enterFrame) this.c_enterFrame = c_enterFrame;public int getThreadID() return threadID;public void setThreadID(int threadID) this.threadID = threadID;/客户端构造函数public Client() c_singleFrames = new HashMap<String, Client_singleFrame>();username_online = new

21、ArrayList<String>(); clientuserid = new ArrayList<Integer>();/ signlechatuse = new ArrayList<String>();/main 方法,设置进入时登录界面public static void main(String args) Client client = new Client();Client_enterFrame c_enterFrame = new Client_enterFrame(client);client.setC_enterFrame(c_enterFr

22、ame); c_enterFrame.setVisible(true);/登录客户端public String login(String username, String hostIp,String hostPort) this.username = username;/ 用户名 String login_mess = null;/ 错误信息或 true try c_socket = new Socket(hostIp, Integer.parseInt(hostPort); catch (NumberFormatException e) login_mess = " 连接的服务器端

23、口号 port 为整 数,取值范围为: 1024<port<65535"return login_mess; catch (UnknownHostException e) login_mess = " 主机地址错误 "return login_mess; catch (IOException e) login_mess = " 连接服务器失败,请稍后再试II.return login_mess;return "true"/创建一个客户端聊天界面,启动一个线程public void showChatFrame(String

24、username) getDataInit();c_chatFrame =Client_chatFrame(this,username); c_chatFrame.setVisible(true); flag_exit = true;/ 客户端已启动 this.start();/ 启动线程/初始化,建立连接通道private void getDataInit() try dis =DataInputStream(c_socket.getInputStream(); dos =newnewnewDataOutputStream(c_socket.getOutputStream(); catch

25、(IOException e) e.printStackTrace();/该类声明为 Thread 的子类,重写 Thread 类的 run 方法public void run() while(flag_exit)/ 客户端已启动try /readUTF(): 读入一个已使用 UTF-8 修 改版格式编码的字符串。chat_re = dis.readUTF(); catch (IOException e) flag_exit = false; if(!chat_re.contains("serverexit") chat_re = null;if(chat_re != nu

26、ll) if(chat_re.contains("clientThread") int local = chat_re.indexOf("clientThread");setThreadID(Integer.parseInt(chat_re.substring(0, local);/ 启动客户端线程,设置 ThreadIDtry dos.writeUTF(username + "login" + getThreadID() + "login"); catch (IOException e) e.printStack

27、Trace();elseif(chat_re.contains("userlist") c_chatFrame.setDisUsers(chat_re);else if(chat_re.contains("chat") c_chatFrame.setDisMess(chat_re);elseif(chat_re.contains("serverexit") c_chatFrame.closeClient();else if(chat_re.contains("single")c_chatFrame.setSingl

28、eFrame(chat_re);/发送聊天信息public void transMess(String mess) try dos.writeUTF(username + "chat" getThreadID() + "chat"+ mess + "chat"); catch (IOException e) e.printStackTrace();/聊天室中退出聊天public void exitChat() try dos.writeUTF(username + "exit" getThreadID() + &q

29、uot;exit");flag_exit = false; System.exit(0); catch (IOException e) e.printStackTrace(); /登录之前退出 public void exitLogin() System.exit(0);/ 服 务 器 关 闭 , 退 出 聊 天 室 客 户 端 , 在 Client_chatFrame 中调用public void exitClient() flag_exit = false; System.exit(0); (2) WinCenter.java public class WinCenter pub

30、lic static void center(Window win)Toolkit tkit = Toolkit.getDefaultToolkit();/ 使用 系统工具包Dimension sSize = tkit.getScreenSize();/ 屏幕尺Dimension wSize = win.getSize();/ 窗体尺寸 if(wSize.height > sSize.height) wSize.height = sSize.height;if(wSize.width > sSize.width)wSize.width = sSize.width;win.setLo

31、cation(sSize.width - wSize.width)/ 2, (sSize.height - wSize.height)/ 2);/ 窗体居屏幕中央(3) Server.javapublic class Server private ServerFrame serverFrame;private ServerThread serverThread;public ServerFrame getServerFrame() return serverFrame;public void setServerFrame(ServerFrameserverFrame) this.serverF

32、rame = serverFrame;public Server()/启动服务器线程public void startServer() tryserverThread = new ServerThread(serverFrame);catch(Exception e)System.exit(0); serverThread.setFlag_exit(true); serverThread.start();/停止服务器线程 public void stopServer()synchronized (serverThread.messages) / 同步 代码块:synchronized:当它用来

33、修饰一个方法或者一 个代码块的时候,能够保证在同一时刻最多只有一个 线程执行该段代码。String str = "serverexit"serverThread.messages.add(str);serverThread.serverFrame.setDisMess("exit");/ /清除聊天信息serverThread.serverFrame.setDisUsers("exit");/ /清除在线用户serverThread.stopServer();/ 停止服务器线程/main 方法public static void ma

34、in(String args) Server server = new Server();ServerFrame serverFrame = new ServerFrame(server);server.setServerFrame(serverFrame); serverFrame.setVisible(true);/停止服务器线程并退出public void close() if(serverThread != null) if(serverThread.isAlive()serverThread.stopServer();System.exit(0);(4) ServerThread.j

35、avapublic class ServerThread extends Thread /Vector 类可以实现可增长的对象数组。与数组一 样,它包含可以使用整数索引进行访问的组件。/但是, Vector 的大小可以根据需要增大或缩小, 以适应创建 Vector 后进行添加或移除项的操作。public ServerSocket serverSocket;/ 服务器套接字public Vector<String> messages;/messages.add(clientThread.getId() + "clientThread")public Vector&

36、lt;ClientThread> clients;/ 客户端线程public MapvInteger, String> users;/客户端线程ID 和用户姓名public BroadCast broadcast;public int Port = 5000;public boolean login = true;public ServerFrame serverFrame;private boolean flag_exit = false;/ 服务器未启动 public ServerThread(ServerFrame serverFrame) this.serverFrame

37、= serverFrame; messages = new Vector<String>(); clients = new Vector<ClientThread>(); users = new HashMap<Integer, String>(); try serverSocket = new ServerSocket(Port); catch (IOException e) this.serverFrame.setStartAndStopUnable(); System.exit(0);broadcast = new BroadCast(this); b

38、roadcast.setFlag_exit(true); broadcast.start();/ 启动广播线程Overridepublic void run() Socket socket;while(flag_exit)try if(serverSocket.isClosed()flag_exit = false;elsetrysocket = serverSocket.accept();/ 接 收客户端请求,无请求则阻塞catch(SocketException e) socket = null; flag_exit = false;if(socket != null)ClientThre

39、ad clientThread = newClientThread(socket, this);clientThread.setFlag_exit(true); clientThread.start();/ 启动客户端线 程/* synchronized: 当它用来修饰一个 方法或者一个代码块的时候, 能够保证在同一时刻最 多只有一个线程执行该段代码。* 同步代码块 线程同步 ( 目的是 为了保护多个线程访问一个资源时对资源的破坏 )*/synchronized (clients) / 客户端线 程clients.addElement(clientThread);synchronized (m

40、essages) users.put(int) clientThread.getId(), "login");messages.add(clientThread.getId() + "clientThread"); catch (IOException e) e.printStackTrace();/停止服务器public void stopServer() try if(this.isAlive()serverSocket.close(); setFlag_exit(false); catch (Throwable e) /设置服务器启动标志publi

41、c void setFlag_exit(boolean b) flag_exit = b;(5) ClientThread.java public class ClientThread extends Thread public Socket clientSocket;/ 客户端套接字 public ServerThread serverThread; public DataInputStream dis; public DataOutputStream dos; /public String client_userID;private boolean flag_exit = false;/

42、客户端线程启动 标记public ClientThread(Socket socket, ServerThreadserverThread)clientSocket = socket; this.serverThread = serverThread; try newdis =DataInputStream(clientSocket.getInputStream();dos = newDataOutputStream(clientSocket.getOutputStream() catch (IOException e) e.printStackTrace(); Override public

43、 void run() while(flag_exit) try /从通道读出信息String Message = dis.readUTF(); if(Message.contains("login")/ 有 新 登 录用户StringuserInfoMessage.split("login");int userIDInteger.parseInt(userInfo1); serverThread.users.remove(userID);if(serverThread.users.containsValue(userInfo0) /新登录的与原来的有重

44、名for(int i = 0; i serverThread.clients.size(); i+)int id (int)serverThread.clients.get(i).getId();if(serverThread.users.get(id).equals(userInfo0) /用户名相同serverThread.users.remove(id); serverThread.users.put(id, userInfo0 + "_" + id);/ 额外添加标记(线程 ID ) 更新新登录的用户break;serverThread.users.put(Inte

45、ger.parseInt(userInfo1), userInfo0 + "_" + userInfo1);/ 更新原来用户 elseserverThread.users.put(userID, userInfo0);/更新服务器端用户列表Message = null;StringBuffer sb = new StringBuffer();synchronized (serverThread.clients) / 同步代码块 :客户线程for(int i = 0; i < serverThread.clients.size(); i+)int threadID =

46、(int) serverThread.clients.elementAt(i).getId();/ 客户线程 IDsb.append(String)serverThread.users.get(new Integer(threadID) + "userlist");/ 用 户 姓 名 + “userlist ”sb.append(threadID + "userlist");/ 用户姓名 +“userlist ”+客户端线程ID+ “userlist ”String userNames = new String(sb);serverThread.serverFrame.setDisUsers(userNames);更新用户列表Message = userNames;elseif(Message.contains("exit")/ 有退出 ,更新用户列表StringuserInfo=Message.split("exit");intuserID=Integer.parseIn

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 社会民生


经营许可证编号:宁ICP备18001539号-1