西安邮电大学Socket网络编程实验报告.docx
《西安邮电大学Socket网络编程实验报告.docx》由会员分享,可在线阅读,更多相关《西安邮电大学Socket网络编程实验报告.docx(17页珍藏版)》请在冰点文库上搜索。
西安邮电大学Socket网络编程实验报告
《高级Internet》实验报告
题目:
Socket网络编程
学生姓名:
班级:
学号:
指导老师:
王文浪
成绩:
西安邮电大学计算机学院
2015年4月27日
一、实验目的
1熟悉Socket网络编程
2熟悉CS客户机模式
3熟悉ServerSocket编写服务端
4熟悉使用多线程机制处理业务
5了解客户机之间通信方式
二、实验内容及要求
1采用CS模式,通过客户机向服务器端发送消息;
2采用Socket来实现客户机之间的网络通信;
3通过ServerSocket创建服务端来处理接受客户机请求;
4通过多线程方式来处理客户机之间的通信请求;
5实现通信的界面窗口;
6实验内容有发送私聊消息、向选中用户发送文件、用户接受文件。
三、实验过程
1确定编程语言,架构方式,实及通信的协议
编程语言:
Java;
结构方式:
CS客户机模式;
通信协议:
在过程中为每一条通信的信息设定一个type参数,标志着通信的类型;
1:
向选中用户发送私信;0:
上下线更新
-1:
下线请求2:
请求发送文件
3:
确定接收文件
2创建工程项目SuperInternet_Client(客户机)SuperInternet_Server(服务器端)
项目客户机目录结构:
项目的服务器端目录结构:
3创建登录界面ChatLogin.java
1)(客户机)实现的核心代码:
//监听登录按钮事件(登录时从配置文件中读取用户的配置信息)btnLoginButton.addActionListener(newActionListener(){
@Override
publicvoidactionPerformed(ActionEvente){
//读取配置文件
PropertiesuserProperties=newProperties();
Filefile=newFile("Userinfo.properties");
//加载配置文件的信息
LoadPropertiesUtil.loadPro(userProperties,file);
//获得输入用户名
Stringuname=textField.getText().toString();
if(file.length()!
=0){
//在配置文件中是以键值对的方式存放用户的信息,用户名(key)=密码值(value)
if(userProperties.containsKey(uname)){//配置文件中包含该用户
//获取密码框输入的用户名
Stringupass=newString(passwordField.getPassword());
//判断用户密码是否正确
if(upass.equals(userProperties.getProperty(uname))){
//创建网络连接并显示聊天窗口
System.out.println("连接成功!
!
");
try{
//创建本地网络连接,端口为8080
Socketclient=newSocket("127.0.0.1",8080);
//将登录按钮设置为不可编辑
btnLoginButton.setEnabled(false);
//登录成功,打开聊天窗口
ChatRoomchatroom=newChatRoom(uname,client);
chatroom.setVisible(true);//显示聊天窗口
setVisible(false);//隐藏登录窗口
}catch(UnknownHostExceptione1){
e1.printStackTrace();
ErrorInfoShowUtil.errorTip("连接服务器失败,请重新登录!
",contentPanel);
}catch(IOExceptione1){
e1.printStackTrace();
ErrorInfoShowUtil.errorTip("连接服务器失败,请重新登录!
",contentPanel);
}
}else{
lblPrompt.setText("您输入的密码错误!
");...后续省略
}
});
2)创建登录界面中调用的工具类
读取配置文件工具类(LoadPropertiesUtil),实现的核心代码:
publicclassLoadPropertiesUtil{
/**
*加载配置文件的信息
*@paramproperties
*@paramfile
*/
publicstaticvoidloadPro(Propertiesproperties,Filefile){
//文件不存在,创建该文件
if(!
file.exists()){
try{
file.createNewFile();
}catch(IOExceptione){
e.printStackTrace();
System.out.println("创建配置文件失败,请检查!
");
}
}
//加载配置文件的信息
try{
properties.load(newFileInputStream(file));
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
错误信息窗口工具类(ErrorInfoShowUtil.java),实现核心代码:
/**
*显示错误连接错误提示框
*@authorXiyouJackielin
*
*/
publicclassErrorInfoShowUtil{
publicstaticvoiderrorTip(StringerrorInfo,JPanelcontentPanel){
JOptionPane.showMessageDialog(contentPanel,
errorInfo,"ErrorMessage",JOptionPane.ERROR_MESSAGE);
}
}
3)编写服务器端ChatServer.java,接收客户机的请求连接,并返回当前在线人数:
服务器端中的静态区初始化监听连接端口及在线人数集合变量
/**
*服务器端,将听客户端请求并处理请求
*@authorXiyouJackielin
*
*/
publicclassChatServer{
//创建服务器端的socket
privatestaticServerSocketserverSocket;
//创建集合存放在线的用户
privatestaticHashMaponlines;
//创建静态块区,初始化相应的数据,只要创建一次
static{
try{
serverSocket=newServerSocket(8080);
onlines=newHashMap();
}catch(IOExceptione){
e.printStackTrace();
}
}
......//后续代码省略(开启线程)
}
④ChatRegister.java(注册页面),实现核心代码:
1)用户输入注册的信息,首先先在配置文件中寻找是否有相同的用户名,如果存在相同的用户名,则提示用户名已经重复,重新输入;
2)验证用户输入的信息,如果两次密码输入不一致,则提示密码错误;
3)用户输入注册信息验证通过后,打开配置文件Userinfo.properties,将注册信息保存至配置文件中;
@Override
publicvoidactionPerformed(ActionEvente){
PropertiesuserProperties=newProperties();
Filefile=newFile("Userinfo.properties");
LoadPropertiesUtil.loadPro(userProperties,file);
Stringuname=textField.getText().toString();
Stringupass=newString(passwordField.getPassword());
StringconfirmPass=newString(confirmpasswordField.getPassword());
//判断配置文件中是否存在该用户
if(uname.length()!
=0){
if(userProperties.containsKey("uname")){
lblPrompt.setText("该用户名已经存在!
");.....//后续省略}
编写保存用户注册信息方法,核心实现代码:
privatevoidsaveUserinfo(PropertiesuserProperties,Filefile,
Stringuname,Stringupass,StringconfirmPass){
if(upass.equals(confirmPass)){
if(upass.length()!
=0){
//以uname为键,以upass为值,保存到配置文件中
userProperties.setProperty(uname,upass);
try{
//将userProperties中的属性列表(键和值)方式写入输出流
userProperties.store(newFileOutputStream(file),
"Copyright(c)XiyouJackielin");
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
//注册完之后将注册按钮设置为不可操作
btnRegisterButton.setEnabled(false);
//返回登录界面
ChatLoginframe=newChatLogin();
frame.setVisible(true);
setVisible(false);......//后续代码省略
4编写聊天窗口(ChatRoom.java)实现客户机之间的聊天功能
在聊天窗口中编写子线程类并开启,用于接收服务器返回的信息并显示出来;
0:
更新在线用户列表
case0:
{
//更新列表
onlines.clear();
//从服务器读取到当前的在线用户
HashSetclients=chatBean.getClients();
Iteratorit=clients.iterator();
while(it.hasNext()){
Stringele=it.next();
if(uname.equals(ele)){
onlines.add(ele+"(我)");
}else{
onlines.add(ele);
}
}
//这里通过渲染器渲染用户列表
abstractListModel=newOnlinesUserListModel(onlines);
list.setModel(abstractListModel);
......//后续代码省略
-1:
户下线;在客户机用户下线直接return结束当前窗口;
1:
私信
case1:
{//私聊
Stringinfo=chatBean.getCurrentTime()+""+chatBean.getName()
+"对"+chatBean.getClients()+"说:
\r\n";
//如果信息中包含我的名字,则将其换成昵称'我'
if(info.contains(uname)){
info=info.replace(uname,"我");
}
acpMessage.play();
textArea.append(info+chatBean.getInfo()+"\r\n");
textArea.selectAll();
break;
}
2:
向用户发送文件;
思路:
由于向用户发送文件需要接受方法确定是否接收,故采用子线程的方式处理请求过程,并向服务器发送相应的结果;
3:
确定接收文件信息;
思路:
发送方向接收方发出发送文件请求后,接收方答应接收或拒收文件,响应信息由服务器转发到发送方,对相应的信息作出相应的操作。
4:
拒接接收文件的信息;
打印出接收方拒绝接收文件的信息;
5服务器端响应处理业务
0:
更新上下线核心代码
//创建服务器端的封装信息类ChatBean,并将响应的信息发送给客户机
ChatBeanserverUplineBean=newChatBean();
serverUplineBean.setType(0);//处理的是0:
上下线更新
//设置服务器端返回信息(通知所有用户xx上线了)
serverUplineBean.setInfo(chatBean.getCurrentTime()+""
+chatBean.getName()+"上线了");
//通知所有用户有人上线
HashSetonlineUserSet=newHashSet();
//遍历在线用户onlines得到用户名的Set集合
SetonlineUserName=newHashSet();
onlineUserName=onlines.keySet();
//将用户名添加到在线用户集合中
onlineUserSet.addAll(onlineUserName);
//封装服务器端的ChatBean类
serverUplineBean.setClients(onlineUserSet);
//调用发送给全部在线用户方法
sendAll(serverUplineBean);
-1:
处理用户下线
//设置下线显示的消息
ChatBeanserverDownlineBean=newChatBean();
serverDownlineBean.setInfo(chatBean.getCurrentTime()+""
+chatBean.getName()+""+"下线了");
System.out.println(chatBean.getName());
serverDownlineBean.setType(0);
HashSetonLineUserSet=newHashSet();
onLineUserSet.addAll(onlines.keySet());
serverDownlineBean.setClients(onLineUserSet);
//将下线消息告知所有在线的用户
sendAll(serverDownlineBean);
return;//由于用户是下线,这里返回结束服务器为该类创建的业务处理线程
1:
发送私信消息
//创建服务器的ChatBean对象用于封装自己返回给客户机的消息
ChatBeanserverMessageBean=newChatBean();
serverMessageBean.setType
(1);
serverMessageBean.setClients(chatBean.getClients());//发送的用户
serverMessageBean.setInfo(chatBean.getInfo());
serverMessageBean.setName(chatBean.getName());serverMessageBean.setCurrentTime(chatBean.getCurrentTime());
//向指定的用户发送消息
sendMessage(serverMessageBean);
break;
2:
接收文件请求
//创建服务器的ChatBean对象用于封装处理用户发送文件的事务返回给客户机处理的信息
ChatBeanserverSendFileBean=newChatBean();
Stringinfo=chatBean.getCurrentTime()+""+chatBean.getName()
+"向您传送文件,是否接收文件";
serverSendFileBean.setType
(2);
serverSendFileBean.setClients(chatBean.getClients());//设置被选中的用户
serverSendFileBean.setFileName(chatBean.getFileName());//文件名称
serverSendFileBean.setSize(chatBean.getSize());//文件大小
serverSendFileBean.setInfo(info);
serverSendFileBean.setName(chatBean.getName());//文件的来源serverSendFileBean.setCurrentTime(chatBean.getCurrentTime());//设置发送的时间
//向选中的用户发送数据
sendMessage(serverSendFileBean);
break;
3:
确认接收文件信息
//创建服务器的ChatBean用于封装接收方反馈给发送方的信息
ChatBeanconfirmReceiveFielInfo=newChatBean();
confirmReceiveFielInfo.setType(3);confirmReceiveFielInfo.setClients(chatBean.getClients());//文件发送者confirmReceiveFielInfo.setSelectedTo(chatBean.getSelectedTo());//文件接收者confirmReceiveFielInfo.setFileName(chatBean.getFileName());//文件名称
confirmReceiveFielInfo.setIp(chatBean.getIp());//接收方的ip地址
confirmReceiveFielInfo.setPort(chatBean.getPort());//接收方的端口
confirmReceiveFielInfo.setName(chatBean.getName());//接收方的客户名称confirmReceiveFielInfo.setCurrentTime(chatBean.getCurrentTime());//发送时间
//通知文件发送方,接收方以确定接收文件,可以开始发送文件请求
sendMessage(confirmReceiveFielInfo);
break;
4.接收方拒绝接收文件
ChatBeanserverRefuseBean=newChatBean();
serverRefuseBean.setType(4);
serverRefuseBean.setClients(chatBean.getClients());//文件发送方serverRefuseBean.setSelectedTo(chatBean.getSelectedTo());//文件接收方
serverRefuseBean.setFileName(chatBean.getFileName());
serverRefuseBean.setInfo(chatBean.getInfo());//拒接消息
serverRefuseBean.setName(chatBean.getName());//接收客户名称
serverRefuseBean.setName(chatBean.getCurrentTime());//拒接时间
sendMessage(serverRefuseBean);
break;
四、测试结果
发送信息测试:
发送文件测试:
五、实验总结
这次实验过程中,我更加熟悉了使用Socket网络通信编程,同时在实验中,首先我们应该设计好通过的协议,即通信遵循的方式,再动手实现通过过程。
在实现过程中碰到了找不到
服务器发送的输入流,及socket关闭异常,经过查找错误,发现了程序中存在了以下的问题,并设计了解决的方案:
“没有创建获取当前的用户的输出流,使用的是全局的输出流,此时将得到的数据发送出去后,客户机接受到输出流数据,并且正在将其中的数据读取出来,但是服务器关闭了该类,导致客户机找不到流报错。
同时当前的链接对象也被关闭,导致了所有的窗口都一起关闭了,所以这里应注意要多创建一个输出流,用于存放更新后的用户列表”。
同时在这次实验过程中,实验过程中通过查找api使用List列表中的getSelcetedValuesList()方法,但是编译环境中找不到,最后发现是编译环境使用的jre版本太老,该函数只有在jre7之后采用,因此编程过程中应将api与编译环境统一,以免发生错误。