关于send时产生WSAEWOULDBLOCK的处理

这篇博客探讨了在使用非阻断模式的Socket编程时,如何处理send函数返回WSAEWOULDBLOCK错误的情况。作者指出,这种错误表明发送窗口已满,但数据并未丢失,而是被系统缓存,等待窗口有空间时继续发送。文章讨论了阻断模式和非阻断模式的区别,并建议在非阻断模式下遇到WSAEWOULDBLOCK时,将待发送数据缓存,等待FD_WRITE事件触发后再发送。同时,作者提到使用select模型作为更高效的选择。

 http://topic.youkuaiyun.com/t/20050328/11/3887277.html

 

搜索了一下论坛里关于send时产生WSAEWOULDBLOCK时的处理办法,得出如下结论:  
        1.产生这个错误只是说明out   buffer已经满了,不代表出错.  
        2.可以等待FD_WRITE消息,此时将没有发送完成的数据再次发送出去.  
   
        但还是有疑问,我的问题如下:  
        1.我上面的两条结论正确吗?  
        2.关于在FD_WRITE里把没有发送完的数据发送完,这一点如何做到呢?因为  
        int   nSended   =   send(mysock,   myBuf,   myLen,   0);  
        如果此时产生WSAEWOULDBLOCK,那么nSended肯定是SOCKET_ERROR,也就是-1了.此时nSended不能代表已经发送成功的字节数,那么在myBuf这个缓冲区里,究竟有多少字节是已经发送出去的,有多少数据是没有发送出去的呢?  
         
        在我的程序时,我想在得到WSAEWOULDBLOCK时,将没有发送完的数据缓冲起来,等到FD_WRITE时,再将这些数据发送出去.所以还请大侠们指点一下,在这种情况下,如何知道有多少数据是被发送了的.  
   
        :_) 问题点数:20、回复次数:16Top

1 楼foxwing(穿过你的骨髓我的手)回复于 2005-03-28 13:26:10 得分 20

首先要确定一点:send函数中的这个myBuf是应用程序提供的一个const类型的字符串(就以字符串为例吧),并不是所谓的“缓冲区”。我觉得其实不是缓冲区已满,而是发送窗口已满,缓冲区是系统动态分配的,一般来说是足够用的,不大容易出现缓冲区已满的现象。  
   
  我觉得过程是这样的:send函数先把数据传给Socket核心处理模块,核心处理模块负责管理这些需要发送出去的数据的内存堆栈,然后试着将这些数据包交付协议层的发送窗口,如果窗口大小足够,则核心驱动模块将数据包完全交付给窗口去发送,并删除暂存在自己内存堆栈上的数据;如果窗口大小不够(导致这种情况的原因有很多,比如接收端接收缓存已满;至于这个“窗口大小不够”这个消息到底是核心驱动模块经自己判断生成的,还是由协议层告知核心驱动模块的,这点我也不大清楚,也想搞明白),则将能交付的数据尽最大量交给窗口去发送,并将无法发送出去的那些数据暂存在核心处理模块的内存堆栈上,并同时向上给send函数发送一个发送窗口已满的消息,然后send函数便处于BLOCK状态。  
  可以确定的是,send函数不会直接和协议层打交道,而是通过核心驱动模块来和协议层交互(废话,呵呵)  
  另外,我觉得知道多少数据被发送是没有意义的,因为一旦将数据通过发送窗口发送出去,核心驱动模块就会将剩余的数据马上交付空的发送窗口,这个时间非常短,等你得到了发送数据的大小时说不定你要发送的所有数据已经发送出去了,从而返回你指定发送的数据大小。如果你的兴趣在于接收端的recv函数处于暂停接收状态(通过设置断点)而要求send函数返回发送窗口里的数据大小,我也有同样的兴趣,呵呵Top

2 楼jakkye(欧)回复于 2005-03-28 17:21:25 得分 0

好!!讲解的好,读了两遍,明白什么意思了...  
  呵呵...以前我一直认为send是直接跟协议层打交道的,但想一想有点不大现实,经这么一解释,霍然开朗型...  
  看来得去把<计算机网络>找出来,加强点原理性的东东了:)  
   
  多谢多谢:)Top

3 楼jakkye(欧)回复于 2005-03-28 17:37:09 得分 0

不过还有一个问题,很简单的一个程序,如下:  
        for(int   i=0;   i<=1000;   i++)  
        {  
              int   nSended   =   send(mySocket,   myBuf,   myLen,   0);  
        }  
  按照你的说法,send函数把myBuf这个字节数组上的内容交给了核心处理模块,那么,如果i==500的时候,nSended变成了SOCKET_ERROR,且WSAGetLastError()   ==   WSAEWOULDBLOCK,而此时这个XUN环并没有判断,还会进行接下来的500个循环,那是不是接下来的500个循环中,send()也都把数据交给核心处理模块了吗?那如果这个循环接下来还有50000个呢?"核心处理模块的内存堆栈"有这么大吗?  
   
        一旦WSAEWOULDBLOCK产生,那么接下来的send函数是如何动作的呢?是把待发

