网络编程——非阻塞模式(ioctlsocket)

来源:互联网 发布:空军飞行员知乎 编辑:程序博客网 时间:2024/05/22 08:11

非阻塞模式是通过反复调用I/O函数查看返回值的方式实现的。

1、API说明

(1) ioctlsocketSOCKET s, long cmd, u_long FAR* argp);

控制套接口的模式。

s:一个标识套接口的描述字。
cmd:对套接口s的操作命令。
argp:指向cmd命令所带参数的指针

2、代码

#include <stdio.h>#include <winsock2.h>  //winsock.h (2种套接字版本)#include <process.h>   //for beginthread#pragma comment(lib,"ws2_32.lib") //wsock32.lib #define MAX_LISTEN_NUM      2  //最多可同时监听的客户连接数#define CONNECT_PORT        1027#define CONNECT_IP          "127.0.0.1"#define DEFAULT_BUFF_SIZE   512unsigned int WINAPI ClientThread(void* lpParam){//当前线程中保存指定的客户端SocketSOCKET sClient = (SOCKET)lpParam;char    szBuff[DEFAULT_BUFF_SIZE] ={0};int     nRes;int     nLeft;  //剩余字节数,用于发送int     nIndex; //送信Buff下标int     errcode = 0;printf("******Start a Client: ThreadID=%d, Socket=%d******\n",::GetCurrentThreadId(),sClient);while(true){//printf("--- start recv message(ThreadID:%d)\n",::GetCurrentThreadId());memset(szBuff, 0, sizeof(szBuff) );nRes = recv(sClient, szBuff, sizeof(szBuff), 0);  //非阻塞模式,这里不会阻塞了if(nRes == SOCKET_ERROR )  //这里使用循环检查返回值的方式来判断,效率很低,应该改用完成通知方式最好!!{errcode=WSAGetLastError();   if(errcode == WSAEWOULDBLOCK) //这里表示没有数据。{  Sleep(100);  continue;  }  else {  if(errcode==WSAETIMEDOUT || errcode==WSAENETDOWN)  {  closesocket(sClient);  WSACleanup();  return -1;  }  }}//获取到了指定的字节数szBuff[nRes] = '\0';printf("-recved message(ThreadID:%d):(%d): %s\n",::GetCurrentThreadId(),nRes,szBuff);//告诉客户端收到了发送的数据printf("-send message:(%d): %s\n",nRes,szBuff);nLeft  = nRes; //指示收到了多少字节nIndex = 0;while( nLeft>0 ){nRes = send(sClient, &szBuff[nIndex], nLeft, 0);if(errcode == SOCKET_ERROR)  //错误处理  {  errcode=WSAGetLastError();  if(errcode == WSAEWOULDBLOCK)   {  Sleep(100);  continue;  }  else {  closesocket(sClient);  WSACleanup();  return -1;  }  } nLeft  -= nRes;nIndex += nRes;}//while(nLeft>0)}//while(true)printf("******Close a Client******\n");return 0;}int main(){WSAData     wsdata;SOCKET      sockServer;SOCKET      sockRecvClient;SOCKADDR_IN addrServer;SOCKADDR_IN addrRecvClient;int         addrlenClient;HANDLE      hThread;         //处理客户连接用的线程HandleUINT        nThreadID;int         nError       = -1;int         retVal;printf(">>>>>>>>server start Startup<<<<<<<<\n");//初始化一个服务进程nError = WSAStartup( MAKEWORD(2,2), &wsdata);if( nError != 0 ){printf("WSAStartup Failed: %d\n",nError);return -1;}//设定本地地址信息addrServer.sin_family = AF_INET;addrServer.sin_port   = htons(CONNECT_PORT);addrServer.sin_addr.S_un.S_addr = inet_addr(CONNECT_IP);printf("--create a socket--\n");//创建一个套接字//使用socket创建的socket是阻塞的sockServer = socket( AF_INET, SOCK_STREAM, 0 );if( sockServer == INVALID_SOCKET ){printf("socket Failed: %d\n",WSAGetLastError());WSACleanup();return -1;}<span style="color:#ff0000;">//设置为非阻塞模式  int imode=1;  retVal=ioctlsocket(sockServer,FIONBIO,(u_long *)&imode);  </span>if(retVal == SOCKET_ERROR)  {  printf("ioctlsocket failed!");  closesocket(sockServer);  WSACleanup();  return -1;  }printf("--  bind ---\n");//Socket的绑定nError = bind(sockServer, (const sockaddr *)(&addrServer), sizeof(addrServer) );if( nError != 0 ){printf("bind Failed: %d\n",WSAGetLastError());WSACleanup();return -1;}printf("-- begin to listen ---\n");//Socket的监听客户端连接nError = listen(sockServer, MAX_LISTEN_NUM );if( nError != 0 ){printf("listen Failed: %d\n",WSAGetLastError());WSACleanup();return -1;}   //开始接受用户的连接while(true){//printf("--server is waiting for a client 。。。\n"); addrlenClient   = sizeof( addrRecvClient );sockRecvClient  = accept( sockServer, (sockaddr *)(&addrRecvClient), &addrlenClient ); //这里不会阻塞了。。if( sockRecvClient == INVALID_SOCKET ){nError = WSAGetLastError();if (nError == WSAEWOULDBLOCK) //这里使用循环检查返回值的方式来判断,效率很低,应该改用完成通知方式最好!!{Sleep(100);  continue;}else{printf("accept failed!");  closesocket(sockServer); //连接失败,关闭服务器套接字并释放套接字库  WSACleanup(); //  return -1; }}//输出连接上的客户端IP:PORTprintf("--Accept client:%s:%d\n",inet_ntoa(addrRecvClient.sin_addr),ntohs(addrRecvClient.sin_port));//启动一个客户端处理线程printf("--Create a new thread: Socket=%d\n",sockRecvClient);hThread = (HANDLE)_beginthreadex(  NULL,0,ClientThread,(void*)sockRecvClient,0,&nThreadID );if( hThread == NULL ){printf("_beginthreadex Failed: %d\n",GetLastError());break;}printf("--A new thread is created: ID=%d Socket:%d\n", nThreadID, sockRecvClient);WaitForSingleObject(hThread, 0 );CloseHandle( hThread );}//while(true)printf("--  CloseSocket ---\n");//关闭Socket连接closesocket( sockServer );printf("-- WSACleanup ------\n");//清除Socket库WSACleanup();printf("-- server exit --\n");return 0;}

