recv是网络编程中用于TCP传输接收数据的接口,由于工作中一直没有接触到网络编程,虽然知道这个函数,但是没有使用经验,所有对接口细节不是很了解。
直到前阵子在项目中调试第三方的源码时,跟踪到这个函数,才发现它的返回值和我想象的不太一样。
recv函数的原型:
ssize_t recv(int sockfd, void* buf, size_t len, int flags);
之前知道的是如果读取成功,会返回实际读取到的字节数,但是如果读取不到数据呢?一直想当然的认为会返回0,其实并不是,当读取不到数据的时候,返回的是-1,错误码是11,对应的含义是:Resource temporarily unavailable,这里的资源自然是指要读取的数据,因为没有数据可以读取,所以资源不可用。
那么返回值为0表示什么意思?答案是当对端关闭了socket的时候(是真正地关闭,会引发四次握手,而不是减少引用),我们再去调用recv读取数据,会返回0。
下面是测试代码,连接建立后,服务端会网socket发送数据,每次发送后sleep5秒,当发送四次数据后,关闭socket。为了测试在没有数据可读的情况下调用recv的返回值,我们在客户端把socket设置为非阻塞模式,当服务端在sleep时,socket没有数据可读,调用recv后会返回-1,打印出来的错误码是11,当服务端关闭了socket后,再次调用recv则返回0。
服务端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAX_OF_LISTEN_QUEUE 10
#define MAXBUF 1024
int main(){
int sockFd;
int destSockFd;
struct sockaddr_in source;
struct sockaddr_in dest;
char buff[MAXBUF+1];
memset(&source, 0, sizeof(source));
source.sin_family = AF_INET;
source.sin_port = htons(8235);
source.sin_addr.s_addr = inet_addr("192.168.1.109");
sockFd = socket(AF_INET, SOCK_STREAM, 0);
if(sockFd < 0){
cout << "created server socket error!" << endl;
}
if(bind(sockFd, (struct sockaddr*)&source, sizeof(source)) != 0){
cout << "server bind error!" << endl;
}
if(listen(sockFd, MAX_OF_LISTEN_QUEUE) != 0){
cout << "server listen error!" << endl;
}
socklen_t len = sizeof(dest);
while(1){
destSockFd = accept(sockFd, (struct sockaddr*)&dest, &len);
if(destSockFd == -1){
cout << "server accept error!" << endl;
}
else{
cout << "server accept a connection!" << endl;
break;
}
}
int hasSend;
strcpy(buff, "This message from server!");
int count = 0;
while(1){
count++;
if(count > 4)
break;
sleep(5);
cout << "server sending message!" << endl;
hasSend = send(destSockFd, buff, strlen(buff), 0);
if(hasSend == -1){
cout << "server send message error!" << endl;
}
if(hasSend == strlen(buff)){
cout << "server send OK!" << endl;
}
else{
cout << "server only send " << hasSend << "bytes!" << endl;
}
}
close(destSockFd);
cout << "server will exit!" << endl;
}
客户端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAXSLEEP 128
#define MAXBUF 1024
int main(){
int sockFd;
struct sockaddr_in dest;
struct sockaddr_in source;
char buff[MAXBUF+1];
sockFd = socket(AF_INET, SOCK_STREAM, 0);
if(sockFd < 0){
cout << "created client socket error!" << endl;
}
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(8235);
dest.sin_addr.s_addr = inet_addr("192.168.1.109");
memset(&source, 0, sizeof(source));
source.sin_family = AF_INET;
source.sin_port = htons(8234);
source.sin_addr.s_addr = inet_addr("192.168.1.109");
if(bind(sockFd, (struct sockaddr*)&source, sizeof(source)) != 0){
cout << "client bind error!" << endl;
}
//use exponential backoff
int nsec;
for(nsec = 1; nsec <= MAXSLEEP; nsec <<= 1){
if(connect(sockFd, (struct sockaddr*)&dest, sizeof(dest)) == 0){
cout << "connect to server successfully, will break" << endl;
break;
}
if(nsec <= MAXSLEEP/2){
cout << "connect to server error, will sleep " << nsec << "seconds!" << endl;
sleep(nsec);
}
}
int flag = fcntl(sockFd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(sockFd, F_SETFL, flag);
int recvLen;
//setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
while(1){
cout << "client recving message!" << endl;
recvLen = recv(sockFd, buff, MAXBUF, 0);
if(recvLen == -1){
cout << "client recv message error!" << endl;
if(errno == 11)
cout << "no data to read!" << endl;
}
else if(recvLen == 0){
cout << "server has close!" << endl;
break;
}
else{
buff[25] = '\0';
cout << "client recv " << recvLen << " bytes, message is:" << buff << endl;
}
sleep(2);
}
close(sockFd);
cout << "client will exit!" << endl;
}