成都手机号码销售网站建设,制作网站协议,网站qq登录原理,不用网站怎么做落地页一、非阻塞socket 非阻塞套接字是指执行此套接字的网络调用时#xff0c;不管是否执行成功#xff0c;都立即返回。比如调用recv()函数读取网络缓冲区中数据#xff0c;不管是否读到数据都立即返回#xff0c;而不会一直挂在此函数调用上。在实际Windows网络通信软件开发中…一、非阻塞socket 非阻塞套接字是指执行此套接字的网络调用时不管是否执行成功都立即返回。比如调用recv()函数读取网络缓冲区中数据不管是否读到数据都立即返回而不会一直挂在此函数调用上。在实际Windows网络通信软件开发中异步非阻塞套接字是用的最多的。平常所说的C/S客户端/服务器结构的软件就是异步非阻塞模式的。 int32_t flags fcntl(socket_fd, F_GETFL, 0); fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK); 二、EAGAIN错误 当应用程序在socket中设置O_NONBLOCK属性后如果发送缓存被占满send就会返回EAGAIN或EWOULDBLOCK的错误。在将socket设置O_NONBLOCK属性后通过socket发送一个100K大小的数据第一次成功发送了13140数据之后继续发送并未成功errno数值为EAGAIN错误。
三、EPOLL模式下EAGAIN错误处理方式 方法需要封装socket_send()的函数用来处理这种情况,该函数会尽量将数据写完再返回返回发送的字节数。在socket_send()内部,当写缓冲已满(send()返回-1,且errno为EAGAIN)那么会等待后再重试。 int32_t socket_send(int fd, char* data, int32_t size) { if (NULL data || size 0) { return -1; } int32_t remainded size; int32_t sended 0; char* pszTmp data; while(remainded 0) { sended send(fd, pszTmp, (size_t)remainded, 0); if (sended 0) { pszTmp sended; remainded - sended; } else if (errno EAGAIN) { continue; } else { break; } } return (size - remainded); } 这种方式并不很完美当发送大数据的时候如果客户端一直不调用recv函数接受数据那么服务器就会卡死在while循环中持续调用send函数返回EAGAIN错误。对服务器来说出现这种情况是致命的届时服务器的所有功能都不能正常运转。 如果当send函数出现EAGAIN错误的时候直到当前socket状态变成可写之前不应该继续调用send函数发送数据。在发送数据之前将socket的监听的事件增加EPOLLOUT在数据全部发送之后再取消EPOLLOUT的监听。 socket监听EPOLLOUT代码 void epoll_event_mod(int epoll_socket_fd, int fd) { struct epoll_event epollEvent; memset(epollEvent, 0x0, sizeo(epollEvent)); epollEvent.data.fd fd; epollEvent.events EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT; epollEvent.data.ptr NULL; epoll_ctl(epoll_socket_fd, EPOLL_CTL_MOD, fd, m_epoll_event); } socket缓存结构体代码 struct stSocketBuffer { int32_t m_iHead; int32_t m_iTail; char m_szBuffer[max_socket_buffer_size]; }; socket待发送数据放入缓存结构代码 int32_t push_socket_data(int fd, char* data, int32_t size) { if (NULL data || size 0) { return -1; } stSocketBuffer* pstBuffer get_socket_buffer(fd); if (NULL pstBuffer) { return -2; } if ( size max_socket_buffer_size m_iHead - m_iTail) { return -3; } if (size m_iTail max_socket_buffer_size) { memcopy(pstBuffer-m_szBuffer[0], pstBuffer-m_szBuffer[pstBuffer-m_iHead], pstBuffer-m_iTail - pstBuffer-m_iHead); pstBuffer-m_iTail - pstBuffer-m_iHead; pstBuffer-m_iHead 0; } memcpy(pstBuffer-m_szBuffer[pstBuffer-m_iTail], data, size); pstBuffer-m_iTail size; return 0; }
将缓存区数据发送出去代码 int32_t socket_send(int fd) { stSocketBuffer* pstBuffer get_socket_buffer(fd); if (NULL pstBuffer) { return -1; } int32_t remainded pstBuffer-m_iTail - pstBuffer-m_iHead; int32_t sended 0; char* pszTmp pstBuffer-m_szBuffer[pstBuffer-m_iHead]; int32_t again_count 0; while(remainded 0 again_count 2) { sended send(fd, pszTmp, (size_t)remainded, 0); if (sended 0) { pstBuffer-m_iHead sended; pszTmp sended; remainded - sended; } else if (errno EAGAIN) { again_count; continue; } else { break; } } return (size - remainded); } 总结当需要向socket发送数据时现将数据压入发送缓存区stSocketBuffer结构体中并且将socket加入可写事件监听。当socket触发可写事件EPOLLOUT时调用socket_send函数发送数据所有数据发送完毕再清除EPOLLOUT事件。