计算机网络实习报告.docx
《计算机网络实习报告.docx》由会员分享,可在线阅读,更多相关《计算机网络实习报告.docx(22页珍藏版)》请在冰点文库上搜索。
![计算机网络实习报告.docx](https://file1.bingdoc.com/fileroot1/2023-5/6/8779f862-c9a9-4d2c-9425-6e072121ec20/8779f862-c9a9-4d2c-9425-6e072121ec201.gif)
计算机网络实习报告
福建农林大学计算机与信息学院
信息工程类
课程实习报告
课程名称:
计算机网络
实习题目:
TCP通信程序的设计
姓名:
陈富强
系:
计算机系
专业:
计算机科学与技术
年级:
2009级
学号:
091150011
指导教师:
周术诚
职称:
教授
2012年5月11日
福建农林大学计算机与信息学院信息工程类
课程实习报告结果评定
评语:
成绩:
指导教师签字:
评定日期:
目录
实习的目的和任务3
实习要求3
实习地点3
主要仪器设备(实验用的软硬件环境)3
实习内容3
客户端通讯核心3
定义自己的数据包3
客户端套接字类6
服务器通讯核心类9
服务器配置9
用户管理10
日志管理11
服务器管理14
服务器通讯核心15
客户端和服务器的通讯16
问题讨论与分析17
结束语19
TCP通信程序的设计
1.实习的目的和任务
掌握java语言TCP/IP通讯程序的设计
2.实习要求
使用TCP/IP协议实现一个客户端和服务器通讯软件,多个客户端可以通过
服务器实现单聊和群聊,也可以通过服务器获取公告信息,在线用户列表,
以及修改密码。
服务器可以查看日志文件,查看当前在线用户和修改用户的
信息,可以对客户端发送公告,强制用户退出等功能。
3.实习地点
福建农林大学
4.主要仪器设备(实验用的软硬件环境)
Windows7操作系统。
TCP/IP协议
编程工具:
MyEclipse10
5.实习内容
5.1客户端通讯核心
5.1.1定义自己的数据包
客户端通讯时候必须有一个套接字socket来与服务器进行数据交流。
这里的数据的格式,我自己编写了一个类DatePackage,并且它实现了可序列化,只有服务器与客户端同时拥有这个类才能进行发送包的解析与取得参数,才能知道这个数据包是要做什么。
publicclassDatePackageimplementsSerializable{
/**
*
*/
privatestaticfinallongserialVersionUID=-3425205872711934961L;
/**
*登录
*/
publicstaticfinalintLOGIN=10;
/**
*登录响应
*/
publicstaticfinalintLOGIN_REPLAY=11;
/**
*获取公告
*/
publicstaticfinalintGETPUB=20;
/**
*获取公告响应
*/
publicstaticfinalintGETPUB_REPLAY=21;
/**
*获取在线用户
*/
publicstaticfinalintGETLIST=30;
/**
*获取在线用户响应
*/
publicstaticfinalintGETLIST_REPLAY=31;
/**
*聊天
*/
publicstaticfinalintCHAT=40;
/**
*聊天响应
*/
publicstaticfinalintCHAT_REPLAY=41;
/**
*修改密码
*/
publicstaticfinalintCHANGEPW=51;
/**
*修改密码响应
*/
publicstaticfinalintCHANGEPW_REPLAY=52;
privateintdoWhat;
privateMapparams=newHashMap();
/**
*获取数据包要做什么
*@return做什么
*/
publicintgetDoWhat(){
returndoWhat;
}
/**
*设置数据包要做什么
*@paramdoWhat做什么
*/
publicvoidsetDoWhat(intdoWhat){
this.doWhat=doWhat;
}
/**
*获取数据包参数
*@paramname参数名字
*@return参数的值
*/
publicObjectgetParams(Stringname){
returnparams.get(name);
}
/**
*设置数据包参数
*@paramname参数名字
*@paramvalue参数值
*/
publicvoidsetParams(Stringname,Objectvalue){
params.put(name,value);
}
}
当要通过TCP/IP协议发送一个数据包给服务器时,必须定义这个数据包是做什么,通过传入静态整形共有变量可以轻松知道这个数据包是做什么的。
如要登录则传入CHAT这个整型变量。
各个变量都有注释说明是做什么的这里就不再赘述了。
除了要让服务器或客户端知道要这个数据包是要做什么的之外还要传入一些额外的参数,例如LONGIN登录这个操作需要传入账户和密码这两个参数,这个时候就可以把参数放到Mapparams里面一起传过去就可以了。
当然事先要先知道里面会有那些参数,服务器或客户端才能将其正确的取出来。
5.1.2客户端套接字类
当客户点击客户端的登录窗口时候,套接字就开始建立了。
这个时候他会通过输入窗口中获取目的主机的ip地址和端口号。
建立的同时还建立了输入流和输出流。
这里使用的对象输入字节流和对象输出字节流,而需要输出和输入的对象正是刚刚上面定义的数据包。
JAVA中规定要输出对象和识别一个对象,这个对象必须实现序列化接口。
登录时候将创建一个数据包,这个数据包含有用户名密码等信息,然后发送给服务器,同时客户端通讯核心类会启动一个线程不断的监听从服务器传来的数据包并进行解析。
若登录成功,服务器穿来的数据包应该包含登录状态,若为success则把客户端的登录窗口切换为聊天窗口登录窗口和聊天窗口分布如下:
如上面所示,在创建聊天窗口的同时还需要同时获得在线用户列表和公告,这个原理和登录的时候是一样的,只要在创建聊天窗口的时候发送两个数据包,一个数据包是获得公告,一个是获得在线列表,同时接收服务器发来的回应包,从回应包里面取得自己需要的数据,并分别在聊天窗口相应的部分显示出来。
聊天公告很简单,取出来的就是一串从服务器发来的字符串。
而在线用户列表则是通过LIST来实现的,LIST里面装的是自己定义的USER类,这个类同数据包类一样,都需要实现可序列化,并且服务器应该也有一个同他一模一样的类。
USER类是对用户信息的封装,用户所有的信息都在USER类中。
或许在线用户列表也可以用字符串来实现,但是考虑到后面还有一些功能可能需要使用到在线用户的各种信息,所有使用USER类来实现会比较好,是软件的可拓展性提升。
修改密码这个功能也是通过向服务器发数据包来实现的。
当然如果很多人同时修改密码可能会造成服务器负担太重,而导致回应包的延迟从而使得用户进入等待的时间也增加了。
通过套接字的对象输出字节流发送修改密码的数据包应该包含用户的原密码和要修改的密码,重复输入密码的验证应该放到客户端进行,否则服务器负担会更加沉重。
所以能放到客户端进行的操作尽量放到客户端进行。
聊天这个操作分为群聊和单聊,所以现在客户端判断好是单聊还是群聊再通过额外信息加入到数据包发给服务器。
判断单聊还是群聊需要在在线用户列表里面加个事件监听,当鼠标点击某个对象的时候,只要判断当前点击的这个对象是否为USER的一个实例就可以判断单聊还是群聊。
因为所有人是一个字符串不是USER对象,而其他人就是USER的对象。
另外,自己是不能跟自己聊天的,所以在判断的时候还要判断当前这个对象是否就是自己。
在客户端通讯核心类中有个USER成员变量,这个变量保存的就是客户端这个客户的所有信息。
当然这个信息在登录的时候由服务器传过来的。
所有整个客户端与服务器的通讯都是通过套接字SOCKET的对象输入流和对象输出流来实现的,而SOCKET又是基于TCP/IP实现的,这里的数据包可以理解为自己定义的通讯协议,只有服务器和客户端的数据包类是一致的才能让服务器解析你的数据包是什么意思并作出相应的解答和回应。
回应的还是方法还是通过传输这个数据包,只不过里面的信息已经换成客户端需要的信息了。
通讯核心类的方法如下(聊天登录窗口和聊天界面的代码就不附上了):
PublicgetUser();//获取当前用户的User类
PublicsetUser(User)//设置当前用户的User类
PublicOICQ_ClientNet(String,int)//构造方法,创建SOCKET
ProtectclassRecv//线程类,用于不断的接受从服务器发来的数据包并解析处理
Publicprocess(DatePackage)//处理数据包
PublicprocessChangePw(DatePackage)//处理修改密码
PublicprocessChatReplay(DatePackage)//处理聊天响应包
PublicprocessLoginReplay(DatePackage)//处理登录响应包
PublicprocessGetPubReplay(DatePackage)//处理获取公告
PublicprocessGetListReplay(DatePackage)//处理获取在线用户列表
Publicsent(DatePackage)//发送数据包
Publiclogin(DatePackage)//登录
Publicclose()//关闭套接字连接和各种资源
PublicGetPub()//获取公告
PublicGetList()//获取在线用户列表
PublicChat()//聊天
5.2服务器通讯核心类
5.2.1服务器配置
服务器配置窗口需要填入各种参数才能登录服务器,如连接oracle的连接和用户名,密码,驱动,端口等信息,如果正确无误后按进入服务器按钮进入服务器。
5.2.2用户管理
聊天系统采用的是Oracle数据库,当然也可以根据以后的情况来改变,因为所以的参数都是通过配置文件来设置和使用的。
在用户管理中可以通过点击查询按钮来查询用户的信息,还可以通过添加新的用户按钮添加新用户,修改密码,修改所有人密码,删除用户等操作。
这些操作有一个共同的特征,就是访问数据库并且做一些增删改查的操作。
所以可以专门写一个类来实现这些与数据库打交道的方法,于是我写了UserDao这个类。
这个类先从另外一个专门获得连接数据库的connection中获得其所必须的connection然后进行各种增删改查操作。
只有选择一个用户,修改用户,删除用户和重置密码才能使用,这个还是通过监听Table类来实现。
并且不能对在线用户进行删除和重置密码等操作。
截图如下:
5.2.3日志管理
当有用户登录和下线时候,当服务器启动和关闭时候都需要进行写入日志操作。
同样专门写一个类来实现这个操作,这个类的方法如下:
/**
*日志管理类
*@authorSlo
*
*/
publicclassLog{
privateStringfolder="log";
privateDateFormatfileNameFormat=newSimpleDateFormat("yyyy-MM-dd");
privateDateFormatlogDateFormat=newSimpleDateFormat("yyyy-MM-ddHH:
mm:
ss");
/**
*构造方法
*/
publicLog(){
Filef=newFile(folder);
if(!
f.exists()){
f.mkdir();
}
}
/**
*写一条日志
*@paramdate日期
*@paramcontent内容
*/
publicvoidwriteLog(Datedate,Stringcontent){
FileWriterfw=null;
Filef=newFile(folder+"/"+fileNameFormat.format(date)+".log");
try{
f.createNewFile();
fw=newFileWriter(f,true);
fw.write("["+logDateFormat.format(date)+"]:
"+content);
fw.write("\r\n");
fw.flush();
}catch(IOExceptione){
e.printStackTrace();
}finally{
try{
if(fw!
=null)fw.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
/**
*读一条日子
*@paramdate日期
*@paramkeyWord关键字
*@return内容
*/
publicListreadLog(Datedate,StringkeyWord){
Filef=newFile(folder+"/"+fileNameFormat.format(date)+".log");
BufferedReaderbf=null;
Listlist=newArrayList();
try{
bf=newBufferedReader(newFileReader(f));
Strings=null;
while((s=bf.readLine())!
=null){
if(s.indexOf(keyWord)!
=-1){
list.add(s);
}
}
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}finally{
try{
if(bf!
=null)bf.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
returnlist;
}
}
截图如下:
5.2.4服务器管理
这个界面是可以开启服务器和关闭服务器,并且可以通过选中上面的在线用户来强制用户下线。
开启服务时候,通讯核心代码的ServerSocket开始创建并且占用一个端口,不断的接受客户端的连接,每当有一个客户端连接进来就在启动一个专门的线程来为这个客户服务,因此要强制用户下线只需要将用户的socket关闭就可以了,用户的客户端就会出现异常,再进行捕获异常就可以解决了。
发送公告,只需要将所有在线用户取出并循环发出公告响应包让客户端收到即可。
其界面如下:
5.2.5服务器通讯核心
在开启服务器时候,通讯核心类就开始发挥其作用了,可以说,这个类是服务器的灵魂。
开启服务器的时候会有一个线程不断的接受远程客户端的请求,当接受到一个请求的时候,他就开启另外一个线程来专门为这个客户服务。
这个线程可以响应客户所有的请求并且发送响应包。
当然,这个类有一个成员变量,可以记录所有连接上的用户信息,仅仅记录用户的socket即可。
用户发出一个数据包,线程就开始分析这个数据包是做什么的并且做出分析和响应,例如一个用户登录上服务器,服务器先验证由客户端发来的账户密码,然后到数据库查询是否有此人,若有则反馈给客户端一个success的数据包,当客户端接收到这个数据包就可以做出对应的响应了。
当用户因为意外情况掉线或断开连接时候,只要在服务器里面捕获一个异常就可以了。
处理这个异常应该包括把用户的在线状态设置为离线并且发送给所有用户通知他们更新在线用户列表。
同时把服务器管理面板在线用户更新。
当关闭服务器的时候,要先确认没有人在线才能关闭服务器,并且关闭服务器要把各种资源都关闭掉。
包括各种输出流和输入流,还有ServerSocket。
若没有关闭完全的话服务器随着开启关闭的次数增多会造成服务器资源匮乏,最后崩溃。
5.3客户端和服务器的通讯
客户端:
连接服务器:
Socket
服务器内部:
6.问题讨论与分析
问题一:
当一个客户端发送完消息后,发第二条的时候,连接就断掉了,而且另外一个客户端也收不到。
截图如下:
两个客户端相互发送的消息都没有收到。
后来发现是服务器的成员变量放错位置了导致输出流和输入流丢失。
改正后截图如下:
问题二:
在没有启动服务的时候,服务器关闭不了。
观察代码发现,关闭时候调用了通讯核心的关闭方法,然而在没有开启服务时候通讯核心里面是没有内容的,所以会出现空指针错误。
解决的办法就是捕获异常让程序终止。
7.结束语
这个程序让我学到了TCP/IP通讯的基本内容,套接字的连接,服务器的响应都是通讯的具体应用。
同时我们自己还可以定义自己的通讯协议,如DatePackage类,客户端发送LOGIN则服务器可以响应这个请求,而这个LOGIN就是我们自己定义的。
通讯协议在任何地方都占有很重要的低位,特别是C/S模式中。