基本概念:
5种编程模型:
select模型
主动获取socket状态
WSAAsyncSelect模型
以消息通知socket状态,被动接受消息(socket状态)
WSAEventSelect模型
以事件通知socket状态,被动接受事件(socket状态)
重叠I/O模型
使用重叠的数据结构一次投递多个I/O请求
系统通过事件通知和完成例程通知应用程序
完成端口模型
基于线程池
非阻塞的socket
int ioctlsocket(
_In_ SOCKET s,
_In_ long cmd, //FIONBIO
_Inout_ u_long *argp//0为阻塞,1为非阻塞
);
当socket被设置为FIONBIO =1 时,与它关联的accpt, recv,send, connect 函数都不会阻塞,将会执行这些函数很快就会返回继续执行
所以需要解决这些问题使得成功执行这些函数
测试代码:
server.cpp
#define WIN32_LEAN_AND_MEAN #include<iostream> #include<string> #include<winsock2.h> using namespace std; #pragma comment(lib,"ws2_32.lib") int main() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData)) { cout << "版本错误, error code is " <<WSAGetLastError()<< endl; return 1; } ULONG noblock = 1; //非阻塞参数 SOCKET server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SOCKET client_socket; SOCKADDR_IN bind_addr; SOCKADDR_IN client_addr; char recv_buffer[1024]; char send_buffer[1024]; int error_code; //存储WSAGetLastError()的返回值 int len = sizeof(SOCKADDR); //设置非阻塞socket if (!ioctlsocket(server_socket, FIONBIO, &noblock)) { cout << "设置非阻塞模式成功" << endl; } bind_addr.sin_addr.S_un.S_addr = htonl(ADDR_ANY); bind_addr.sin_family = AF_INET; bind_addr.sin_port = htons(8889); //绑定ip和端口 if (bind(server_socket, (sockaddr*)&bind_addr, len) == SOCKET_ERROR) { cout << "bind error code is " << WSAGetLastError() << endl; WSACleanup(); return -1; } //开始监听 if (listen(server_socket, 5) == SOCKET_ERROR) { cout << "listen error code is " << WSAGetLastError() << endl; WSACleanup(); return -2; } //解决非阻塞的accpet while (1) { //非阻塞接受客户端连接,当没有客户端连接时当然返回INVALID_SOCKET,但是 //错误码可以辨识是没有客户端连接还是有客户端连接却连接时失败. //也就是错误码可以让我们知道错误原因是什么. 如果错误码是WSAEWOULDBLOCK //则没有客户端连接,sleep一下后继续接受一下.如果是有客户端连接却失败了 //需要打印出错误原因 //如果接受成功则可以开始于客户端通信 client_socket = accept(server_socket, (sockaddr*)&client_addr, &len); if (client_socket == INVALID_SOCKET) { error_code = WSAGetLastError(); if (error_code == WSAEWOULDBLOCK) { Sleep(100); continue; } else { cout << "accept失败,错误码" << error_code << endl; closesocket(server_socket); WSACleanup(); return -3; } } break; } //解决非阻塞的recv和send while (1) { memset(recv_buffer, '\0', 1024); //对于该函数,由于是非阻塞模式,没有数据不会阻塞,错误处理和上面类似 if (recv(client_socket, recv_buffer, 1024, 0) == SOCKET_ERROR) { error_code = WSAGetLastError(); if (error_code == WSAEWOULDBLOCK) { Sleep(100); continue; } else { cout << "recv error code is " << error_code << endl; closesocket(client_socket); closesocket(server_socket); WSACleanup(); return -4; } } if (strcmp(recv_buffer, "quit") == 0) { cout << "客户端退出" << endl; closesocket(client_socket); closesocket(server_socket); WSACleanup(); return 1; } else { while (1) { cout << "recvice :" << recv_buffer << endl; cin >> send_buffer; if (send(client_socket, send_buffer, strlen(send_buffer) + 1, 0)==SOCKET_ERROR) { error_code = WSAGetLastError(); if (error_code == WSAEWOULDBLOCK) { Sleep(200); continue; } else { cout << "send error code is " << error_code << endl; closesocket(client_socket); closesocket(server_socket); WSACleanup(); return -5; } } break; } } } return 0; }
client.cpp
#define _WINSOCK_DEPRECATED_NO_WARNINGS #include<winsock2.h> #include<windows.h> #include<iostream> #include<string> using namespace std; #pragma comment(lib,"ws2_32.lib") int main() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData)) { cout << "版本错误, error code is " << WSAGetLastError() << endl; return 1; } ULONG noblock = 1; SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SOCKADDR_IN server_addr; char recv_buffer[1024]; char send_buffer[1024]; int error_code; int len = sizeof(SOCKADDR); int isQuit=0; if (!ioctlsocket(client_socket, FIONBIO, &noblock)) { cout << "设置非阻塞模式成功" << endl; } server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8889); while (1) { if (connect(client_socket, (const sockaddr*)&server_addr, len)==SOCKET_ERROR) { error_code = WSAGetLastError(); if (error_code == WSAEWOULDBLOCK || error_code ==WSAEINVAL) { continue; } //当连接一次后需要判断是否已经连接.不然会报10056错误 else if (error_code == WSAEISCONN) { break; } else { cout << "connect error code is " << error_code << endl; closesocket(client_socket); WSACleanup(); return -1; } } break; } //发送和接受循环 while (1) { memset(send_buffer, 0, 1024); cout << "输入内容:" << endl; cin >> send_buffer; if (strcmp(send_buffer, "quit")==0) { cout << "退出!" << endl; isQuit = 1; } //这个循环是发送一次的 while (1) { if (send(client_socket, send_buffer,1024, 0) == SOCKET_ERROR) { error_code = WSAGetLastError(); if (error_code == WSAEWOULDBLOCK) { Sleep(50); continue; } else { cout << "send error code is " << error_code << endl; closesocket(client_socket); WSACleanup(); return -2; } } else { //发送成功,如果发送的是quit则退出否则继续运行 if (isQuit == 1) { closesocket(client_socket); WSACleanup(); return 1; } else { break; } } } //这个循环是接受一次的 while (1) { memset(recv_buffer, 0, 1024); if (recv(client_socket, recv_buffer, 1024, 0) == SOCKET_ERROR) { error_code = WSAGetLastError(); if (error_code == WSAEWOULDBLOCK) { Sleep(100); continue; } else { cout << "recv error code is " << error_code << endl; closesocket(client_socket); WSACleanup(); return -3; } } else { cout << "recvice :" << recv_buffer << endl; break; } } } return 1; }