Client:还是采用阻塞模式

#include <stdio.h>#include <winsock2.h>  //winsock.h (2种套接字版本)#include <process.h>   //for beginthread#pragma comment(lib,"ws2_32.lib") //wsock32.lib #define DEFAULT_BUFF_SIZE   512#define CONNECT_PORT        1027#define CONNECT_IP          "127.0.0.1"int main(){printf(">>>>>>>>Start Client <<<<<<<<<\n");WSAData       wsData;SOCKET       sClient;     //SOCKADDR_IN  addrServer;  //用于连接服务器的Socket地址信息SOCKADDR_IN  addrLocal;   //本地使用的地址和端口int          addrLen;char       szBuff[DEFAULT_BUFF_SIZE] = {0};char       szBuffRecv[DEFAULT_BUFF_SIZE] = {0};int        nRes;int        nIndex; //带发送的Buff的下标int        nLen;   //数据int        nLeft;//获取要发送的信息strcpy( szBuff, "Message Message\0" );//初始化Socketprintf("---  Initlize WSAStartup --- \n");WSAStartup(MAKEWORD(2,2), &wsData);//创建一个客户端Socket//socket创建的是一个阻塞模式下的Socketprintf("---- Create a Socket --- \n");sClient = socket( AF_INET, SOCK_STREAM,0 );if( sClient == INVALID_SOCKET ){printf("socket Failed: %d\n", WSAGetLastError());WSACleanup();return -1;}//设定服务器端IP和端口信息用于连接addrServer.sin_family = AF_INET;addrServer.sin_port   = htons( CONNECT_PORT );addrServer.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP );//连接服务器printf("--- conect server :127.0.0.1:1027\n");nRes = connect( sClient, (const sockaddr *)&addrServer, sizeof(addrServer) );if( nRes == SOCKET_ERROR ){printf("connect Failed: %d\n",WSAGetLastError());WSACleanup();return -1;}//客户端连接成功printf("--- connect server success,Socket:%d\n",sClient);  while(true){memset(szBuff, 0, sizeof(szBuff));printf("Message YOU Want Send: \n");fflush(stdin);fgets( szBuff, sizeof(szBuff)-1, stdin );nLen         = strlen( szBuff );szBuff[nLen] = '\0'; //消除回车符--nLen;nIndex       = 0;nLeft        = nLen;while( nLeft > 0 ){nRes = send( sClient, &szBuff[nIndex], nLeft, 0 );printf("begin send\n");nRes = send( sClient, szBuff, 12, 0 );printf("begin success\n");if( nRes == 0 ){printf("-Send OK.\n");break;}else if( nRes == SOCKET_ERROR ){printf( "-send failed: %d\n",WSAGetLastError() );break;}printf("-Has Send %d Bytes( %d Bytes)\n", nRes, nLen );  nLeft  -= nRes;  nIndex += nRes;}//while(nLeft>0)memset(szBuffRecv, 0, sizeof(szBuffRecv));nRes = recv( sClient, szBuffRecv, sizeof(szBuffRecv), 0 );if( nRes == 0 ){printf("-Server Has Closed.\n");break;}else if( nRes == SOCKET_ERROR ){printf("-recv failed: %d\n", WSAGetLastError());break;}szBuffRecv[nRes]='\0';printf("-Recv [%d]Bytes: %s\n",nRes, szBuffRecv);}//while(true)printf("- closesocket\n");closesocket( sClient );printf("- WSACleanup\n");WSACleanup();return 0;}

3、问题

(1)阻塞模式和非阻塞模式区别:
阻塞模式和非阻塞模式的主要区别在于无请求来到时,阻塞模式会一直等在接收函数即accep函数,直到有请求到来才会继续向
下进行处理。而非阻塞模式下,运行接收函数,如果有请求,则会接收请求,如果无请求,会返回一个负值,并继续向下运行。一般来说,使用阻塞模式的程序比较多,因为阻塞模式是由内核保障等待请求的,当他阻塞时不占用系统资源,而非阻塞模式需要我们人工轮询,占用资源较多。另外,阻塞模式可以使用select函数设置超时时间。

注意;客户端一般不采用非阻塞模式。

原创粉丝点击