非阻塞I/O模型的测试
- 客户端代码:
#include<WinSock2.h>
#include<WS2tcpip.h>
#include<stdlib.h>
#include<stdio.h>
#include<iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define DEF_PORT 27015
#define DEF_SIZE 512
int main()
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
int iResult;
//初始化Winsock
iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
if(iResult != 0){
cout<<"WSAStartup failed with error: "<<iResult<<endl;
return 1;
}
//创建用于连接的socket
ConnectSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(ConnectSocket == INVALID_SOCKET){
cout<<"socket created with error: "<<GetLastError()<<endl;
WSACleanup();
return 1;
}
//连接
sockaddr_in addserver;
addserver.sin_family = AF_INET;
addserver.sin_port = htons(DEF_PORT);
addserver.sin_addr.S_un.S_addr = inet_addr("10.50.92.211");
//连接
iResult = connect(ConnectSocket,(const sockaddr*)&addserver,sizeof(addserver));
if(iResult != 0){
printf("connect failed with error :%d\n",GetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
char sendbuf[DEF_SIZE];
printf("conntect successful...\n");
printf("Please input sendbuf:");
cin>>sendbuf;
//发送数据
iResult = send(ConnectSocket,sendbuf,sizeof(sendbuf),0);
if(iResult > 0){
printf("host send: %s\n",sendbuf);
}
closesocket(ConnectSocket);
WSACleanup();
system("pause");
return 0;
}
- 非阻塞I/O模型服务器代码:
#include<WinSock2.h>
#include<WS2tcpip.h>
#include<stdlib.h>
#include<stdio.h>
#include<iostream>
using namespace std;
#pragma comment (lib,"ws2_32.lib")
#define DEF_PORT 27015
#define DEF_SIZE 512
int main()
{
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET AcceptSocket = INVALID_SOCKET;
//1.初始化
iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
if(iResult != 0)
{
printf("WSAStartup failed with error :%d\n",iResult);
return 1;
}
//2.创建用于监听的套接字
ListenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(ListenSocket == INVALID_SOCKET){
printf("create ListenSocket failed with error:%d\n",GetLastError());
WSACleanup();
return 1;
}
//3.为监听套接字绑定地址和端口号
sockaddr_in addServer;
addServer.sin_family = AF_INET;
addServer.sin_port = htons(DEF_PORT);
addServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
iResult = bind(ListenSocket,(const sockaddr*)&addServer,sizeof(addServer));
if(iResult == SOCKET_ERROR){
printf("bind failed with error:%d",GetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
//4.设置套接字为非阻塞模式
int iMode = 1;
iResult = ioctlsocket(ListenSocket,FIONBIO,(u_long*)&iMode);
if(iResult == SOCKET_ERROR)
{
printf("ioctlsocket failed with error: %d\n",GetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
/*5.监听客户端的连接
如果设置为SOMAXCONN,则负责套接字的底层服务提供程序会将backlog设置为最大合理值。*/
iResult = listen(ListenSocket,SOMAXCONN);
if(iResult == SOCKET_ERROR){
printf("Listen failed with error:%d\n",GetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
printf("TCP Server is starting...\n");
sockaddr_in addClient;
int addClient_Size = sizeof(addClient);
char recvbuf[DEF_SIZE] = {0};
int recvlen = sizeof(recvbuf);
//循环等待客户请求建立连接,并处理连接请求
while(1){
//因为是非阻塞模式,必然会返回
AcceptSocket = accept(ListenSocket,(sockaddr*)&addClient,&addClient_Size);
if(AcceptSocket == INVALID_SOCKET){
iResult = WSAGetLastError();
if(iResult == WSAEWOULDBLOCK)
{
Sleep(1000); //1000ms
printf("等待连接中...........\n");
continue;
}
else {
printf("accpet failed!\n");
closesocket(ListenSocket);
WSACleanup();
return 1;
}
}
printf("收到新的连接:%s\n",inet_ntoa(addClient.sin_addr));
//循环接收数据
while(1){
memset(recvbuf,0,sizeof(recvbuf));
iResult = recv(AcceptSocket,recvbuf,recvlen,0);
if(iResult > 0){
printf("数据内容: %s\n",recvbuf);
//跳出轮询接收
continue;
}
else if(iResult == 0){ //返回值0代表正常关闭
printf("客户端已经正常关闭....\n");
closesocket(AcceptSocket);
break;
}
else{ //接收出现错误
iResult = WSAGetLastError();
if(iResult == WSAEWOULDBLOCK)
{
//无法立即完成非阻塞socket上的操作
Sleep(1000); //1000ms
printf("当前I/O不满足,等待毫秒轮询\n");
continue;
}
else {
printf("recv failed with error: %d\n",GetLastError());
closesocket(AcceptSocket);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
}
}
}
closesocket(AcceptSocket);
closesocket(ListenSocket);
WSACleanup();
system("pause");
return 0;
}
- 测试结果
- 模型评价
非阻塞I/O模型使用简单的方法来避免套接字在某个I/O操作上阻塞等待,应用进程不再睡眠等待,而是可以在等待I/O条件满足的这段时间内做其他的事情。在函数轮询的间隙可以对其他套接字的I/O操作进行类似的尝试,对于多个套接字的网络I/O可以避免串行等待带来的效率低下问题。
缺点:由于应用程序不断尝试接口函数的调用,直到成功完成指定的操作,cpu的利用效率很低。此外如果设置了较长的延迟实际按,则最后一次成功的I/O操作对于I/O事件发生来讲有滞后事件,所以不适用于对实时性要求高的应用。