In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)06/03 Report--
These years, came into contact with the shape of the project, wrote a lot of network programming code, from windows to linux, fell into a lot of holes, because network programming involves a lot of details and skills, always want to write an article to summarize the experience and experience in this area, hope to be of some help to the newcomers, then it is very good.
The platforms covered in this article include windows and linux, and here we begin.
1. How to write non-blocking connect () function
We know that the connect () function is blocked by default until the three-way handshake is established, or there is no timeout return, during which the program execution flow is blocked. So how do you use the connect () function to write non-blocking connection code?
The following ideas can be adopted in both windows and linux platforms:
When creating a socket, set socket to non-blocking mode. For more information, please refer to my series of articles entitled "Server programming experience (4)-- how to set socket to non-blocking mode".
Then call connect () to connect, and if connect () can connect successfully immediately, return 0; if the connection cannot be made immediately, return-1 (SOCKET_ERROR is also equal to-1 on windows). At this time, the error code is WSAEWOULDBLOCK (windows platform) or EINPROGRESS (linux platform), indicating that it cannot be completed immediately.
The select () function is then called to detect whether the socket is writable within a specified period of time, and if writable indicates that the connect () connection is successful.
It should be noted that connect () on the linux platform cannot return-1 for the time being. The error code may be EINPROGRESS, or it may be interrupted by the signal. At this time, the error code is: EINTR. This situation should also be taken into account; on the windows platform, in addition to using the select () function to detect whether the socket is writable, you can also use the function WSAAsyncSelect or WSAEventSelect that comes with the windows platform.
Here is the code:
/ * * @ param timeout connection timeout (in seconds) * @ return returns true if the connection succeeds, or false * / bool CSocket::Connect (int timeout) {/ / windows sets socket to non-blocking mode unsigned long on = 1; if (:: ioctlsocket (m_hSocket, FIONBIO, & on)
< 0) return false; //linux将socket设置成非阻塞的方式 //将新socket设置为non-blocking /* int oldflag = ::fcntl(newfd, F_GETFL, 0); int newflag = oldflag | O_NONBLOCK; if (::fcntl(m_hSocket, F_SETFL, newflag) == -1) return false; */ struct sockaddr_in addrSrv = { 0 }; addrSrv.sin_family = AF_INET; addrSrv.sin_addr = htonl(addr); addrSrv.sin_port = htons((u_short)m_nPort); int ret = ::connect(m_hSocket, (struct sockaddr*)&addrSrv, sizeof(addrSrv)); if (ret == 0) return true; //windows下检测WSAEWOULDBLOCK if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) return false; //linux下需要检测EINPROGRESS和EINTR /* if (ret < 0 && (errno != EINPROGRESS || errno != EINTR)) return false; */ fd_set writeset; FD_ZERO(&writeset); FD_SET(m_hSocket, &writeset); struct timeval tv; tv.tv_sec = timeout; //可以利用tv_usec做更小精度的超时设置 tv.tv_usec = 0; if (::select(m_hSocket + 1, NULL, &writeset, NULL, &tv) != 1) return false; return true;} 二、非阻塞socket下如何正确的收发数据 这里不讨论阻塞模式下,阻塞模式下send和recv函数如果tcp窗口太小或没有数据的话都是阻塞在send和recv调用处的。对于收数据,一般的流程是先用select(windows和linux平台皆可)、WSAAsyncSelect()或WSAEventSelect()(windows平台)、poll或epoll_wait(linux平台)检测socket有数据可读,然后进行收取。对于发数据,;linux平台下epoll模型存在水平模式和边缘模式两种情形,如果是边缘模式一定要一次性把socket上的数据收取干净才行,也就是一定要循环到recv函数出错,错误码是EWOULDBLOCK。而linux下的水平模式或者windows平台上可以根据业务一次性收取固定的字节数,或者收完为止。还有个区别上文也说过,就是windows下发数据的代码稍微有点不同的就是不需要检测错误码是EINTR,只需要检测是否是WSAEWOULDBLOCK。代码如下: 用于windows或linux水平模式下收取数据,这种情况下收取的数据可以小于指定大小,总之一次能收到多少是多少: bool TcpSession::Recv(){ //每次只收取256个字节 char buff[256]; //memset(buff, 0, sizeof(buff)); int nRecv = ::recv(clientfd_, buff, 256, 0); if (nRecv == 0) return false; inputBuffer_.add(buff, (size_t)nRecv); return true;}如果是linux epoll边缘模式(ET),则一定要一次性收完:bool TcpSession::RecvEtMode(){ //每次只收取256个字节 char buff[256]; while (true) { //memset(buff, 0, sizeof(buff)); int nRecv = ::recv(clientfd_, buff, 256, 0); if (nRecv == -1) { if (errno == EWOULDBLOCK || errno == EINTR) return true; return false; } //对端关闭了socket else if (nRecv == 0) return false; inputBuffer_.add(buff, (size_t)nRecv); } return true;} 用于linux平台发送数据: bool TcpSession::Send(){ while (true) { int n = ::send(clientfd_, buffer_, buffer_.length(), 0); if (n == -1) { //tcp窗口容量不够, 暂且发不出去,下次再发 if (errno == EWOULDBLOCK) break; //被信号中断,继续发送 else if (errno == EINTR) continue; return false; } //对端关闭了连接 else if (n == 0) return false; buffer_.erase(n); //全部发送完毕 if (buffer_.length() == 0) break; } return true;} 另外,收发数据还有个技巧是设置超时时间,除了用setsocketopt函数设置send和recv的超时时间以外,还可以自定义整个收发数据过程中的超时时间,思路是开始收数据前记录下时间,收取完毕后记录下时间,如果这个时间差大于超时时间,则认为超时,代码分别是: long tmSend = 3*1000L;long tmRecv = 3*1000L;setsockopt(m_hSocket, IPPROTO_TCP, TCP_NODELAY,(LPSTR)&noDelay, sizeof(long));setsockopt(m_hSocket, SOL_SOCKET, SO_SNDTIMEO,(LPSTR)&tmSend, sizeof(long));int httpclientsocket::RecvData(string& outbuf,int& pkglen){ if(m_fd == -1) return -1; pkglen = 0; char buf[4096]; time_t tstart = time(NULL); while(true) { int ret = ::recv(m_fd,buf,4096,0); if(ret == 0) { Close(); return 0;//对方关闭socket了 } else if(ret < 0) { if(errno == EAGAIN || errno ==EWOULDBLOCK || errno == EINTR) { if(time(NULL) - tstart >M_timeout) {Close (); return 0;} else continue;} else {Close (); return ret / / receive error}} outbuf.append (buf,buf+ret); pkglen = GetBufLen (outbuf.data (), outbuf.length ()); if (pkglen)
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.