操作系统课程设计报告
1
课程设计
目录
一. 设计要求和目的………………………………………3
二. 背景知识…………………………………………………………4
三. 客户端设计………………………………………………………5 四. 客户端主要代码…………………………………………………6 五. 设计体会…………………………………………………………9
2
课程设计
摘要
随着网络信息时代的来临,Internet应用越来越广泛人们越来越习惯于在网上获取和交流信息。据调查显示,80%以上的人上网都会打开聊天工具来聊天,而几乎每一个年轻人都会去聊天。使用网上聊天已经成为现代年轻人一种新的交往方式。聊天室更适合于陌生人之间进行较为主观、感兴化的讨论。所以有大部分的人会进入聊天室聊天它会给人一个完全自由的聊天世界。因此我们联系所学的操作系统、数据库和MFC知识决定做一个简易的聊天系统。
一、设计要求和目的
此课题是实现一个即时通信系统完成 1:进程间通信、并发(同步/互斥)、文件读写 2:内存管理、Dll、Windows消息机制、IO。 课题任务分工:
客户端子系统:
在客户端系统的功能实现上,可以分为以下几个部分: [1]进程信息的输入
系统把用户进程要求发送的信息由键盘输入从文件读取。这部分功能是客户端子系统的基本部分,这个功能是以后各个部分的基础。系统要求做到即能够从其它子系 统中共享一部分信息 [2]进程信息的存储:
将进程的信息存储到客户端系统中,以及将发送的信息保存在文件中,以备以后进程之间通信确认以及查询。 [3]通信数据的传递及接收:
将客户所发送的信息由客户端由网络传到服务器端上,并且接受航服务器返回的接收方发送的信息,然后存储起来。
3
课程设计
用户连接设置用户设置客户端连接处理用户退出处理保存服务器端监控保存用户得到的信息用户发送信息服务器日志数据通信信息通信内容
二.背景知识 SOCKET
Socket可以看成在两个程序进行通讯连接中的一个端点,是连接应用程序和网络驱动程序的桥梁,Socket在应用程序中创建,通过绑定与网络驱动建立关系。此后,应用程序送给Socket的数据,由Socket交网络驱动程序向网络上发送出去。计算机从网络上收到与该Socket绑定IP地址和端口号相关的数据后,由网络驱动程序交给Socket,应用程序便可从该Socket中提取接收到得数据,网络应用程序就是这样通过Socket进行数据的发送与接收的
(1) 创建Socket;
(2) 打开连接到Socket的输入/出流; (3) 按照一定的协议对Socket进行读/写操作;
4
课程设计
(4) 关闭Socket.
IO技术
阻塞模式
可能造成阻塞的函数有:connect()、accept()、读写函数
C/S两端通过Socket机制进行连接
(1) 客户端的编程流程:
a创建Socket对象,向Server的监听端口请求; b通过向新Socket中读写数据与Server端通信; c关闭Socket,结束与Server端; (2)服务器端的编程流程:
1打开Server Socket,创建一个服务器型套接字和一个普通套接字,服务器型套接字在指 定端口为客户端请求的Socket 服务; 2 等待来自客户端的Client端的请求;
3接收Client端的请求,用返回的Socket建立连接; 4通过向Socket中读写数据来与Client端通信; 5关闭Socket,结束与当前Client端的通信; 6关闭SerketSocket对象结束监听服务。
三.客户端设计
客户端主要完成建立连接、消息输入、消息发送、消息存储功能。 功能含义如下:
1.建立连接:建立一个ServerSocket连接,不断侦听是否有服务端连接或者断开连接。 2.消息输入:根据用户输入的消息,将消息显示在屏幕面板上。
3.消息发送:把用户输入的消息作为字符串通过Socket端口发送到服务器。
4.消息存储:把用户输入的消息存储到data.txt文件中,以便用户以后查阅聊天记录。
客户端设计如下:
1.界面设计 2.连接设计 3.接收信息设计 4.用户信息设计 5.帮助设计
客户端用户设计的IP地址和端口号连接到相应的服务器,通过接收用户输入的消息,然后通过所监听的端口把消息发送到服务端,由服务端把消息发送到指定的用户
5
课程设计
用户连接服务器
1客户端请求连接
客户端通过connect()请求连接,填写端口号以及IP地址,填写自己的信息。
2服务器端响应
服务器端的监听器监听到客户端的连接请求后,检测后允许客户端连接到服务器。
3给客户端返回信息
连接成功后服务器返回给客户端连接成功的信息,并且给所有的用户发送用户登录信息。 4关闭Socket连接
当以上的程序都正常运行后,需要关闭Socket连接,否则将会浪费服务器与客户端之间的资源。
5用户退出
5.1接收退出信息 当用户退出时,客户端将会用基于Socket的对象输出流发送给服务器退出对象。
5.2在线列表中删除用户
用户退出后应该把用户从在线列表中删除,否则用户退出用户还在在线列表中,那么该用户下次将会无法登录。
5.3更新在线列表
用户退出后将服务器端监控界面的用户列表更新。否则用户数据将会不同步。
四.客户端主要代码
/**
* 事件处理 */
public void actionPerformed(ActionEvent e) { Object obj = e.getSource();
if (obj == userItem || obj == userButton) { //用户信息设置 //调出用户信息设置对话框
UserConf userConf = new UserConf(this,userName); userConf.setVisible(true);
userName = userConf.userInputName; }
else if (obj == connectItem || obj == connectButton) { //连接服务端设置
//调出连接设置对话框
6
课程设计
}
ConnectConf conConf = new ConnectConf(this,ip,port); conConf.setVisible(true); ip = conConf.userInputIp;
port = conConf.userInputPort;
else if (obj == loginItem || obj == loginButton) { //登录 Connect(); }
else if (obj == logoffItem || obj == logoffButton) { //注销 DisConnect();
showStatus.setText(\"\"); }
else if(obj == dataItem){ try{
File read=new File(\"data.txt\"); Desktop.getDesktop().open(read); }catch (IOException e1){ e1.printStackTrace(); } }
else if (obj == clientMessage || obj == clientMessageButton)
{ //发送消息
SendMessage();
clientMessage.setText(\"\"); }
else if (obj == exitButton || obj == exitItem) { //退出 int j=JOptionPane.showConfirmDialog(
this,\"真的要退出吗?\",\"退出\",
JOptionPane.YES_OPTION,JOptionPane.QUESTION_MESSAGE);
if (j == JOptionPane.YES_OPTION){ if(type == 1){ DisConnect(); } }
System.exit(0); } }
else if (obj == helpItem) { //菜单栏中的帮助 //调出帮助对话框
Help helpDialog = new Help(this); helpDialog.setVisible(true); }
7
课程设计
public void run(){
while(!socket.isClosed()){ try{
String type = (String)input.readObject();
if(type.equalsIgnoreCase(\"系统信息\")){
String sysmsg = (String)input.readObject(); SimpleDateFormat sdf = SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
String ly_time = sdf.format(new Date()); textarea.append(ly_time); textarea.append(\"\\n\");
textarea.append(\"系统信息: \"+sysmsg); }
else if(type.equalsIgnoreCase(\"服务关闭\")){ output.close(); input.close(); socket.close();
textarea.append(\"服务器已关闭!\\n\");
break; }
new
else if(type.equalsIgnoreCase(\"聊天信息\")){
String message = (String)input.readObject(); SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
String ly_time = sdf.format(new Date()); textarea.append(ly_time); textarea.append(\"\\n\"); textarea.append(message); try{
record=new BufferedWriter(new FileWriter(\"data.txt\",true));
record.write(ly_time); record.newLine();
record.write(message); record.newLine(); record.close();
}catch (IOException e){ e.printStackTrace(); } }
else if(type.equalsIgnoreCase(\"用户列表\")){
8
课程设计
String userlist = (String)input.readObject(); String usernames[] = userlist.split(\"\\n\"); combobox.removeAllItems();
int i =0;
combobox.addItem(\"所有人\"); while(i < usernames.length){
combobox.addItem(usernames[i]); i ++; }
combobox.setSelectedIndex(0);
showStatus.setText(\"在线用户 \" + usernames.length +
\" 人\"); } } }
} }
catch (Exception e ){
System.out.println(e); }
五.设计体会
本次课程设计历时将近两个月,当老师布置课程设计题目时还觉得这个课程设计很简单,再加上正在学Java,以及相关界面设计,当时还没有学Socket编程,觉得只要学号Socket就行差不多了,但是在做的过程中,要写需求分析等资料,之前也没怎么写过,对这方面不是很了解,所以遇到了种种困难,好多功能和逻辑都没想到,导致后来写程序的时候走了很过弯路。还有就是在学习Socket编程的时候,本来觉得不就是Java里面的一个类吗,这还不好写吗,但是真的用起来,就没有那么简单了,不是这儿出错就是那儿出错,最后看了几个例子,然后有看了好多的API函数,慢慢的,也就写的越来越顺手了。
服务器端程序设计模块:
作为C/S模式下的系统开发,很显然服务器端程序的设计是非常重要的。下
9
课程设计
面就服务器端的相关程序模块进行设计,并一定程度上实现了相关功能。 主服务器类的设计
设计服务器时需要考虑如下几个因素: (1)服务器的运行速度;
(2)服务器的响应速度,包括新建连接和发送数据的响应速度; (3)I/O吞吐量;
(4)其他:流量控制(QoS)、安全性。
针对TCP/IP协议的数据结构,设计服务器的程序结构需要考虑以下的因素: (1)多线程; (2)多进程; (3)单线程。
主服务器类实现了服务器端的多线程,使用SeverSocket s=newServerSocket(8080)语句在8080端口创建套接口;使用new ServerThread(socket)语句创建新的线程。主服务器类调用ServerThread类,而每个ServerThread实体就是一个独立的线程,刚好对应于客户端的连接请求响应线程。服务器端有一个(或多个)进程在指定的端口等待客户的连接信息,一旦连接成功,就可以按设计的数据交换方法和格式进行数据传输。
客户端只在需要的时候向服务器端发出连接请求。实现界面如图
10
课程设计
服务器端完成的功能是:对服务器的某一可用端口进行监听,以获得客户端请求,从而对客户端请求进行处理。因为是多客户同时请求,所以要采用多线程,为每一个在线用户分配一个线程,实时处理每个客户端的请求。因此, 对服务器端程序抽象如下:
二、公共数据处理
处理公共数据。如在线人数统计,客户的公共数据(如通知等),客户数据资料的存储与读取等(与数据库交互);
三、端口监听器
监听服务器某一端口,为每一在线客户建立一个会话线程;
四、客户请求处理
11
课程设计
处理客户的请求。根据客户的请求执行相应的操作。
五、服务器管理器
服务器端的管理工具,如对数据进行统计
服务器端的结构图:
客户端连接服务器端处理用户退出处理保存监控保存服务器设置用户得到的信息通信内容用户发送信息服务器日志数据通信信息
服务器实现所涉及的几个主要类以及各个类中主要的方法实现:
1. ChatServer.java 服务器的主框架类。
此类实现接口ActionListener,用于对用户事件的监听,以及对事件的处理。 /**
* 事件处理 */
public void actionPerformed(ActionEvent e) {
Object obj = e.getSource();
12
课程设计
if (obj == startServer || obj == startItem) { // 启动服务端
startService();
} else if (obj == stopServer || obj == stopItem)
{ // 停止服务端
int j = JOptionPane.showConfirmDialog(this, \"真的停止服务吗?\", \"停止服务\",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE);
if (j == JOptionPane.YES_OPTION) {
stopService(); }
} else if (obj == portSet || obj == portItem)
{ // 端口设置
// 调出端口设置的对话框
PortConf portConf = new PortConf(this); portConf.setVisible(true);
} else if (obj == exitButton || obj == exitItem)
{ // 退出程序
int j = JOptionPane.showConfirmDialog(this, \"真的要退出吗?\", \"
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE);
退出\",
if (j == JOptionPane.YES_OPTION) {
stopService(); System.exit(0); }
} else if (obj == helpItem)
{ // 菜单栏中的帮助 // 调出帮助对话框
Help helpDialog = new Help(this); helpDialog.setVisible(true);
} else if (obj == sysMessage || obj == sysMessageButton)
{ // 发送系统消息
sendSystemMessage(); } } /**
* 此函数用于启动服务器,并在指定的端口监听客户端的连接 */
public void startService() {
13
课程设计
try {
serverSocket = new ServerSocket(port, 10);
messageShow.append(\"服务端已经启动,在\" + port + \"端口侦听...\\n\");
startServer.setEnabled(false); startItem.setEnabled(false); portSet.setEnabled(false); portItem.setEnabled(false);
stopServer.setEnabled(true); stopItem.setEnabled(true); sysMessage.setEnabled(true); } catch (Exception e) {
e.printStackTrace(); }
userLinkList = new UserLinkList();
listenThread = new ServerListen(serverSocket, combobox, messageShow,showStatus, userLinkList); listenThread.start(); }
/**
* 此函数用于关闭服务器,关闭服务器后给所有在线用户发送服务器关闭的信息,服 * 务器关闭后,所有的input,output输入输出流都会关闭,并且socket也会关闭。 * 所有的数据都恢复初始化 */
public void stopService() {
try {
// 向所有人发送服务器关闭的消息 sendStopToAll();
listenThread.isStop = true; serverSocket.close();
int count = userLinkList.getCount(); int i = 0;
while (i < count) {
Node node = userLinkList.findUser(i);
14
课程设计
}
node.input.close(); node.output.close(); node.socket.close();
i++; }
stopServer.setEnabled(false); stopItem.setEnabled(false); startServer.setEnabled(true); startItem.setEnabled(true); portSet.setEnabled(true); portItem.setEnabled(true); sysMessage.setEnabled(false);
messageShow.append(\"服务端已经关闭\\n\"); combobox.removeAllItems();
combobox.addItem(\"所有人\"); } catch (Exception e) {
e.printStackTrace(); }
2. ServerListen.java 服务端的侦听类。
此类继承于Thread类,服务器的监听类,等待用户的连接,并且给在线用户发送上线用户的信息提示。 public void run(){
while(!isStop && !server.isClosed()){ try{
client = new Node();
client.socket = server.accept(); client.output = new ObjectOutputStream(client.socket.getOutputStream()); client.output.flush(); client.input = new ObjectInputStream(client.socket.getInputStream());
client.username = (String)client.input.readObject();
//显示提示信息
combobox.addItem(client.username); userLinkList.addUser(client);
SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd
15
课程设计
HH:mm:ss\");
String str = sdf.format(new Date());
textarea.append(str + \"\\n\" + \"用户 \" + client.username + \" 上线\" + \"\\n\");
textfield.setText(\"在线用户\" + userLinkList.getCount() + \"人\\n\");
recvThread = new ServerReceive(textarea,textfield, combobox,client,userLinkList); recvThread.start(); }
catch(Exception e){ } } }
3. ServerReceive.java服务器收发消息的类。
此类也继承于Thread类,主要处理客户端发来的信息,并对信息进行必要的处理,通过input输入流以及output输出流把信息输出在指定用户的对话框中。除此之外还对用户下线时作必要的处理,提示用户下线,以及显示在线人数。 public void run(){
//向所有人发送用户的列表 sendUserList();
while(!isStop && !client.socket.isClosed()){ try{
String type = (String)client.input.readObject();
if(type.equalsIgnoreCase(\"聊天信息\")){
String toSomebody = (String)client.input.readObject(); String status = (String)client.input.readObject(); String action = (String)client.input.readObject(); String message = (String)client.input.readObject();
String msg = client.username +\" \"+ action+ \"对 \"+ toSomebody + \" 说 : \"+ message + \"\\n\";
if(status.equalsIgnoreCase(\"悄悄话\")){ msg = \" [悄悄话] \" + msg; }
SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
String str = sdf.format(new Date()); textarea.append(str + \"\\n\" + msg);
if(toSomebody.equalsIgnoreCase(\"所有人\")){
16
课程设计
}
sendToAll(msg);//向所有人发送消息 }
else{ try{ }
client.output.writeObject(\"聊天信息\"); client.output.flush();
client.output.writeObject(msg); client.output.flush(); }
catch (Exception e){
//System.out.println(\"###\"+e); }
Node node = userLinkList.findUser(toSomebody);
if(node != null){ }
node.output.writeObject(\"聊天信息\"); node.output.flush();
node.output.writeObject(msg); node.output.flush();
else if(type.equalsIgnoreCase(\"用户下线\")){
Node node = userLinkList.findUser(client.username); userLinkList.delUser(node);
String msg = \"用户 \" + client.username + \" 下线\\n\"; int count = userLinkList.getCount(); combobox.removeAllItems();
combobox.addItem(\"所有人\"); int i = 0;
while(i < count){
node = userLinkList.findUser(i); if(node == null) { i ++;
continue; }
combobox.addItem(node.username); i++; }
combobox.setSelectedIndex(0);
17
课程设计
HH:mm:ss\"); \"人\\n\");
} }
SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd String str = sdf.format(new Date()); textarea.append(str + \"\\n\" + msg);
textfield.setText(\"在线用户\" + userLinkList.getCount() +
sendToAll(msg);//向所有人发送消息
sendUserList();//重新发送用户列表,刷新
break; } }
catch (Exception e){
//System.out.println(e); }
18
因篇幅问题不能全部显示,请点此查看更多更全内容