if(FD_ISSET(s[i],&fdread))
MyRecv(s[i]);
}
7、使用WSAAsyncSelect异步I/O模型编写一个基于对话框的网络应用程序,请根据要求,完成以下题目:
1)当在套接字MySocket上有数据可读或者对方释放与MySocket的连接时,向对话框发送一条消息号为WM_MYSOCKMSG消息。
该对话框的句柄保存在变量hWnd中。
请写出能完成该功能的WSAAsyncSelect()函数调用语句。
答:
完成该功能的语句如下:
WSAAsyncSelect(MySocket,hWnd,WM_MYSOCKMSG,FD_READ|FD_CLOSE);
2)对话框通过调用消息处理函数voidMyMsgHandle(WPARAMwParam,LPARAMlParam)完成对WM_MYSOCKMSG消息的处理。
为实现该功能,如何对MFC生成的对话框应用程序进行修改?
答:
在对话框中添加自定义消息的方法为:
a)定义消息号:
可以使用宏定义语句“#define”增加一个用户自定义消息,该定义的作用范围应该包括整个类。
该消息号不能与现有消息的消息相同。
通常用户自定义的消息号从系统定义的常量WM_USER开始分配。
例如:
#defineWM_MYSOCKMSG(WM_USER+1)
b)增加消息处理函数:
在对话框中增加一个成员函数,用于对自定义消息进行处理。
比如:
voidMyMsgHandle(WPARAMwParam,LPARAMlParam);
c)在对话框类的头文件中,移动消息处理函数声明到类的AFX_MSG块,并在函数声明前面添加afx_msg。
比如:
//Generatedmessagemapfunctions
//{{AFX_MSG(CTestDlg)
…
afx_msgvoidMyMsgHandle(WPARAMwParam,LPARAMlParam);
…
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
d)在类的实现文件的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数。
比如:
BEGIN_MESSAGE_MAP(CTestDlg,CDialog)
//{{AFX_MSG_MAP(CTestDlg)
…
ON_MESSAGE(WM_MYMSG,MyMsgHandle)
…
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
e)在消息处理函数中添加代码,以实现对消息的处理。
传递给消息处理函数的参数wParam和lParam携带了与消息相关的更详细的信息。
3)若接收到对方发送的消息,将消息直接输出;若是对方释放连接,则显示“对方掉线”,然后关闭套接字。
请给出实现该功能的消息处理函数voidMyMsgHandle(WPARAMwParam,LPARAMlParam)。
答:
相应的关键代码如下:
switch(WSAGETSELECTEVENT(lParam))
{
caseFD_READ:
…//接收并显示消息
break;
caseFD_CLOSE:
…//输出对方掉线
closesocket(wParam);
break;
default:
…
}
8、简述WSAEventSelect事件选择模型的编程模型。
答:
WSAEventSelect事件选择模型的编程步骤如下:
(1)创建事件对象句柄
通过调用WSACreateEvent函数来创建一个对象,该函数的返回值为事件句柄对象句柄。
函数原型如下:
WSAEVENTWSACreateEvent(void);
比如:
WSAEVENTnewEventWSACreateEvent(void);
(2)关联套接字和事件对象,注册关心的网络事件
通过调用WSAEventSelect函数,把事件对象句柄与套接字关联在一起,同时注册感兴趣的网络事件类型。
WSAEventSelect函数的原型为:
intWSAEventSelect(SOCKETs,WSAEVENThEventObject,longlNetworkEvents);
比如当在套接字s上发生有数据可读或者对方关闭套接字时,向事件对象newEvent传信,则相应的代码为:
WSAEventSelect(s,newEvent,FD_READ|FD_CLOSE);
(3)等待网络事件触发事件对象句柄的工作状态
调用WSAWaitForMultipleEvents函数,等待网络事件触发事件对象句柄的工作状态。
函数原型如下:
DWORDWSAWaitForMultipleEvents(DWORDcEvents,
constWSAEVENTFAR*lphEvents,
BOOLfWaitAll,
DWORDdwTimeout,
BOOLfAlertable
);
比如:
Index=WSAWaitForMultipleEvent(1,&newEvent,FALSE,WSA_INFINITE,FALSE);
(4)检查套接字上所发生的网络事件类型
调用WSAEnumNetworkEvents函数,检查出套接字上发生的网络事件类型。
函数原型定义如下:
intWSAEnumNetworkEvents(
SOCKETs,
WSAEVENThEventObject,
LPWSANETWORKEVENTSlpNetworkEvents
);
比如:
WSANETWORKEVENTSNetworkEvent;
WSAEnumNetworkEvents(s,newEvent,&NetworkEvent);
(5)处理网络事件
确定在套接字上发生的网络事件类型,然后根据不同的情况做出相应的处理。
应用程序完成了对一个事件对象的处理后,便应调用WSACloseEvent函数,释放由事件句柄使用的系统资源。
比如:
if(NetworkEvent.lNetworkEvent&FD_READ)
{
if(NetworkEvent.lErrorCode[FD_READ_BIT]==0)
{
…//接收数据
}else{
…//出错处理
}
}
if(NetworkEvent.lNetworkEvent&FD_CLOSE)
{
if(NetworkEvent.lErrorCode[FD_CLOSE_BIT]==0)
{
…//关闭连接处理
}else{
…//出错处理
}
}
WSACloseEvent(newEvent);
9、编写一个基于数据报套接字的点到点通信程序,具体要求如下:
1)基于数据报套接字来实现。
2)服务器方工作的端口号为2000。
3)客户方通过键盘接收服务器方的IP地址和要发送给服务器方的消息,然后把该消息发送到服务器方。
4)服务器方接收客户方的消息并显示,然后产生一条消息并发送给客户方,该消息的内容是客户方的IP地址、端口号。
5)客户方接收服务器方响应的消息并显示
6)双方释放套接字。
答:
服务器端代码如下:
#include"stdafx.h"
#include"winsock2.h"
#pragmacomment(lib,"ws2_32.lib")
intmain(intargc,char*argv[])
{
SOCKETsock;
SOCKADDR_INSerAddr,CliAddr;
intalen=sizeof(SOCKADDR_IN);
intmsglen;
charbuf[1000];
WORDwVerReq;
WSADATAwsaData;
wVerReq=MAKEWORD(1,1);
if(WSAStartup(wVerReq,&wsaData))
return1;
memset(&SerAddr,0,alen);
SerAddr.sin_family=AF_INET;
SerAddr.sin_port=htons(2000);
SerAddr.sin_addr.s_addr=htonl(INADDR_ANY);
sock=socket(PF_INET,SOCK_DGRAM,0);
bind(sock,(LPSOCKADDR)&SerAddr,alen);
msglen=recvfrom(sock,buf,1000,0,(LPSOCKADDR)&CliAddr,&alen);
buf[msglen]='\0';
printf("%s\n",buf);
sprintf(buf,"%s:
%d",inet_ntoa(CliAddr.sin_addr),ntohs(CliAddr.sin_port));
sendto(sock,buf,strlen(buf),0,(LPSOCKADDR)&CliAddr,alen);
closesocket(sock);
WSACleanup();
return0;
}
客户端代码如下:
#include"stdafx.h"
#include"winsock2.h"
#pragmacomment(lib,"ws2_32.lib")
intmain(intargc,char*argv[])
{
SOCKETsock;
SOCKADDR_INSerAddr;
intalen=sizeof(SOCKADDR_IN);
intmsglen;
charbuf[1000];
WORDwVerReq;
WSADATAwsaData;
wVerReq=MAKEWORD(1,1);
if(WSAStartup(wVerReq,&wsaData))
return1;
printf("请输入服务器的IP地址:
");
scanf("%s",buf);
memset(&SerAddr,0,alen);
SerAddr.sin_family=AF_INET;
SerAddr.sin_port=htons(2000);
SerAddr.sin_addr.s_addr=inet_addr(buf);
printf("请输入要发送到服务器的消息:
");
scanf("%s",buf);
sock=socket(PF_INET,SOCK_DGRAM,0);
sendto(sock,buf,strlen(buf),0,(LPSOCKADDR)&SerAddr,alen);
while((msglen=recvfrom(sock,buf,1000,0,(LPSOCKADDR)&SerAddr,&alen))<=0);
buf[msglen]='\0';
printf("%s\n",buf);
closesocket(sock);
WSACleanup();
return0;
}
10、设计一个工作在Windows环境下、基于流式套接字的网络客户端应用程序,要求如下:
1)基于流式套接字。
2)服务器端使用的IP地址为:
10.132.254.200,端口号是:
2000。
3)客户端连接到服务器端后,把保存在变量msg1中的以’\0’结尾的字符串发送到服务器。
4)服务器端使用printf()函数显示接收到的字符串,然后再把该字符串的长度以长整型数据的形式发送给客户端。
5)客户端接收到服务器返回的字符串长度,然后使用printf()函数以“服务器返回的数据是XXXX”形式输出服务器的返回结果。
其中XXXX是服务器返回的数据。
6)最后服务器和客户均释放连接。
给出能够完成以上功能的服务器端和客户端程序的关键代码。
答:
(1)服务器端关键代码如下:
SOCKETsockListen,sockRecv;
SOCKADDR_INSerAddr,CliAddr;
WORDwVerReq;
WSADATAwsaData;
charBuf[1000];
unsignedlongintmsglen;
intalen=sizeof(SOCKADDR);
wVerReq=MAKEWORD(1,1);
if(WSAStartup(wVerReq,&wsaData))
return;
memset(&SerAddr,0,alen);
SerAddr.sin_family=AF_INET;
SerAddr.sin_port=htons(2000);
SerAddr.sin_addr.s_addr=htonl(INADDR_ANY);
sockListen=socket(PF_INET,SOCK_STREAM,0);
bind(sockListen,(LPSOCKADDR)&SerAddr,alen);
listen(sockListen,5);
alen=sizeof(SOCKADDR);
sockRecv=accept(sockListen,(LPSOCKADDR)&CliAddr,&alen);
msglen=recv(sockRecv,Buf,1000,0);
Buf[msglen]=‘\0’;
printf("%s\n",Buf);
msglen=htonl(msglen);
send(sockRecv,(char*)&msglen,sizeof(msglen),0);
closesocket(sockRecv);
closesocket(sockListen);
WSACleanup();
(2)客户端关键代码如下:
7、你喜欢哪一类型的DIY手工艺制品?
SOCKETsock;
SOCKADDR_INSerAddr;
WORDwVerReq;
为了解目前大学生对DI