网络编程基础第五讲非阻塞模型
一丶简介
通过上一讲.我们了解到了阻塞模式. recv/send IO操作不完成.不会进行返回.迭代模式就是只服务一个连接.对这个连接进行读写.
非阻塞模式就是 IO没有完成.可以立即进行返回.
我们可以通过方法 ioctlsocket进行设置为非阻塞
例子:
int iMode = 1; //为1表示非阻塞. 为0 表示阻塞. int nRet = ioctlsocket(SOCKET, FIONBIO,(u_long *)&iMode);
示例代码:
#include <winsock2.h> #include <stdio.h> #pragma comment(lib, "Ws2_32.lib") void main() { //------------------------- // Initialize Winsock WSADATA wsaData; int iResult; u_long iMode = 0; iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != NO_ERROR) printf("Error at WSAStartup()\n"); //------------------------- // Create a SOCKET object. SOCKET m_socket; m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (m_socket == INVALID_SOCKET) { printf("Error at socket(): %ld\n", WSAGetLastError()); WSACleanup(); return; } //------------------------- // Set the socket I/O mode: In this case FIONBIO // enables or disables the blocking mode for the // socket based on the numerical value of iMode. // If iMode = 0, blocking is enabled; // If iMode != 0, non-blocking mode is enabled. iResult = ioctlsocket(m_socket, FIONBIO, &iMode); if (iResult != NO_ERROR) printf("ioctlsocket failed with error: %ld\n", iResult); }
二丶完整代码解析
非阻塞模式就是直接数据返回. 所以我们跟阻塞是的代码不一样的地方就是判断返回值. 因为返回值是立即返回.所以要判断数据是否接受到.连接是否接受到.这个是最大的区别.
// Server.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <WinSock2.h> #pragma comment(lib,"ws2_32.lib") int main() { WSADATA wsaData; int iResult; u_long iMode = 0; iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != NO_ERROR) printf("Error at WSAStartup()\n"); //------------------------- // Create a SOCKET object. SOCKET m_socket; m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (m_socket == INVALID_SOCKET) { printf("Error at socket(): %ld\n", WSAGetLastError()); WSACleanup(); return 0; } //设置套接字为非阻塞模式 iResult = ioctlsocket(m_socket, FIONBIO, &iMode); if (iResult != NO_ERROR) printf("ioctlsocket failed with error: %ld\n", iResult); //主要是进行连接的时候.代码不一样了.因为是非阻塞.所以返回的错误是资源暂时不可用. sockaddr_in hClientAddr; int nAddrSize = sizeof(hClientAddr); SOCKET hClientSocket; while (true) { hClientSocket = accept(m_socket,(sockaddr *) &hClientAddr, &nAddrSize); if (INVALID_SOCKET == hClientSocket) //因为是立即返回.所以错误会是INVALID_SOCKET 所以我们要进行错误码判断. { int iCode = GetLastError(); //使用那个都可以 int nCode = WSAGetLastError(); if (nCode == WSAEWOULDBLOCK) //资源暂时不可用.所以延迟继续进行连接 具体查询WSALastError()关于套接字的返回错误. { Sleep(100); continue; } else { printf("延迟连接出错\r\n"); closesocket(m_socket); WSACleanup(); break; //否则错误. } } } //recv一样会返回. char szRecvBuffer[0x1000] = { 0 }; while (true) { RtlZeroMemory(szRecvBuffer, sizeof(szRecvBuffer)); //清空缓冲区. if (SOCKET_ERROR == recv(hClientSocket,szRecvBuffer,0x1000,0))//非阻塞一样判断 { int iErrCode = GetLastError(); if (iErrCode == WSAEWOULDBLOCK) { /* 暂时无法接受.延迟继续接受. 注意这一次接受的数据我们需要保存起来.下次接受还要保存.最后判断数据是否完整. 非阻塞就是这样做的.所以具体写法自己实现 */ Sleep(100); continue; } else if (iErrCode == WSAETIMEDOUT || iErrCode == WSAENETDOWN) { //超时跟网络中断.打印输出并且释放资源 printf("网络中断.或者接受数据超时\r\n"); closesocket(hClientSocket); closesocket(m_socket); WSACleanup(); break; } } //成功接受.打印出来. szRecvBuffer[0x1000] = '\0'; //加个结尾. printf(szRecvBuffer); } return -1; }