在阻塞模式下,在IO操作完成前,执行的操作函数将一直等候而不会立刻返回,该函数所在的进程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管IO是否完成,该函数所在的线程将继续运行。阻塞模式编程简单,但效率低;非阻塞模式编程复杂,但效率高,比如:
举个简单的例子,你点击一个发送按钮:
如果是阻塞式的,那么在发送未完成之前,界面就会"卡死"。
而非阻塞式的,无论数据有没有完成,你都继续进行界面的操作
server:
// Server.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <iostream> #include <WINSOCK2.H> #pragma comment(lib, "wsock32.lib") using namespace std; #define SERVER_EXIT_OK 0 //服务器正常退出 #define SERVER_DLL_REEOR 1 //调用Windows sockets DLL失败 #define SERVER_API_ERROR 2 //调用Windows sockets API失败 #define SERVERPORT 5555//服务器TCP端口 #define MAX_NUM_BUF 64 //缓冲区最大尺寸 //变量 char bufRecv[MAX_NUM_BUF]; //读缓冲区 char bufSend[MAX_NUM_BUF]; //写缓冲区 SOCKET sServer; //服务器监听套接字 SOCKET sClient; //接受客户端套接字 BOOL bConning; //与客户端的连接状态 //函数 void InitMember(void); //初始化成员变量 int ExitClient(int nExit); //客户端退出 BOOL RecvLine(SOCKET s, char* buf); //读取一行数据 BOOL SendLine(SOCKET s, char* buf); //发送一行数据 int HandleSocketError(char *str); //对Windows sockets API调用错误处理 void ShowSocketMsg(char* str); //显示错误信息 //主函数 int main(int argc, char* argv[]) { InitMember(); //初始化变量 WORD wVersionRequested; //应用程序需要Windows sockets DLL的版本 WSADATA wsaData; //Windows sockets DLL版本信息 int retVal; //调用Windows sockets API返回值 //初始化Windows Sockets DLL wVersionRequested = MAKEWORD(1,1); retVal = WSAStartup(wVersionRequested, &wsaData); if ( 0 != retVal ) { ShowSocketMsg("Can not find a usable Windows Sockets dll!"); return SERVER_DLL_REEOR; } //确保WinSock DLL支持1.1 if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1) { ShowSocketMsg("Can not find a usable Windows Sockets dll!"); WSACleanup( ); return SERVER_DLL_REEOR; } //创建套接字 sServer= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET == sServer) { return HandleSocketError("Failed socket()!"); } //服务器套接字地址 SOCKADDR_IN addrServ; addrServ.sin_family = AF_INET; addrServ.sin_port = htons(SERVERPORT); addrServ.sin_addr.s_addr = INADDR_ANY; //绑定套接字 retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN)); if(SOCKET_ERROR == retVal) { closesocket(sServer); //关闭套接字 return HandleSocketError("Failed bind()!"); //错误处理 } //开始监听 retVal = listen(sServer, 1); if(SOCKET_ERROR == retVal) { closesocket(sServer); //关闭套接字 return HandleSocketError("Failed listen()!");//错误处理 } //等待客户端的连接 cout << "Server succeeded!" << endl; cout << "Waiting for new clients..." << endl; //接受客户端请求 sockaddr_in addrClient; int addrClientlen = sizeof(addrClient); sClient = accept(sServer,(sockaddr FAR*)&addrClient, &addrClientlen); if(INVALID_SOCKET == sClient) { closesocket(sServer); //关闭套接字 return HandleSocketError("Failed accept()!"); //错误处理 }else{ bConning = TRUE; //客户端请求成功 } //显示客户端的IP和端口 char *pClientIP = inet_ntoa(addrClient.sin_addr); u_short clientPort = ntohs(addrClient.sin_port); cout<<"Accept a client."<<endl; cout<<"IP: "<<pClientIP<<endl; cout<<"Port: "<<clientPort<<endl; //接收客户端数据 if (!RecvLine(sClient, bufRecv)) { return ExitClient(SERVER_API_ERROR);//退出 } //显示客户端数据 cout << bufRecv<<endl; //向客户端发送数据 strcpy(bufSend, "Hello,Client!\n"); if (!SendLine(sClient, bufSend)) { return ExitClient(SERVER_API_ERROR); } //显示退出信息 cout << "Server exiting..." << endl; //退出 return ExitClient(SERVER_EXIT_OK); } /* * 初始化成员变量 */ void InitMember(void) { //初始化读和写缓冲区 memset(bufRecv, 0, MAX_NUM_BUF); memset(bufSend, 0, MAX_NUM_BUF); //初始化 sServer = INVALID_SOCKET; sClient = INVALID_SOCKET; //没有连接状态 bConning = FALSE; } /* * 退出 */ int ExitClient(int nExit) { closesocket(sServer); //关闭监听套接字 closesocket(sClient); //关闭连接客户端套接接 WSACleanup(); //卸载Windows sockets DLL 清理内存 return nExit; //退出 } /* * 读一行数据 */ BOOL RecvLine(SOCKET s, char* buf) { BOOL retVal = TRUE; //返回值 BOOL bLineEnd = FALSE; //行结束 int nReadLen = 0; //读入字节数 int nDataLen = 0; //数据长度 while (!bLineEnd && bConning) //与客户端连接 没有换行 { nReadLen = recv(s, buf + nDataLen, 1, 0);//每次接收一个字节 //错误处理 if (SOCKET_ERROR == nReadLen) { int nErrCode = WSAGetLastError();//错误代码 if (WSAENOTCONN == nErrCode) { ShowSocketMsg("The socket is not connected!"); }else if(WSAESHUTDOWN == nErrCode) { ShowSocketMsg("The socket has been shut down!"); }else if (WSAETIMEDOUT == nErrCode) { ShowSocketMsg("The connection has been dropped!"); }else if (WSAECONNRESET == nErrCode) { ShowSocketMsg("The virtual circuit was reset by the remote side!"); }else{} retVal = FALSE; //读数据失败 break; //跳出循环 } if (0 == nReadLen)//客户端关闭 { retVal = FALSE; //读数据失败 break ; //跳出循环 } //读入数据 if ('\n' == *(buf + nDataLen)) //换行符 { bLineEnd = TRUE; //接收数据结束 }else{ nDataLen += nReadLen; //增加数据长度 } } return retVal; } /* * //发送一行数据 */ BOOL SendLine(SOCKET s, char* str) { int retVal;//返回值 retVal = send(s, str, strlen(str), 0);//一次发送 //错误处理 if (SOCKET_ERROR == retVal) { int nErrCode = WSAGetLastError();//错误代码 if (WSAENOTCONN == nErrCode) { ShowSocketMsg("The socket is not connected!"); }else if(WSAESHUTDOWN == nErrCode) { ShowSocketMsg("The socket has been shut down!"); }else if (WSAETIMEDOUT == nErrCode) { ShowSocketMsg("The connection has been dropped!"); }else{} return FALSE; //发送失败 } return TRUE; //发送成功 } /* * 错误处理 */ int HandleSocketError(char *str) { ShowSocketMsg(str); //显示错误消息 WSACleanup(); //卸载Windows socket DLL return SERVER_API_ERROR;//退出应用程序 } /* * 显示错误 */ void ShowSocketMsg(char* str) { MessageBox(NULL, str, "SERVER ERROR", MB_OK); }
client:
#include "stdafx.h" #include <windows.h> #include <winsock.h> #include <iostream> #pragma comment(lib, "wsock32.lib") using namespace std; #define CLIENT_EXIT_OK 0 //客户端正常退出 #define CLIENT_DLL_REEOR 1 //调用Windows socket dll失败 #define CLIENT_API_ERROR 2 //调用Windows socket api失败 #define MAX_NUM_BUF 64 //缓冲区的最大长度 #define SERVERPORT 5555//服务器TCP端口 //变量 char bufRecv[MAX_NUM_BUF]; //读缓冲区 char bufSend[MAX_NUM_BUF]; //写缓冲区 SOCKET sHost; //socket BOOL bConning; //连接服务器状态 //函数 void InitMember(void); //初始化变量 int ExitClient(int nExit); //退出 BOOL RecvLine(SOCKET s, char* buf); //读取一行数据 void ShowErrorMsg(void); //显示错误信息 //主函数 int main() { //初始化变量 InitMember(); WORD wVersionRequested; //应用程序需要Windows sockets DLL的版本 WSADATA wsaData; //Windows sockets DLL版本信息 int retVal; //调用Windows sockets API返回值 //初始化Windows Sockets DLL wVersionRequested = MAKEWORD(1,1); retVal = WSAStartup(wVersionRequested,(LPWSADATA)&wsaData); if ( 0 != retVal ) { MessageBox(NULL, "Can not find a usable Windows Sockets dll!", "ERROR", MB_OK); return CLIENT_DLL_REEOR; } //创建Windows socket sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET == sHost) { ShowErrorMsg(); //显示错误信息 WSACleanup(); //释放资源 return CLIENT_API_ERROR;//退出 } //准备连接服务器 cout << "Client succeeded!" << endl; cout<<"Be ready to connect to server..."<<endl; //获取主机的信息 LPHOSTENT hostEntry; char hostname[MAX_NUM_BUF]; gethostname(hostname,MAX_NUM_BUF); //获取主机名称 hostEntry = gethostbyname(hostname); //获取主机信息 if(!hostEntry) { ShowErrorMsg(); //显示错误信息 return ExitClient(CLIENT_API_ERROR); //退出 } //设置sockaddr_in SOCKADDR_IN addrServ; addrServ.sin_family = AF_INET; addrServ.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list); addrServ.sin_port = htons(SERVERPORT); //连接服务器 retVal=connect(sHost,(LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN)); if(SOCKET_ERROR == retVal) { ShowErrorMsg(); //显示错误信息 return ExitClient(CLIENT_API_ERROR); //退出 }else{ bConning = TRUE; //连接服务器成功 } //连接服务器成功 cout<<"Connect successfully!"<<endl; //向服务器发送数据 strcpy(bufSend, "Hello, Server!\n"); retVal = send(sHost, bufSend, strlen(bufSend), 0); if (SOCKET_ERROR == retVal) { ShowErrorMsg(); //显示错误信息 return ExitClient(CLIENT_API_ERROR); //退出 } //从服务器接收数据 if (!RecvLine(sHost, bufRecv)) { ShowErrorMsg(); //显示错误信息 return ExitClient(CLIENT_API_ERROR); //退出 } //显示服务器的应答 cout<<bufRecv<<endl; //退出 return ExitClient(CLIENT_EXIT_OK); } /* * 显示错误信息 */ void ShowErrorMsg(void) { int nErrCode = WSAGetLastError();//获取错误代码 HLOCAL hlocal = NULL; //获取错误的文本字符串 BOOL fOk = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, nErrCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (PTSTR)&hlocal, 0, NULL); //显示错误信息 if (hlocal != NULL) { MessageBox(NULL, (char*)LocalLock(hlocal), "CLIENT ERROR", MB_OK); LocalFree(hlocal); } } /* * 初始化成员变量 */ void InitMember(void) { //初始化读和写缓冲区 memset(bufRecv, 0, MAX_NUM_BUF); memset(bufSend, 0, MAX_NUM_BUF); //初始化 sHost = INVALID_SOCKET; //没有连接状态 bConning = FALSE; } /* * 退出 */ int ExitClient(int nExit) { closesocket(sHost); //关闭套接字 WSACleanup(); //卸载Windows sockets DLL 清理内存 //显示退出信息 cout << "Client exiting..." << endl; Sleep(20000); return nExit; //退出 } /* * 读取一行数据 */ BOOL RecvLine(SOCKET s, char* buf) { BOOL retVal = TRUE; //返回值 BOOL bLineEnd = FALSE; //行结束 int nReadLen = 0; //读入字节数 int nDataLen = 0; //数据长度 while (!bLineEnd && bConning) //与客户端连接 没有换行 { nReadLen = recv(s, buf + nDataLen, 1, 0);//每次接收一个字节 //错误处理 if (SOCKET_ERROR == nReadLen) { retVal= FALSE; //读数据失败 break; //跳出循环 } if (0 == nReadLen)//客户端关闭 { retVal = FALSE; //读数据失败 break ; //跳出循环 } //读入数据 if ('\n' == *(buf + nDataLen)) //换行符 { bLineEnd = TRUE; //接收数据结束 }else{ nDataLen += nReadLen; //增加数据长度 } } return retVal; }