UDP客户服务器应用程序设计.docx
《UDP客户服务器应用程序设计.docx》由会员分享,可在线阅读,更多相关《UDP客户服务器应用程序设计.docx(13页珍藏版)》请在冰豆网上搜索。
UDP客户服务器应用程序设计
UDP客户/服务器应用程序设计
姓名:
学号:
班级:
目录
一.实验名称3
二.实验要求3
三.程序实现流程图3
四.编程使用的主要函数3
五.主要程序段及其功能5
六.程序测试:
9
1.正常绑定,正常通话:
9
单播:
<默认方式>9
广播:
11
2.异常绑定,异常通话:
12
3.程序特色:
12
七.实验收获13
一.实验名称
UDP客户/服务器应用程序设计
二.实验要求
编程实现一个聊天室系统。
该系统包括客户端和服务器端两部分。
用户通过客户端发送消息。
服务器端在收到消息后,显示在主界面上。
要求还能实现广播功能。
实验报告要求有实现过程的流程图,对主要的函数及其参数给予说明,要有实现过程的主要程序段,并对各段程序的功能及作用进行说明。
三.程序实现流程图
四.编程使用的主要函数
1.intWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);
初始化套接字库函数。
使用Socket之前必须调用此函数,当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中,以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。
参数wVersionRequested表示当前套接字库的版本号,参数lpWSAData是指向结构体WSADATA的指针变量,表示获取套接字库的详细信息,函数调用成功返回0。
2.SOCKETsocket(intaf,inttype,intprotocol)
创建网络通信套接字句柄。
参数af指定套接字所使用的地址格式,对于TCP/IP协议族,该参数置PF_INET;type是表示套接字的类型,采用流套接字类型用SOCK_STREAM,数据报套接字类型使用SOCK_DGRAM;protocol表示应用程序使用的通讯协议,一般写0表示对两种类型的Socket分别采用默认的TCP或UDP传输协议,函数调用成功返回新建套接字的句柄,否则返回INVALID_SOCKET。
3.intbind(SOCKETs,structsockaddr_in*name,intnamelen)
绑定地址信息.对服务器而言套接字创建成功后,应将套接字与地址结构信息进行绑定,第一个参数s为套接字句柄,第二个参数地质结构信息,第三个参数地质结构的大小。
调用成功返回0,失败返回SOCKET_ERROR。
4.intsetsockopt(intsockfd,intlevel,intoptname,constvoid*optval,socklen_toptlen)
套接字设置选项,可以设置广播发送方式。
第一个参数是套接字描述符,第二个参数是选项定义的层次,目前仅支持SOL_SOCKET和IPPORT_TCP层次,第三个参数设置为SO_BROADCAST表示允许套接字传送广播信息,第四个参数是指针,指向存放选项值的缓冲区,最后一个参数表示此缓冲区的长度。
设置成功返回0,失败返回SOCKT_ERROR。
5.intsendto(SOCKETs,constcharFAR*buf,intlen,intflags,conststructsockaddrFAR*to,inttolen)
实现服务器和客户端的数据发送。
参数s是服务器或者是客户端套接字,buf应用程序发送的数据缓冲区,len实际发送数据长度,flags一般为0,表示表示只描述同步Socket的sendto函数的执行流程。
后两个参数表示目标地址结构信息和目标地址结构大小。
6.intrecvfrom(SOCKETs,constcharFAR*buf,intlen,intflags,conststructsockaddrFAR*to,inttolen)
实现服务器和客户端的数据接收。
参数s是服务器或者是客户端套接字,buf应用程序接收数据的缓冲区,len指定数据缓冲区长度,flags一般为0,表示表示只描述同步Socket的recvfrom()函数的执行流程。
后两个参数表示目标地址结构信息和目标地址结构大小。
7.intclosesocket(SOCKETs)
关闭套接字函数。
S表示即将关闭的套接字句柄。
五.主要程序段及其功能
备注:
由于客户端和服务器大部分代码都相同,这里主要以服务器代码为例!
//服务器地址信息的绑定:
当点击确定按钮时,此函数开始绑定地址信息(绑定自身地址信息,等待接收对方的信息;同时将对方的地址信息绑定到地址结构信息中,用于发送信息)
voidCTCPDlg:
:
OnBind()
{CStringstr;
GetDlgItem(IDC_PORT)->GetWindowText(str);
if(str=="")
{
MessageBox("端口号不能为空!
");
}
else
{//绑定本机地址信息
port=atoi(str.GetBuffer(0));
addr.sin_family=AF_INET;
addr.sin_addr.S_un.S_addr=INADDR_ANY;
addr.sin_port=htons(port);
if(bind(s,(sockaddr*)&addr,sizeof(addr))==SOCKET_ERROR)
{
MessageBox("绑定失败!
");
return;
}
//设置套接字为广播发送if(setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char*)&fBroadcast,sizeof(BOOL))==SOCKET_ERROR)
{
MessageBox("设置广播套接字失败!
");
}
//设置广播地址信息
addr2.sin_family=AF_INET;
addr2.sin_addr.S_un.S_addr=INADDR_BROADCAST;
addr2.sin_port=htons(9999);
GetDlgItem(IDC_TEXT)->SetWindowText("地址信息绑定成功,请选择发送方式!
\r\n");
if(:
:
WSAAsyncSelect(s,this->m_hWnd,WM_SOCKET,FD_READ)>0)
MessageBox("选择失败!
");
GetDlgItem(IDC_SENDTEXT)->EnableWindow(true);
GetDlgItem(IDC_SEND)->EnableWindow(true);
GetDlgItem(IDC_ADDR)->EnableWindow(false);
GetDlgItem(IDC_PORT)->EnableWindow(false);
}
}
//服务器端数据的发送:
由于UDP是面向无连接的,所以发送函数只需指定要发送的地址信息即可。
voidCTCPDlg:
:
OnSend()
{
CStringstr1,time1;
CEdit*pEt=(CEdit*)GetDlgItem(IDC_TEXT);
GetDlgItem(IDC_SENDTEXT)->GetWindowText(str1);
char*p=str1.GetBuffer(str1.GetLength()+1);
if(str1=="")
{
MessageBox("消息不能为空!
");
}
else
{if(radio==2){
if(:
:
sendto(s,p,str1.GetLength(),0,(sockaddr*)&addr1,sizeof(addr1))==SOCKET_ERROR)
{
MessageBox("消息发送失败!
");
return;
}
}
if(radio==1)
{
if(:
:
sendto(s,p,str1.GetLength(),0,(sockaddr*)&addr2,sizeof(addr2))==SOCKET_ERROR)
{
MessageBox("广播信息发送失败!
");
return;
}
}
intnLen=pEt->SendMessage(WM_GETTEXTLENGTH);
pEt->SetSel(nLen,nLen);
time2=CTime:
:
GetCurrentTime();
time1=time2.Format("%H:
%M:
%S");
time1+="\r\n";
if(radio==1)
pEt->ReplaceSel("\r\n广播->我"+time1+str1);
if(radio==2)
pEt->ReplaceSel("\r\n单播->我"+time1+str1);
GetDlgItem(IDC_SENDTEXT)->SetWindowText(NULL);
}
}//服务器端信息的接收:
当有接收网络事件时,无论是客户端还是服务器都是使用相同的函数进行接收,只需指定套接字描述符和接受新存放的位置,以及存放对方IP地址信息的位置即可!
LRESULTCTCPDlg:
:
OnSocket(WPARAMwParam,LPARAMlParam)
{charcs[1024]={0};
CStringtime1;
intlen=sizeof(addr2);
CEdit*pEt=(CEdit*)GetDlgItem(IDC_TEXT);
if(lParam==FD_READ)
{
CStringnum="";
intcou=:
:
recvfrom(s,cs,1024,0,(sockaddr*)&addr2,&len);
:
:
WSAAsyncSelect(s,this->m_hWnd,WM_SOCKET,FD_READ);
cs[cou]=NULL;
num+=(LPSTR)cs;
time2=CTime:
:
GetCurrentTime();
time1=time2.Format("%H:
%M:
%S");
intnLen=pEt->SendMessage(WM_GETTEXTLENGTH);
pEt->SetSel(nLen,nLen);
pEt->ReplaceSel("\r\n他"+time1+"\r\n"+num);
}
return0;
}
//定时器的实现:
定时器的实现是通过调用系统消息函数OnTimer()实现,函数设置为SetTimer(1,1000,NULL),1为定时器的名字,第二参数表示每一秒钟响应一次,最后一个参数表示调用系统默认函数OnTiemer()(言外之意可以调用任意自己重写的函数),定时器的使用以及用变量的控制实现了间隔一段时间显示不同的字体不同的背景颜色.
voidCTCPDlg:
:
OnTimer(UINTnIDEvent)
{
defaulttime2=CTime:
:
GetCurrentTime();
CStringtime1;
if(k==0)
{
statu=:
:
CreateStatusWindow(WS_CHILD|WS_VISIBLE,"-------------欢迎使用TCP聊天工具--作者:
201008202106谢明哲!
----------",this->m_hWnd,IDC_ADDR);
:
:
SendMessage(statu,SB_SETBKCOLOR,0,(long)RGB(255,204,255));
k++;
}
if(k==5)
{
statu=:
:
CreateStatusWindow(WS_CHILD|WS_VISIBLE,"----------------计算机网络编程作业-------------",this->m_hWnd,IDC_ADDR);
:
:
SendMessage(statu,SB_SETBKCOLOR,0,(long)RGB(255,255,153));
k=-4;
}
else
k++;
time2=CTime:
:
GetCurrentTime();
time1=time2.Format("%H:
%M:
%S");
this->SetWindowText("当前时间:
"+time1);
CDialog:
:
OnTimer(nIDEvent);
}
六.程序测试:
1.正常绑定,正常通话:
单播:
<默认方式>
广播:
2.异常绑定,异常通话:
3.程序特色:
定时器的使用:
顶端时间的显示,最低端信息背景颜色的变化!
编辑控件类CEdit的使用:
由于使用函数GetDlgItem(IDC_TEXT)->GetWindowText(str);GetDlgItem(IDC_TEXT)->SetWindowText(str)来进行文本框的更新显示很不方便,这是采用全部读取信息框内容,然后加上当前聊天信息全部更新到信息框,带来两个问题:
1.浪费,为了更新一句却要全部记录所有信息.2.当信息框满后,在发信息不能定位到最后一行,需要用滚动条很不方便!
但是使用CEdit类后,这些问题都可以迎刃而解。
其次就是广播的实现!
以广播的方式发送时,局域网内的其它客户端都可以接受到广播信息!
七.实验收获
虽然基于UDP的应用程序设计,可以在TCP通信的基础上修改的到,但是由于两种通信一种是面向连接的,一种是面向无连接的;一种是面向字节流,一种是面向数据报。
所以原理差别还是非常之大,需要深刻领会其中的区别,才能清晰而熟练地实现基于UDP的应用程序的通信。
通过本次应用程序的设计,加深了对计算机网络中基于UDP通信知识的巩固,而且做程序设计的过程中,也有了自己独特的认识,理解式记忆印象更深刻!