当前位置:文档之家› 操作系统课程设计聊天室系统设计报告

操作系统课程设计聊天室系统设计报告

青岛科技大学操作系统课程设计

学生班级计算机132班

学生学号1308010204

学生姓名王永远

2016 5 18

______年 ___月 ___日

目录

设计题目:聊天室系统 (3)

一、设计目的及要求 (3)

1.1设计目的 (3)

1.2设计要求: (3)

二、技术背景 (3)

2.1网络编程 (3)

2.2网络通信 (3)

2.3 Socket (3)

2.4 TCP传输 (4)

2.5 Java的多线程机制 (4)

三、需求分析 (4)

3.1客户端 (4)

3.1.1登录 (4)

3.1.2多人聊天 (4)

3.1.3单人聊天 (5)

3.2服务器端 (5)

四、总体设计 (5)

4.1设计思想流程图 (5)

4.2设计思想分析 (6)

五、详细设计 (6)

5.1客户端设计 (7)

5.1.1登录界面 (7)

5.1.2聊天室界面 (7)

5.1.3单人聊天界面 (7)

5.2服务器端设计 (8)

六、系统测试 (8)

6.1登录测试 (8)

6.1.1用户名为英文字母 (8)

6.1.2用户名为中文 (9)

6.1.3用户名为标点符号 (9)

6.1.4多个用户有重名 (10)

6.1.5服务器地址不合法 (11)

6.1.6端口号不合法 (12)

6.1.7服务器未开启 (12)

6.2群发消息测试 (13)

6.3私聊测试 (15)

6.4用户列表显示测试 (16)

七、心得体会 (16)

八、参考资料 (16)

九、程序清单 (16)

设计题目:聊天室系统

一、设计目的及要求

1.1设计目的

通过该聊天室系统,掌握网络编程的概念及基于网络的C/S模式软件系统开发,掌握基于TCP 协议的Socket编程,掌握Java的多线程机制。

1.2设计要求:

实现多个用户之间类似于QQ的聊天程序,有聊天界面,多用户之间既可以实现群聊,也可以单独聊天。

二、技术背景

2.1网络编程

就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。

2.2网络通信

(1)IP地址:网络中设备的标识,不易记忆,可用主机名

要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接受数据的计算机和识别发送的计算机,在TCP/IP协议中,这个标识号就是IP地址。

(2)端口号:用于标识进程的逻辑地址

物理端口网卡口

逻辑端口我们指的就是逻辑端口

A:每个网络程序都会至少有一个逻辑端口

B:用于标识进程的逻辑地址,不同进程的标识

C:有效端口:0~65535,其中0~1024系统使用或保留端口。

(3)传输协议:通信的规则,常见的有UDP,TCP

UDP:将数据源和目的封装成数据包中,不需要建立连接;每个数据报的大小在限制在64k;因无连接,是不可靠协议;不需要建立连接,速度快。

TCP:建立连接,形成传输数据的通道;在连接中进行大数据量传输;通过三次握手完成连接,是可靠协议;必须建立连接,效率会稍低。

2.3 Socket

(1)Socket套接字

网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。

