recv 返回值大于0,但是缓冲区中无数据的问题

在Linux环境中,一个服务端使用poll管理输入输出事件,而客户端socket设置为非阻塞并使用poll监听。然而遇到问题:客户端poll在缓冲区无数据时未阻塞,recv函数返回值大于0但接收的buffer长度为0,导致数据无法正常读取。服务端代码展示了如何初始化和管理socket,而客户端代码显示了连接、发送和接收报文的过程,但遇到连接中断和数据接收异常的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.项目介绍:

在linux系统下,实现服务端和客户端的简单通信:

1.1 服务端采用的是poll机制实现对输入输出事件进行管理

1.2 客户端socekt设置成了非阻塞;并且在接收函数出采用poll机制监听socket;

2.问题介绍

问题1:客户端poll机制监听socket时,会不断的监听成功;

理论上:

接收缓冲区的  “第一个这是第1个超级女生,编号001。” 这段数据被recv后,poll应该阻塞(因为缓冲区中已经没有数据),直到收到"第一个这是第2个超级女生,编号002"才停止阻塞;

实际上:

问题2:poll函数在缓冲区没数据时并没有阻塞; 而且recv在缓冲区没数据时不断接收数据;

2.2  recv函数返回值>0 但是recv(socket,buffer,10,0);但是用于接收缓冲区中数据的buffer的长度却为0,没有任何数据;

理论上:

缓冲区如果没有数据可读,返回值应该小于0,错误代码除了errno==EAGAIN的情况,其余错误都应该退出;

实际上:

recv返回值大于0,但是没有任何数据被读取到;

服务端代码如下:


#include<stdio.h>
#include<string.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<sys/fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include <poll.h>
#define MAXSIZE 1024

int initserver(int port);               //初始化服务端端口号

int main(int argc,char *argv[])
{
        //帮助文档
        if(argc!=2)
        {
                printf("Using:./tcppoll port\n");
                printf("Example:/project/public/socket1/tcppoll 5050\n");
                printf("本程序演示poll模型的特点:监听事件的发生。\n\n\n");
                return -1;
        }

        //初始化端口
        int listenfd=initserver(atoi(argv[1]));
        printf("listenfd=%d\n",listenfd);

        if(listenfd<0){printf("initserver() failed.\n");return -1;}

        //定义pollfd结构体      poll函数用该结构体接收是否有事件的发生
        struct pollfd pfds[MAXSIZE];

        //初始化结构体
        for(int ii=0;ii<MAXSIZE;ii++)
        {
                pfds[ii].fd=-1;                 //该值为-1时,相应的字段事件将被忽虑,然后reevent返回值是0;
        }
       

        //把监听listenfd的socket和读事件添加到数组中
        pfds[listenfd].fd=listenfd;
        pfds[listenfd].events=POLLIN;
        pfds[listenfd].revents=0;

        int maxfd=listenfd;             //最大的socket

        //用poll模型实现监听多个socket的动作
        while(1)
        {

                int iret=poll(pfds,maxfd+1,-1);        //poll机制监听事件 iret记录其返回值

                if(iret<0){perror("poll() error.");break;}        //<0 调用函数失败

                if(iret==0){printf("poll timeout.");continue;}        //设置的超时时间内没发生任何事情,我们的poll函数没有设置超时时间

                //iret>0表示有事件发生,查看发生事件的socket
                for(int eventfd=0;eventfd<=maxfd;eventfd++)        //遍历被监听的socket
                {
                        if(pfds[eventfd].fd==-1)continue;

                        if( (pfds[eventfd].revents&POLLIN)==0)continue;

                        //若发生的事件的socket为listenfd,则表示有新的客户端连接上来
                        if(eventfd==listenfd)
                        {

                                //为客户端的连接建立通信socket
                                struct sockaddr_in sock;
                                socklen_t socklen=sizeof(sock);
                                int clientsock=accept(listenfd,(struct sockaddr*)&sock,&socklen);
                                if(clientsock<0){ perror("accept() failed");continue; }

                                printf("accept client(socket=%d) ok.\n",clientsock);

                                //将该socket加入poll的结构体中

                                pfds[clientsock].fd=clientsock;
                                pfds[clientsock].events=POLLIN;
                                pfds[clientsock].revents=0;


                                //每次新增一个socket,都要与原maxfd比较看哪个最大
                                if(maxfd<clientsock)maxfd=clientsock;
                        }
                        else
                        {
                               // printf("有数据可读取.\n");
                                char buffer[1024];memset(buffer,0,sizeof(buffer));
                                //客户端发送的数据可读  

                              if(recv(eventfd,buffer,sizeof(buffer),0)<=0)
                                {
                                        printf("client(socket=%d) disconnect.\n",eventfd);
                                        close(eventfd);                         //关闭socket
                                        pfds[eventfd].fd=-1;

                                        //检验断开连接的socket是否是最大socket
                                        if(eventfd==maxfd)
                                        {
                                                for(int ii=maxfd;ii>=0;ii--)
                                                {
                                                        if(pfds[ii].fd==-1)continue;
                                                        maxfd=ii;break;
                                                }
                                        }
                                }
                                else
                                {

                                        //发送报文给对端
                                        printf("recv(client=%d)=%s\n",eventfd,buffer);
                                        send(eventfd,buffer,sizeof(buffer),0);
                                }
                        }
                }

        }


        return 0;

}

