recv
1
| recv(sockfd,buf,len,MSG_PEEK);
|
MSG_PEEK代表从缓冲区提取数据之后,缓冲区原来的数据不会丢失
相当于复制一份
实现sleep(10)的功能:
1
| select(fd + 1, &NULL, NULL, NULL, &timeout);
|
超时
- 接受客户端连接请求超时(accept)
- 建立连接请求超时(connect)
- 发送请求超时(write,send或sendto)
- 接收请求超时(recv或read)
连接服务端超时实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| int TcpSocket::connectTimeout(sockaddr_in *addr, unsigned int wait_seconds) { int ret; socklen_t addrlen = sizeof(struct sockaddr_in);
if (wait_seconds > 0) blockIO(m_socket);
ret = connect(m_socket, (struct sockaddr*)addr, addrlen); if (ret < 0 && errno == EINPROGRESS) { //printf("11111111111111111111\n"); fd_set connect_fdset; struct timeval timeout; FD_ZERO(&connect_fdset); FD_SET(m_socket, &connect_fdset); timeout.tv_sec = wait_seconds; timeout.tv_usec = 0; do { // 一但连接建立,则套接字就可写 所以connect_fdset放在了写集合中 ret = select(m_socket + 1, NULL, &connect_fdset, NULL, &timeout); } while (ret < 0 && errno == EINTR); if (ret == 0) { ret = -1; errno = ETIMEDOUT; } else if (ret < 0) return -1; else if (ret == 1) { //printf("22222222222222222\n"); /* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/ /* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */ int err; socklen_t socklen = sizeof(err); int sockoptret = getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &err, &socklen); if (sockoptret == -1) { return -1; } if (err == 0) { //printf("3333333333333\n"); ret = 0; } else { //printf("4444444444444444:%d\n", err); errno = err; ret = -1; } } } if (wait_seconds > 0) { noBlockIO(m_socket); } return ret; }
|
发送数据超时检测实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| /* * writeTimeout - 写超时检测函数,不含写操作 * @wait_seconds: 等待超时秒数,如果为0表示不检测超时 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT */ int TcpSocket::writeTimeout(unsigned int wait_seconds) { int ret = 0; if (wait_seconds > 0) { fd_set write_fdset; struct timeval timeout;
FD_ZERO(&write_fdset); FD_SET(m_socket, &write_fdset);
timeout.tv_sec = wait_seconds; timeout.tv_usec = 0; do { ret = select(m_socket + 1, NULL, &write_fdset, NULL, &timeout); } while (ret < 0 && errno == EINTR);
if (ret == 0) { ret = -1; errno = ETIMEDOUT; } else if (ret == 1) ret = 0; }
return ret; }
|
读超时检测实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| /* * readTimeout - 读超时检测函数,不含读操作 * @wait_seconds: 等待超时秒数,如果为0表示不检测超时 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT */ int TcpSocket::readTimeout(unsigned int wait_seconds) { int ret = 0; if (wait_seconds > 0) { fd_set read_fdset; struct timeval timeout;
FD_ZERO(&read_fdset); FD_SET(m_socket, &read_fdset);
timeout.tv_sec = wait_seconds; timeout.tv_usec = 0;
//select返回值三态 //1 若timeout时间到(超时),没有检测到读事件 ret返回=0 //2 若ret返回<0 && errno == EINTR 说明select的过程中被别的信号中断(可中断睡眠原理) //2-1 若返回-1,select出错 //3 若ret返回值>0 表示有read事件发生,返回事件发生的个数
do { ret = select(m_socket + 1, &read_fdset, NULL, NULL, &timeout);
} while (ret < 0 && errno == EINTR);
if (ret == 0) { ret = -1; errno = ETIMEDOUT; } else if (ret == 1) ret = 0; }
return ret; }
|