Socket C++ TCP阻塞 非阻塞总结

上传人:痛*** 文档编号:180772210 上传时间:2023-01-07 格式:DOC 页数:30 大小:209KB
返回 下载 相关 举报
Socket C++ TCP阻塞 非阻塞总结_第1页
第1页 / 共30页
Socket C++ TCP阻塞 非阻塞总结_第2页
第2页 / 共30页
Socket C++ TCP阻塞 非阻塞总结_第3页
第3页 / 共30页
点击查看更多>>
资源描述
0、可运行实例及基本知识1、如何设置socket函数的非阻塞调用?2、深入 CSocket 编程之阻塞和非阻塞模式3、SOCKET类的设计和实现 服务器#include stdafx.h#include #include #include #include #include #include #pragma comment(lib, ws2_32.lib)#define SERVPORT 7861 /*服务器监听端口号*/#define MAXDATASIZE 100#define BACKLOG 10using namespace std;std:vector client_fd;char bufMAXDATASIZE; DWORD WINAPI qtPingServerThreadFunc(LPVOID lpThreadParameter);int _tmain(int argc, _TCHAR* argv)SOCKET sockfd; /*sock_fd:监听socket;client_fd:数据传输socket */ struct sockaddr_in my_addr; /* 本机地址信息*/ struct sockaddr_in remote_addr; /* 客户端地址信息*/ char szMsg=hello;WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(2,0);err = WSAStartup(wVersionRequested,&wsaData);if (0 != err)coutSocket failed;return 0;if (LOBYTE(wsaData.wVersion)!= 2 | HIBYTE(wsaData.wVersion) != 0)WSACleanup();return 0;sockfd = socket(AF_INET,SOCK_STREAM,0);if (INVALID_SOCKET = sockfd)coutSoket Create Failed;return 0;/*sockfd = socket(AF_INET, SOCK_STREAM, 0);*/if (sockfd = -1) perror( socket创建出错!); exit(1); my_addr.sin_family=AF_INET; my_addr.sin_port=htons(SERVPORT); my_addr.sin_addr.s_addr = INADDR_ANY; memset( &(my_addr.sin_zero),0,8);if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr) = -1) perror( bind出错!);/exit(1); int n = 0;while(1) if (listen(sockfd, BACKLOG) != -1) int sin_size = sizeof(struct sockaddr_in); SOCKET nSocket = accept(sockfd, (struct sockaddr *) &remote_addr, &sin_size);std:vector:iterator itr_end = client_fd.end();std:vector:iterator itr = std:find(client_fd.begin(),itr_end,nSocket);if (itr = itr_end)client_fd.push_back(nSocket);printf( received a connection from %sn, inet_ntoa(remote_addr.sin_addr);DWORD dwPingThreadID;HANDLE hPingHandle = CreateThread(0, 0, qtPingServerThreadFunc, 0, 0, &dwPingThreadID); for (int n = 0;n client_fd.size(); n+)closesocket(client_fdn); client_fd.clear(); DWORD WINAPI qtPingServerThreadFunc(LPVOID lpThreadParameter)while (1)for (int n = 0;n client_fd.size(); n+)int recvbytes;if (recvbytes=recv(client_fdn, buf, MAXDATASIZE, 0) !=-1) bufrecvbytes = 0; printf( Received: %d,%sn,n,buf);send(client_fdn, buf, sizeof(buf), 0);return 0x1001;客户端:#include #pragma comment(lib, ws2_32.lib)using namespace std;#define SERVPORT 7861 /*服务器监听端口号*/#define DEST_IP 192.168.1.35#define MAXDATASIZE 100;int _tmain(int argc, _TCHAR* argv)int sockfd, recvbytes; char bufMAXDATASIZE; struct hostent *host; struct sockaddr_in serv_addr; struct sockaddr_in dest_addr; /* 目的地址*/ WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(2,0);err = WSAStartup(wVersionRequested,&wsaData);if (0 != err)couth_addr);*/serv_addr.sin_addr.s_addr = inet_addr(DEST_IP); memset( &(serv_addr.sin_zero),0,8);if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr) = -1) perror(connect出错!);/exit(1);char szMsg = hao;int nlen = sizeof(serv_addr);int uIndex = 0;while (1)Sleep(1000);if (send(sockfd, Hello, are connected!n, 23, 0) = -1)coutWSAGetLastError()endl;elsecoutuIndex+:an TCP package is sendedm_hSocketWindow != NULL); return WSAAsyncSelect(m_hSocket, pState-m_hSocketWindow, WM_SOCKET_NOTIFY, lEvent) != SOCKET_ERROR; 函数参数lEvent表示希望监视的网络事件。 _ afxSockThreadState得到的是当前的模块线程状态,m_ hSocketWindow是本模块在当前线 程的“socket窗口”,指定监视m_hSocket的网络事件,如指定事件发生,给窗口 m_hSocketWindow发送WM_SOCKET_NOTIFY消息。 被指定的网络事件对应的网络I/O将是异步操作,是非阻塞操作。例如:指定FR_READ导致 Receive是一个异步操作,如果不能立即读到数据,则返回一个错误WSAEWOULDBLOCK。在数 据到达之后,WinSock通知窗口m_hSocketWindow,导致OnReceive被调用。 指定FR_WRITE导致Send是一个异步操作,即使数据没有送出也返回一个错误WSAEWOULDBLOCK 。在数据可以发送之后,WinSock通知窗口m_hSocketWindow,导致OnSend被调用。 指定FR_CONNECT导致Connect是一个异步操作,还没有连接上就返回错误信息 WSAEWOULDBLOCK,在连接完成之后,WinSock通知窗口m_hSocketWindow,导致OnConnect被 调用。 对于其他网络事件,就不一一解释了。 所以,使用CAsyncSocket时,如果使用Create缺省创建socket,则所有网络I/O都是异步操 作,进行有关网络I/O时则必须覆盖以下的相关函数: OnAccept、OnClose、OnConnect、OnOutOfBandData、OnReceive、OnSend。 (5)Bind函数 经过上述过程,socket创建完毕,下面,调用Bind函数给m_hSocket指定本地端口和IP地址 。Bind的实现如下: BOOL CAsyncSocket:Bind(UINT nSocketPort, LPCTSTR lpszSocketAddress) USES_CONVERSION; /使用WinSock的地址结构构造地址信息 SOCKADDR_IN sockAddr; memset(&sockAddr,0,sizeof(sockAddr); /得到地址参数的值 LPSTR lpszAscii = T2A(LPTSTR)lpszSocketAddress); /指定是Internet地址类型 sockAddr.sin_family = AF_INET; if (lpszAscii = NULL) /没有指定地址,则自动得到一个本地IP地址 /把32比特的数据从主机字节序转换成网络字节序 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); else /得到地址 DWORD lResult = inet_addr(lpszAscii); if (lResult = INADDR_NONE) WSASetLastError(WSAEINVAL); return FALSE; sockAddr.sin_addr.s_addr = lResult; /如果端口为0,则WinSock分配一个端口(10245000) /把16比特的数据从主机字节序转换成网络字节序 sockAddr.sin_port = htons(u_short)nSocketPort); /Bind调用WinSock API函数bind return Bind(SOCKADDR*)&sockAddr, sizeof(sockAddr); 其中:函数参数1指定了端口;参数2指定了一个包含本地地址的字符串,缺省是NULL。 函数Bind首先使用结构SOCKADDR_IN构造地址信息。该结构的域sin_family表示地址格式 (TCP/IP同协议族),赋值为AF_INET(Internet地址格式);域sin_port表示端口,如果参 数1为0,则WinSock分配一个端口给它,范围在1024和5000之间;域sin_addr是表示地址信 息,它是一个联合体,其中s_addr表示如下形式的字符串,“28.56.22.8”。如果参数没有 指定地址,则WinSock自动地得到本地IP地址(如果有几个网卡,则使用其中一个的地址) 。 (6)总结Create的过程 首先,调用socket函数创建一个socket;然后把创建的socket对象映射到CAsyncSocket对象 (捆绑在一起),指定本socket要通知的网络事件,并创建一个“socket窗口”来接收网络 事件消息,最后,指定socket的本地信息。 下一步,是使用成员函数Connect连接远地主机,配置socket的远地信息。函数Connect类似 于Bind,把指定的远地地址转换成SOCKADDR_IN对象表示的地址信息(包括网络字节序的转 换),然后调用WinSock函数Connect连接远地主机,配置socket的远地端口和远地IP地址。 3.异步网络事件的处理 当网络事件发生时,“socket窗口”接收WM_SOCKET_NOTIFY消息,消息处理函数 OnSocketNotify被调用。“socket窗口”的定义和消息处理是MFC实现的,这里不作详细的 讨论。 OnSocketNotify回调CAsyncSocket的成员函数DoCallBack,DoCallBack调用事件处理函数, 如OnRead、OnWrite等。摘录DoCallBack的一段代码如下: switch (WSAGETSELECTEVENT(lParam) case FD_READ: DWORD nBytes; /得到可以一次读取的字节数 pSocket-IOCtl(FIONREAD, &nBytes); if (nBytes != 0) pSocket-OnReceive(nErrorCode); break; case FD_WRITE: pSocket-OnSend(nErrorCode); break; case FD_OOB: pSocket-OnOutOfBandData(nErrorCode); break; case FD_ACCEPT: pSocket-OnAccept(nErrorCode); break; case FD_CONNECT: pSocket-OnConnect(nErrorCode); break; case FD_CLOSE: pSocket-OnClose(nErrorCode); break; lParam是WM_SOCKET_NOFITY的消息参数,OnSocketNotify传递给函数DoCallBack,表示通知 事件。 函数IOCtl是CAsyncSocket的成员函数,用来对socket的I/O进行控制。这里的使用表示本次 调用Receive函数至多可以读nBytes个字节。 从上面的讨论可以看出,从创建socket到网络I/O,CAsyncSocket直接封装了低层的WinSock API,简化了WinSock编程,实现了一个异步操作的界面。如果希望某个操作是阻塞操作,则 在调用Create时不要指定该操作对应的网络事件。例如,希望Connect和Send是阻塞操作, 在任务完成之后才返回,则可以使用如下的语句: pSocket-Create(0, SOCK_STREAM, FR_WRITE|FR_OOB|FR_ACCEPT|FR_CLOSE); 这样,在Connect和Send时,如果是用户界面线程的话,可能阻塞线程消息循环。所以,最 好在工作者线程中使用阻塞操作。 2.CSocket 如果希望在用户界面线程中使用阻塞socket,则可以使用CSocket。它在非阻塞socket基础之上实 现了阻塞操作,在阻塞期间实现了消息循环。 对于CSocket,处理网络事件通知的函数OnAccept、OnClose、OnReceive仍然可以使用, OnConnect、OnSend在CSocket中永远不会被调用,另外OnOutOfBandData在CSocket中不鼓励使用 。 CSocket对象在调用Connect、Send、Accept、Close、Receive等成员函数后,这些函数在完成任 务之后(连接被建立、数据被发送、连接请求被接收、socket被关闭、数据被读取)之后才会返 回。因此,Connect和Send不会导致OnConnect和OnSend被调用。如果覆盖虚拟函数OnReceive、 OnAccept、OnClose,不主动调用Receive、Accept、Close,则在网络事件到达之后导致对应的虚 拟函数被调用,虚拟函数的实现应该调用Receive、Accept、Close来完成操作。下面,就一个函 数Receive来考察CSocket如何实现阻塞操作和消息循环的。 int CSocket:Receive(void* lpBuf, int nBufLen, int nFlags) /m_pbBlocking是CSocket的成员变量,用来标识当前是否正在进行 /阻塞操作。但不能同时进行两个阻塞操作。 if (m_pbBlocking != NULL) WSASetLastError(WSAEINPROGRESS); return FALSE; /完成数据读取 int nResult; while (nResult = CAsyncSocket:Receive(lpBuf, nBufLen, nFlags) = SOCKET_ERROR) if (GetLastError() = WSAEWOULDBLOCK) /进入消息循环,等待网络事件FD_READ if (!PumpMessages(FD_READ) return SOCKET_ERROR; else return SOCKET_ERROR; return nResult; 其中: 参数1指定一个缓冲区保存读取的数据;参数2指定缓冲区的大小;参数3取值MSG_PEEK(数据拷贝 到缓冲区,但不从输入队列移走),或者MSG_OOB(处理带外数据),或者MSG_PEEK|MSG_OOB。 Receive函数首先判断当前CSocket对象是否正在处理一个阻塞操作,如果是,则返回错误 WSAEINPROGRESS;否则,开始数据读取的处理。 读取数据时,如果基类CAsyncSocket的Receive读取到了数据,则返回;否则,如果返回一个错误 ,而且错误号是WSAEWOULDBLOCK,则表示操作阻塞,于是调用PumpMessage进入消息循环等待数据 到达(网络事件FD_READ发生)。数据到达之后退出消息循环,再次调用CAsyncSocket的Receive 读取数据,直到没有数据可读为止。 PumpMessages是CSocket的成员函数,它完成以下工作: (1)设置m_pbBlocking,表示进入阻塞操作。 (2)进行消息循环,如果有以下事件发生则退出消息循环:收到指定定时器的定时事件消息 WM_TIMER,退出循环,返回TRUE;收到发送给本socket的消息WM_SOCKET_NOTIFY,网络事件 FD_CLOSE或者等待的网络事件发生,退出循环,返回TRUE;发送错误或者收到WM_QUIT消息,退出 循环,返回FALSE; (3)在消息循环中,把WM_SOCKET_DEAD消息和发送给其他socket的通知消息WM_SOCKET_NOFITY放 进模块线程状态的通知消息列表m_listSocketNotifications,在阻塞操作完成之后处理;对其他 消息,则把它们送给目的窗口的窗口过程处理。 3.CSocketFile MFC还提供了一个网络编程模式,可以充分利用CSocket的特性。该模式的基础是CSocketFile类。 使用方法如下: 首先,构造一个CSocket对象;调用Create函数创建一个socket对象(SOCK_STREAM类型)。 接着,如果是客户程序,调用Connect连接到远地主机;如果是服务器程序,先调用Listen监听 socket端口,收到连接请求后调用Accept接收请求。 然后,创建一个和CSocket对象关联的CSocketFile对象,创建一个和CSocketFile对象关联的 CArchive对象,指定CArchive对象是用于读或者写。如果既要读又要写,则创建两个CArchive对 象。 创建工作完成之后,
展开阅读全文
相关资源
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!