int initserver(int port)                //初始化服务端端口号
{
        int listenfd;                   //监听socket
        if( (listenfd=socket(AF_INET,SOCK_STREAM,0))==-1 ){ perror("socket() failed."); return -1;}

        int opt = 1; unsigned int len = sizeof(opt);
        setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,len);

         // 第2步:把服务端用于通讯的地址和端口绑定到socket上。
        struct sockaddr_in servaddr;    // 服务端地址信息的数据结构。
        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;  // 协议族,在socket编程中只能是AF_INET。
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);          // 任意ip地址。
        servaddr.sin_port = htons(port); // 指定通讯端口。
        if ( bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr) ) != 0 )
        { perror("bind() failed.\n"); close(listenfd); return -1; }

        // 第3步:把socket设置为监听模式。
        if (listen(listenfd,5) != 0 ) { perror("listen() failed.\n"); close(listenfd); return -1; }

        return listenfd;
}
                                 

客户端代码:
 

#include<poll.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include <errno.h>
int main(int argc,char *argv[])
{
  if (argc!=3)
  {
    printf("Using:./demo0101 ip port\nExample:./demo0101 127.0.0.1 5005\n\n"); return -1;
  }

  // 第1步:创建客户端的socket。
  int sockfd;
  if ( (sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) { perror("socket"); return -1; }

  // 第2步:向服务器发起连接请求。
  struct hostent* h;
  if ( (h = gethostbyname(argv[1])) == 0 )   // 指定服务端的ip地址。
  { printf("gethostbyname failed.\n"); close(sockfd); return -1; }
  struct sockaddr_in servaddr;
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons(atoi(argv[2])); // 指定服务端的通讯端口。
  memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);


  fcntl(sockfd,F_SETFL,O_NONBLOCK);   //FSETFL设置sockfd描述符为O_NONBLOCK参数 意思是把socket描述符设置成非阻塞状态   
  int ret;
  if (ret=(connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr))) != 0)  // 向服务端发起连接清求。
  {

      if(errno==EINPROGRESS);                //连接正在进行中,啥也不干
      else
         { perror("connect"); close(sockfd); return -1; }
//    printf("ret=%d,error=%d,EINPROGRESS=%d\n",ret,errno,EINPROGRESS); 
  }

  int iret;
  char buffer[1024];

       

  // 第3步:与服务端通讯,发送一个报文后等待回复,然后再发下一个报文。
  for (int ii=0;ii<10;ii++)
  {
    memset(buffer,0,sizeof(buffer));
    sprintf(buffer,"这是第%d个超级女生,编号%03d。",ii+1,ii+1);


    if ( (iret=send(sockfd,buffer,strlen(buffer),0))<=0) // 向服务端发送报文。
    { perror("send"); break; }
    printf("发送:%s\n",buffer);

    memset(buffer,0,sizeof(buffer));

        //poll函数监听socket读事件,非阻塞状态下函数返回快,防止错误退出
    struct pollfd pfd;
    pfd.fd=sockfd;
    pfd.events=POLLIN;                
    pfd.revents=0;

   int iret0;

    while(1)
    {
        
        iret0=poll(&pfd,1,-1);

        if(iret0<0)break;

        if(iret0==0)continue;
        
  
           //     printf("iret0=%d,QIAN_revents=%d\n",iret0,pfd.revents);
             

         if(pfd.revents&POLLIN==0)continue;        //查看返回时间是否为空
         memset(buffer,0,sizeof(buffer));

                
         if ( (iret=recv(sockfd,buffer,10,0))<=0) // 接收服务端的回应报文。每次接收10个字节
         {
                  if(EAGAIN==errno)break;        //非阻塞太快无数据可读
                  else {printf("iret=%d,errno=%d,EAGAIN=%d\n",iret,errno,EAGAIN); break;}
          }
               

          /*
          if ( (iret=recv(sockfd,ptr,10,0))<=0) // 接收服务端的回应报文。
          {
             if(EAGAIN==errno);
              else {printf("iret=%d,errno=%d,EAGAIN=%d\n",iret,errno,EAGAIN); break;}
           }

            //ptr=ptr+iret;

           /printf("接收:%s\n",ptr);
           */

               
           printf("接收:%s(strlen=%d---iret=%d)\n",buffer,strlen(buffer),iret);

        }
    }

    sleep(1);  // 每隔一秒后再次发送报文。
  }

  // 第4步:关闭socket,释放资源。
  close(sockfd);
}

服务端运行结果: 出现clients(client=4)disconnect的原因是我在客户端使用了crtl+c终止了客户端。

客户端运行结果:

没有数据poll没有阻塞,没有数据recv也没有退出,客户的接收的buffer输出为空,长度为0,但是recv返回值却为10>0,

 我想问一下有没有知道原因的同学,求告知;

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值