(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建立服务器端的socket服务,需要一个端口

B服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信

C通过客户端的获取流对象的方法,读取数据或者写入数据

D如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

2.5 Java的多线程机制

进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。

线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。

多进程是指操作系统能同时运行多个任务(程序),多线程是指在同一程序中有多个顺序流在执行。在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,启动JVM实际上就是在操作系统中启动了一个进程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

三、需求分析

实现聊天的功能,采用Java Socket编程,服务器与客户端采用了TCP/IP连接方式,在设计聊天方案时,可将所有信息发往服务器端,再由服务器进行处理,服务器端是所有信息的中心。

3.1客户端

3.1.1登录

用户需要填写用户名、服务器地址、端口号才可以进入聊天室与在线用户聊天,此外,服务器地址默认是本机地址,端口号默认是5000。用户名可以中文,英文字母或标点符号,服务器地址必须是符合点分十进制的合法地址,端口号可以修改,但服务器端程序内默认端口号也必须修改。该聊天室不需要注册,直接登录即可聊天,退出后,系统不保留用户任何信息。

若新登录用户与在线用户的用户名重名,则系统会自动修改用户名。格式为:用户名+“_客户端线程ID”。

3.1.2多人聊天

每个在线用户都可以发送聊天信息,服务器端会一直监听,并把每一个在线用户发送的聊天信

息转发到每一个客户端。

聊天室的聊天信息格式为:

用户名时间(yyyy-MM-dd HH:mm:ss)

聊天信息

3.1.3单人聊天

用户可以选择某一个在线用户实现单人聊天,该聊天信息不会在聊天室显示,只有单聊的两个人能够看到。

单人聊天的聊天信息格式为:

用户名时间(yyyy-MM-dd HH:mm)

聊天信息

3.2服务器端

服务器端主要处理客户端的请求,包括用户的登录,发送多人聊天信息,退出聊天室,单人聊天请求,发送单人聊天信息,并且随时更新在线用户列表。

四、总体设计

4.1设计思想流程图

4.2设计思想分析

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

对于客户端的各种请求,实际上都是通过在客户端发往服务器的各种字符流区分的,具体的方法就是在消息的内部添加特殊字符串,从而实现服务器对消息请求的识别。比如对于登陆信息,消息中添加的内容就是“@login”,而对于私聊中的消息,消息中添加的内容就是“@single”,其他的同理都添加了相应内容。当然,对于客户端来说,这些都是透明的,用户的操作并没有受到任何影响。在服务器端,消息被检测分析后,变回根据具体的目的进行处理,比如是私聊消息,服务器便会根据其内部添加的信息,向目标端转发该条消息,当目标端接收到连接请求后,会主动建立一个私聊窗口,从而实现私聊。

服务器端需要能同时接受多个用户的请求,为了实现这一点,一般使用多线程机制来处理,对每一个客户端连接通信,服务器端都有一个线程专门负责处理。

对于客户端的各种请求,内部添加的信息分别如下:

●@clientThread客户端线程启动

messages.add(clientThread.getId() + "@clientThread");

客户端线程ID

●@login登录客户端

username+"@login"+getThreadID()+"@login"

用户名+客户端线程ID

●@userlist用户列表

https://www.doczj.com/doc/2110594433.html,ers.get(new Integer(threadID)) + "@userlist"+threadID + "@userlist"

用户名+客户端线程ID

●@chat群聊

username + "@chat" + getThreadID() + "@chat"+ mess + "@chat"

用户名+客户端线程ID+聊天信息

●@serverexit服务器退出

serverThread.messages+"@serverexit"

●@single单聊

https://www.doczj.com/doc/2110594433.html,ername + "@single" + client.getThreadID() + "@single" +(int)client.clientuserid.get(index) + "@single" + mess + "@single";

用户名+客户端线程ID+客户端线程ID+聊天信息

●@exit退出群聊

username + "@exit" + getThreadID() + "@exit"

用户名+客户端线程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私聊测试

6.4用户列表显示测试

无论已经登录用户的退出,还是新用户成功登录,用户列表显示部分都能正确显示。

七、心得体会

这段时间通过不断的修改,我终于把聊天室系统完成了,虽然它只有简单的聊天功能,但通过它,我不但巩固了以前学的知识,而且学到了许多在课堂中学不到的知识。通过这次课程设计,我更坚定了理论与实际相结合是十分重要的想法,即使一个人读了再多的技术图书,但没有相关的实践经验,那么他也不会真正地掌握一门技术。只有把理论知识与实践相结合,才会深入的了解并提高自己的独立思考能力。

通过这次的课程设计,我将自己所学的Java语言得到了实际的应用,在完成的过程中也遇到了许多困难,但通过不断地查阅资料,最终还是解决了,在这个过程中,我学会了独立思考,同时也让我明白完成一件事情要不断开阔视野,拓展知识面,解放自己的思维。总之,在完成课程设计的过程中,我学会了如何克服开发中遇到的技术困难,学会了独立面对并解决问题。

八、参考资料

叶核亚JA V A程序设计实用教程(第2版)电子工业出版社

朱福喜,路迟JA V A语言与面向对象程序设计武汉大学出版社

沈文炎Java高级编程机械工业出版社

九、程序清单

(1)Client.java

public class Client extends Thread{

public Socket c_socket ;//套接字

private Client_chatFrame c_chatFrame;//聊天室聊天界面

private 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 c_singleFrames;//单人聊天,用户名为键,单聊客户端为值

public List username_online;//在线用户

public List clientuserid;//用户ID

public String username = null;//用户名

public String chat_re;//通道内相关信息

//getter, setter方法

public Client_chatFrame getC_chatFrame() {

return c_chatFrame;

}

public Client_singleFrame 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 void 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();

username_online = new ArrayList();

clientuserid = new ArrayList();

// signlechatuse = new ArrayList();

}

//main方法,设置进入时登录界面

public static void main(String[] args) {

Client client = new Client();

Client_enterFrame c_enterFrame = new Client_enterFrame(client);

client.setC_enterFrame(c_enterFrame);

c_enterFrame.setVisible(true);

}

//登录客户端

public String login(String username, String hostIp, String hostPort) {

https://www.doczj.com/doc/2110594433.html,ername = username;//用户名

String login_mess = null;//错误信息或true

try {

c_socket = new Socket(hostIp, Integer.parseInt(hostPort));

} catch (NumberFormatException e) {

login_mess = "连接的服务器端口号port为整数,取值范围为:1024

return login_mess;

} catch (UnknownHostException e) {

login_mess = "主机地址错误";

return login_mess;

} catch (IOException e) {

login_mess = "连接服务器失败,请稍后再试";

return login_mess;

}

return "true";

}

//创建一个客户端聊天界面,启动一个线程

public void showChatFrame(String username) {

getDataInit();

c_chatFrame = new Client_chatFrame(this,username);

c_chatFrame.setVisible(true);

flag_exit = true;//客户端已启动

this.start();//启动线程

}

//初始化,建立连接通道

private void getDataInit() {

try {

dis = new DataInputStream(c_socket.getInputStream());

dos = new DataOutputStream(c_socket.getOutputStream());

} catch (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 != null){

if(chat_re.contains("@clientThread")){

int local = chat_re.indexOf("@clientThread");

setThreadID(Integer.parseInt(chat_re.substring(0, local)));//启动客户端线程,设置ThreadID

try {

dos.writeUTF(username + "@login" + getThreadID() + "@login");

} catch (IOException e) {

e.printStackTrace();

}

}else{

if(chat_re.contains("@userlist")){

c_chatFrame.setDisUsers(chat_re);

}else{

if(chat_re.contains("@chat")){

c_chatFrame.setDisMess(chat_re);

}else{

if(chat_re.contains("@serverexit")){

c_chatFrame.closeClient();

}else{

if(chat_re.contains("@single")){

c_chatFrame.setSingleFrame(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() + "@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 {

public 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.setLocation((sSize.width - wSize.width)/ 2, (sSize.height - wSize.height)/ 2);//窗体居屏幕中央

}

}

(3)Server.java

public class Server {

private ServerFrame serverFrame;

private ServerThread serverThread;

public ServerFrame getServerFrame() {

return serverFrame;

}

public void setServerFrame(ServerFrame serverFrame) {

this.serverFrame = serverFrame;

}

public Server(){}

//启动服务器线程

public void startServer() {

try{

serverThread = new ServerThread(serverFrame);

}catch(Exception e){

System.exit(0);

}

serverThread.setFlag_exit(true);

serverThread.start();

相关主题
文本预览
相关文档 最新文档