Linux socket:如何使send()等待recv()

use*_*995 9 c sockets send recv

我正在使用TCP协议创建一个简单的客户端 - 服务器应用程序.

我知道默认情况下.recv()将阻塞,直到另一方调用send()此套接字.但是有可能send()阻塞自己直到另一方recv()编辑了msg而不是保持send()传出队列,然后发现另一方recv()获得了由多个send()s 发送的一大堆msg

换一种说法.是否有可能让send()对方recv()在等到对方之前等待对方send()

说清楚我的问题.我将在这里发布一个简单的代码:

client.c

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <poll.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h> 

int main(int argc, char *argv[])
{
    int sockfd = 0;
    char sendBuff[1024];
    struct sockaddr_in serv_addr;
    int i;

    if(argc != 2)
    {
        printf("\n Usage: %s <ip of server> \n",argv[0]);
        return 1;
    } 

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Error : Could not create socket \n");
        return 1;
    } 

    memset(&serv_addr, '0', sizeof(serv_addr)); 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(5000); 

    if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
    {
        printf("\n inet_pton error occured\n");
        return 1;
    } 

    if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        printf("\n Error : Connect Failed \n");
        return 1;
    }

    do{
        memset(sendBuff, '\0', sizeof(sendBuff));
        sprintf(sendBuff, "This is line %d", i);
        send(sockfd, sendBuff, strlen(sendBuff), 0);
        //sleep(1);
    }while(++i<100);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

server.c

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h> 

int main(int argc, char *argv[])
{
    int listenfd = 0, connfd = 0;
    struct sockaddr_in serv_addr; 

    char sendBuff[1025];
    char recvBuff[100];

    int i = 0;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&serv_addr, '0', sizeof(serv_addr));
    memset(sendBuff, '0', sizeof(sendBuff)); 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(5000); 

    bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 

    listen(listenfd, 10); 

    connfd = accept(listenfd, (struct sockaddr*)NULL, NULL); 

    do{  
        memset(recvBuff, '\0', sizeof(recvBuff));
        recv(connfd, recvBuff, sizeof(recvBuff),0);
        printf( "%s\n", recvBuff);
    }while(++i<100); 

    return 0;
} 
Run Code Online (Sandbox Code Playgroud)

我期望服务器端的结果是打印:

This is line 0
This is line 1
This is line 2
This is line 3
...
Run Code Online (Sandbox Code Playgroud)

但是,实际结果是这样的:

This is line 0
This is line 1This is line 2This is line3This is line 4
This is line 5This is line 6This is line 7This is line 8This is line 9This is line 10
This is line 11This is line 12...
Run Code Online (Sandbox Code Playgroud)

然而,这很容易解释:当客户端发出a时send(),它没有等待服务器端recv()完成,并且由于某种原因,服务器端recv()循环比客户端慢send().因此send(),客户端上的几个可以堆叠在一起并由服务器作为整体接收.(我的解释是对的吗?)

实际上似乎有一个非常愚蠢和松散的解决方案.只需在循环中添加一个sleep(1)(我注释掉)send().我知道这将使代码效率非常低,并且如果recv()循环将花费更长的时间来实现一些其他复杂的操作(当程序变大时这显然是不可预测的),这将花费超过1秒.此解决方案失败!

那么有没有更好的方法让双方互相沟通,以确保单个send()收到的一个单一的msg发送recv()

ric*_*ici 7

发送方知道接收方已收到消息的唯一方法是让接收方回复.

换句话说,如果要保持发送方和接收方同步,则需要实现自己的显式流控制.

确实,TCP协议要求接收器确认收到的数据包,因此您可能认为接收器可能只是询问TCP实现数据是否已被确认.这通常是可能的,虽然我不相信有一种跨平台的方式.例如,在Linux上,您可以使用ioctl代码SIOCOUTQ检查出站缓冲区的大小.但据我所知,在出站缓冲区为空之前没有阻塞机制,所以你能做的最好就是定期轮询.这不太令人满意.

在任何情况下,结果都会产生误导,因为接收端也有一个缓冲区,如果处理是瓶颈,接收缓冲区将是第一个填满的.低级数据确认仅指示数据已到达接收TCP堆栈(即,低级IP接口,通常在内核内).它并不表示数据已由接收进程处理,并且发送方没有机制来查询接收缓冲区的使用情况.

此外,等待确认消息到达发送端通常会不必要地降低通信速度,因为它为每个事务添加了传输延迟(接收器→发送器).这并不像你的sleep解决方案那么引人注目,但它有点类似.

如果您的消息非常短,您可能会遇到Nagle算法,这会导致发送方在很短的时间内保留小数据包,希望可以合并多个数据包.你可以把Nagle算法关闭使用TCP连接setsockopt,与TCP_NODELAY选项.但除非你确定它有用,否则不要这样做.(规范使用的TCP_NODELAY是telnet类型连接,其中数据由单独的击键组成.)


rav*_*avi 6

client.c

while(condition)
{
  send() from client;
  recv() from server;
}
Run Code Online (Sandbox Code Playgroud)

server.c

recv() from client;
while(condition)
{
  send() from server; //acknowledge to client
  recv() from client;
}
Run Code Online (Sandbox Code Playgroud)