windows 套接字默认是阻塞式的
sock= socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in addr; //告诉sock 应该再什么地方licence
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(11111); //端口啦
addr.sin_addr.s_addr=htonl(INADDR_ANY); //在本机的所有ip上开始监听
bind (sock,(sockaddr *)&addr,sizeof(addr));//bind....
listen(sock,5); //最大5个队列
SOCKET socka; //这个用来接受一个连接
fd_set rfd; // 描述符集 这个将用来测试有没有一个可用的连接
struct timeval timeout;
FD_ZERO(&rfd); //总是这样先清空一个描述符集
timeout.tv_sec=60; //等下select用到这个
timeout.tv_usec=0;
u_long ul=1;
ioctlsocket(sock,FIONBIO,&ul); //用非阻塞的连接
//现在开始用select
FD_SET(sock,&rfd); //把sock放入要测试的描述符集 就是说把sock放入了rfd里面 这样下一步调用select对rfd进行测试的时候就会测试sock了(因为我们将sock放入的rdf) 一个描述符集可以包含多个被测试的描述符,
if(select(sock+1,&rfd,0,0, &timeout)==0)
{ //这个大括号接上面的,返回0那么就超过了timeout预定的时间
//处理....
}
if(FD_ISSET(sock,&rfd))
{ //有一个描述符准备好了
socka=accept(sock,0,0); //一个用来测试读 一个用来测试写
FD_ZERO(&rfd);
FD_ZERO(&wfd);
FD_SET(socka,&rfd);//把socka放入读描述符集
FD_SET(sockb,&rfd);//把sockb放入读描述符集
FD_SET(socka,&wfd);把socka放入写描述符集
FD_SET(sockb,&wfd);把sockb放入写描述符集
if(SOCKET_ERROR!=select(0,&rfd,&wfd,0,0)) //测试这两个描述符集,永不超时 其中rfd只用来测试读 wfd只用来测试写
{ //没有错误
if(FD_ISSET(socka,&rfd)) //socka可读
{...}
if(FD_ISSET(sockb,&rfd) //sockb可读
{...}
if(FD_ISSET(socka,&wfd) //socka 可写
{...}
if(FD_ISSET(sockb,&wfd) //sockb可写
{...}
}
int select(int n,
fd_set * readfds,
fd_set * writefds,
fd_set * exceptfds,
struct timeval * timeout);
#include
#include
#include
#include
#include
#include
int main ()
{
int keyboard;
int ret,i;
char c;
fd_set readfd;
struct timeval timeout;
keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
assert(keyboard>0);
while(1)
{
timeout.tv_sec=1;
timeout.tv_usec=0;
FD_ZERO(&readfd);
FD_SET(keyboard,&readfd);
ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);
if(FD_ISSET(keyboard,&readfd))
{
i=read(keyboard,&c,1);
if('\n'==c)
continue;
printf("hehethe input is %c\n",c);
if ('q'==c)
break;
}
}
}
【我的笔记】tcp/udp的server,如果是非阻塞socket,都不用sleep,tcp/udp server线程也不会占用过多cpu资源;select本身能起到sleep的作用。
【more参考】(我如果贴了原文链接,帖子就显示不正常,我去)
序:前段时间狂看了很多关于网络编程的资料,这里自己总结一下,以便自己以后可以参考。
什么是阻塞socket,什么是非阻塞socket。对于这个问题,我们要先弄清什么是阻塞/非阻塞。阻塞与非阻塞是对一个文件描述符指定的文件或设备的两种工作方式。 阻塞的意思是指,当试图对该文件描述符进行读写时,如果当时没有东西可读或者暂时不可写,程序就进入等待状态,直到有东西可读或者可写为止。 非阻塞的意思是,当没有东西可读或者不可写时,读写函数就马上返回,而不会等待。
现在来理解什么是阻塞socket,什么是非阻塞socket。每个通过socket()函数创建的socket,本质就是一个文件描述符,所以对该文件描述符的IO操作方式不同,就有了阻塞socket和非阻塞socket。 那是不是说阻塞socket下的所以socket api函数都是阻塞的呢,如果你还不能正确的回答这个问题,说明上面简短的说明并没有让你真正的明白什么是阻塞socket和非阻塞socket。这个问题的答案是否定的,为什么是否定的,因为并不是每个socket的api都会涉及到对文件描述符的IO操作。
这里我列举了,哪些socket api会阻塞:
accept,connect,recv(recvfrom),send(sendto),closesocket,select(poll或epoll)
1)accept在阻塞模式下,没有新连接时,线程会进入睡眠状态;非阻塞模式下,没有新连接时,立即返回WOULDBLOCK错误。
【winsock MSDN】The accept function can block the caller until a connection is present if no pending connections are present on the queue, and the socket is marked as blocking. If the socket is marked as nonblocking and no pending connections are present on the queue, accept returns an error as described in the following. After the successful completion ofaccept returns a new socket handle, the accepted socket cannot be used to accept more connections. The original socket remains open and listens for new connection requests.
2)connect在阻塞模式下,仅TCP连接建立成功或出错时才返回,分几种具体的情况,这里不再叙述(通常都是在阻塞模式调用connect);非阻塞模式下,该函数会立即返回INPROCESS错误(需用select检测该连接是否建立成功,有点啰嗦)
On a blocking socket, the return value indicates success or failure of the connection attempt.
With a nonblocking socket, the connection attempt cannot be completed immediately. In this case, connect will return SOCKET_ERROR, andWSAGetLastError will return WSAEWOULDBLOCK. In this case, there are three possible scenarios:
3)recv/recvfrom/send/sendto很好理解,因为这两类函数读写socket文件描述符的接收/发送缓冲区。
4) select/poll/epoll并不是真正意义上的阻塞(也就是说与socket是否阻塞无关),它们的阻塞是由于它们最后一个timeout参数决定的,timeout大于0时,它们会一直等待直到超时才退出(相等于阻塞了吧,^_^),而timeout=-1即永远等待。
5)closesocket也不是真正意义上的阻塞,它其实是指是否等待关闭(相当于阻塞了吧,^_^),它受套接字选项SO_LINGER和SO_DONTLINGER的影响。若SO_DONTLINGER或SO_LINGER的间隔=0时,closesocket就是非等待关闭的,但是当SO_LINGER的间隔>0时,closesoket就是等待关闭的,直到剩余数据都发送完毕或直到超时才退出。(但是这个地方只有对于阻塞的套接口才有用,如果是非阻塞的套接口,它会立即返回并且指示错误WOULDBLOCK)。
最后提供linux下和windows下设置阻塞和非阻塞的几种方式:
【linux】:
int flags = fcntl(listenSock, F_GETFL, 0);
fcntl(listenSock, F_SETFL, flags|O_NONBLOCK);
read/recv函数的最后一个参数也可以设置阻塞或非阻塞方式
【windows】:
ioctlsocket,WSAAsyncselect()和WSAEventselect()
read/recv函数的最后一个参数也可以设置阻塞或非阻塞方式
//ioctlsocket() 设置非阻塞的示例
u_long iMode = 1;
#ifndef WIN32 //Linux OS
#define ioctlsocket(a,b,c) ioctl(a,b,c)
#endif
ioctlsocket(m_sock, FIONBIO, &iMode);
【附录】
【MSDN send】
The successful completion of a send function does not indicate that the data was successfully delivered and received to the recipient. This function only indicates the data was successfully sent.
If no buffer space is available within the transport system to hold the data to be transmitted,send will block unless the socket has been placed in nonblocking mode. On nonblocking stream oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both the client and server computers.The select, WSAAsyncSelect or WSAEventSelect functions can be used to determine when it is possible to send more data.
Calling send with a len parameter of zero is permissible and will be treated by implementations as successful. In such cases,send will return zero as a valid value. For message-oriented sockets, a zero-length transport datagram is sent.
send返回成功,并不代表接收方收到了数据,仅表示已经发出去了。
当socket发送缓存不足时,对于阻塞socket,send会被阻塞住(直到缓存有剩余空间能发出去);对于非阻塞socket,发送的数据长度可能从1~指定发送length,这取决于收/发双方的计算机。select可以用于判断是否能继续发送更多数据。
If no error occurs, recv returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
The recv function is used to read incoming data on connection-oriented sockets, or connectionless sockets. When using a connection-oriented protocol, the sockets must be connected before callingrecv. When using a connectionless protocol, the sockets must be bound before callingrecv.
The local address of the socket must be known. For server applications, use an explicitbind function or an implicit accept or WSAAccept function. Explicit binding is discouraged for client applications. For client applications, the socket can become bound implicitly to a local address usingconnect, WSAConnect, sendto,WSASendTo, or WSAJoinLeaf.
For connected or connectionless sockets, the recv function restricts the addresses from which received messages are accepted. The function only returns messages from the remote address specified in the connection. Messages from other addresses are (silently) discarded.
For connection-oriented sockets (type SOCK_STREAM for example), calling recv will return as much data as is currently available—up to the size of the buffer specified. If the socket has been configured for in-line reception of OOB data (socket option SO_OOBINLINE) and OOB data is yet unread, only OOB data will be returned. The application can use the ioctlsocket orWSAIoctlSIOCATMARK command to determine whether any more OOB data remains to be read.
For connectionless sockets (type SOCK_DGRAM or other message-oriented sockets), data is extracted from the first enqueued datagram (message) from the destination address specified by theconnect function.
If the datagram or message is larger than the buffer specified, the buffer is filled with the first part of the datagram, andrecv generates the error WSAEMSGSIZE.For unreliable protocols (for example, UDP) the excess data is lost; for reliable protocols, the data is retained by the service provider until it is successfully read by callingrecv with a large enough buffer.
If no incoming data is available at the socket, the recv call blocks and waits for data to arrive according to the blocking rules defined forWSARecv with the MSG_PARTIAL flag not set unless the socket is nonblocking. In this case, a value of SOCKET_ERROR is returned with the error code set toWSAEWOULDBLOCK. The select,WSAAsyncSelect, or WSAEventSelect functions can be used to determine when more data arrives.
If the socket is connection oriented and the remote side has shut down the connection gracefully, and all data has been received, arecv will complete immediately with zero bytes received. If the connection has been reset, arecv will fail with the error WSAECONNRESET.
对于阻塞socket,如果socket没来数据,则recv调用会被阻塞,直到有数据到来。Note When issuing a blocking Winsock call, such as send, recv, select, accept, or connect function calls, Winsock may need to wait for a network event before the call can complete. Winsock performs an alertable wait in this situation, which can be interrupted by an asynchronous procedure call (APC) scheduled on the same thread, and thereby create unspecified results. Do not issue blocking Winsock function calls without being certain that the current thread is not waiting inside another blocking Winsock function call; doing so results in unpredictable behavior.
如果你不确定调用阻塞recv的线程中,是否还需要等待其他的Winsock阻塞接口调用,那么请不要使用阻塞recv。如果这种情况下,使用了阻塞recv,可能不正确的返回(没有收到数,被其它异步调用引起的返回)。