### 非阻塞多线程模式的概念与工作原理 非阻塞多线程模式是一种在多任务环境中处理输入/输出(I/O)操作的方式。在这种模式下,当一个线程调用读取或写入函数,如果数据尚未准备好,函数不会阻塞线程的执行,而是立即返回一个状态值[^1]。这种机制允许程序继续执行其他任务,而无需等待I/O操作完成。 为了实现非阻塞行为,通常需要将套接字设置为非阻塞模式。这可以通过调用 `ioctlsocket` 函数来完成,该函数用于修改套接字的行为[^2]。当套接字处于非阻塞模式,如果尝试进行读取或写入操作而数据未就绪,系统会返回错误代码 `WSAEWOULDBLOCK`,表示操作无法立即完成。 以下是一个示例代码,展示如何使用 `ioctlsocket` 设置非阻塞模式: ```c u_long mode = 1; // 1 表示非阻塞模式 int result = ioctlsocket(socket, FIONBIO, &mode); if (result != NO_ERROR) { // 错误处理 } ``` ### WSAEWOULDBLOCK 错误代码含义 `WSAEWOULDBLOCK` 是 Windows Sockets API 中的一个错误代码,表示当前操作无法立即完成,因为资源暂不可用。在非阻塞模式下,当调用 `recv()` 或 `send()` 等函数,如果数据尚未准备好或缓冲区不可用,函数将返回此错误代码[^2]。这并不意味着发生了致命错误,而是通知调用方稍后重试。 ### CreateThread 函数用法 `CreateThread` 是 Windows API 提供的一个函数,用于创建新线程并开始执行指定的线程处理函数。其基本语法如下: ```c HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); ``` - `lpThreadAttributes`:指向安全属性结构的指针,通常设置为 `NULL`。 - `dwStackSize`:新线程的堆栈大小,通常设置为 `0`,表示使用默认堆栈大小。 - `lpStartAddress`:线程入口点函数的地址。 - `lpParameter`:传递给线程入口点函数的参数。 - `dwCreationFlags`:控制线程创建的行为,例如是否挂起线程。 - `lpThreadId`:指向接收新线程标识符的变量。 以下是一个简单的线程创建示例: ```c DWORD WINAPI ThreadFunction(LPVOID lpParameter) { // 线程处理逻辑 return 0; } HANDLE hThread = CreateThread(NULL, 0, ThreadFunction, NULL, 0, NULL); if (hThread == NULL) { // 错误处理 } CloseHandle(hThread); ``` ### 线程处理函数实现 线程处理函数是线程启动后执行的具体逻辑。它通常以 `DWORD WINAPI` 开头,并接受一个 `LPVOID` 类型的参数。以下是一个完整的非阻塞多线程通信示例,展示如何结合 `CreateThread` 和非阻塞套接字实现并发处理: ```c #include <winsock2.h> #include <windows.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") void SetSocketNonBlocking(SOCKET sock) { u_long mode = 1; ioctlsocket(sock, FIONBIO, &mode); // 设置非阻塞模式 } DWORD WINAPI ClientHandler(LPVOID lpParameter) { SOCKET clientSocket = (SOCKET)lpParameter; char buffer[256]; int bytesReceived; while (true) { bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0); if (bytesReceived > 0) { buffer[bytesReceived] = '\0'; printf("Received: %s\n", buffer); } else if (bytesReceived == 0) { printf("Client disconnected.\n"); break; } else if (WSAGetLastError() == WSAEWOULDBLOCK) { Sleep(100); // 暂停一段间后重试 } else { printf("Error receiving data.\n"); break; } } closesocket(clientSocket); return 0; } int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SetSocketNonBlocking(serverSocket); sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8080); serverAddr.sin_addr.s_addr = INADDR_ANY; bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)); listen(serverSocket, SOMAXCONN); while (true) { SOCKET clientSocket = accept(serverSocket, NULL, NULL); if (clientSocket == INVALID_SOCKET) { if (WSAGetLastError() == WSAEWOULDBLOCK) { Sleep(100); // 暂停一段间后重试 continue; } else { printf("Error accepting connection.\n"); break; } } HANDLE hThread = CreateThread(NULL, 0, ClientHandler, (LPVOID)clientSocket, 0, NULL); if (hThread == NULL) { printf("Error creating thread.\n"); closesocket(clientSocket); } else { CloseHandle(hThread); } } WSACleanup(); return 0; } ``` ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值