Linux网络编程:recv的返回值

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 <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <netdb.h>

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 <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <netdb.h>
#include <sys/select.h>
#include <fcntl.h>

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;
}

这里写图片描述

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值