什么';使用tcp套接字时的数据转换模式是什么?

什么';使用tcp套接字时的数据转换模式是什么?,c,sockets,C,Sockets,我有两个服务器代码: 第一个服务器:每次向客户端发送一个字符,直到字符串完成 int main(int argc, char **argv) { int listenfd, connfd; struct sockaddr_in servaddr; char buff[MAXLINE]; time_t ticks; char

我有两个服务器代码:

  • 第一个服务器:每次向客户端发送一个字符,直到字符串完成

    int
    main(int argc, char **argv)
    {
        int                 listenfd, connfd;
        struct sockaddr_in  servaddr;
        char                buff[MAXLINE];
        time_t              ticks;
            char                            temp[1];
            int                             i = 0;
    
        listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family      = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port        = htons(9999); /* daytime server */
    
        Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    
        Listen(listenfd, LISTENQ);
    
        for ( ; ; ) {
            connfd = Accept(listenfd, (SA *) NULL, NULL);
    
            ticks = time(NULL);
            snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
    
            for(i = 0; i < strlen(buff); i++)
            {
                temp[0] = buff[i];
                Write(connfd, temp, strlen(temp));
            }
    
            Close(connfd);
        }
    }
    
  • 客户端:接收服务器发送的字符

    int
    main(int argc, char **argv)
    {
        int                 sockfd, n;
        char                recvline[MAXLINE + 1];
        struct sockaddr_in  servaddr;
            int count = 0;
    
        if (argc != 2)
            err_quit("usage: a.out <IPaddress>");
    
        if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            err_sys("socket error");
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port   = htons(9999);  /* daytime server */
        if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
            err_quit("inet_pton error for %s", argv[1]);
    
        if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
            err_sys("connect error");
    
        while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
            recvline[n] = 0;    /* null terminate */
                    count++;
            if (fputs(recvline, stdout) == EOF)
                err_sys("fputs error");
        }
        if (n < 0)
            err_sys("read error");
            printf("read time:%d\n", count);
    
        exit(0);
    }
    
    int
    主(内部argc,字符**argv)
    {
    int-sockfd,n;
    char recvline[MAXLINE+1];
    servaddr中的结构sockaddr_;
    整数计数=0;
    如果(argc!=2)
    err_quit(“用法:a.out”);
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
    err_sys(“套接字错误”);
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sinu port=htons(9999);/*日间服务器*/
    if(inet\u pton(AF\u inet,argv[1],&servaddr.sin\u addr)0){
    recvline[n]=0;/*null终止*/
    计数++;
    if(fputs(接收线、标准输出)=EOF)
    错误系统(“fputs错误”);
    }
    if(n<0)
    错误系统(“读取错误”);
    printf(“读取时间:%d\n”,计数);
    出口(0);
    }
    
  • 结果是变量count的输出均为1。我的问题是为什么第一台服务器的输出是1,我认为第一台服务器的结果应该是strlen(buff)


    PS:我在同一台机器上运行服务器和客户端。

    TCP是一种流协议。因此,一侧的写入次数不会导致另一侧的读取量相同,因为协议不会保留有关如何写入套接字的信息

    通常,在发送方,在发送数据包之前会有一个延迟,以防您向套接字写入更多数据,从而可以将更多数据填充到同一个数据包中。其中一个原因是,写得不好的服务器可能会用单字节数据包充斥网络

    在接收方,协议不知道为什么您的数据可能作为单独的数据包到达,它可能因为MTU而被拆分,它可能在途中被一些数据包检查软件或设备重新组装,因此,无论您何时从套接字读取数据,它都会提供尽可能多的数据,而不管数据是如何发送给您的


    在本地计算机上,如在您的设置中,客户端可能在服务器写入时甚至没有运行,因此即使在发送方没有缓冲,它也不会开始读取,直到服务器写入所有内容,因此它将一次性读取所有内容。或者不是,你可能很不幸,你的服务器被抢占了足够长的时间,以至于你内核中的TCP实现认为你将不再发送任何数据,只向客户端发送一个字节,客户端被安排在服务器再次运行之前运行,并且客户端在第一次读取时只会收到一个字节。

    第一台服务器中存在缓冲区溢出:
    char temp[1]必须是
    字符温度[2];温度[1]=0
    或者必须将strlen(temp)
    替换为1。一个更重要的原因是很难正确使用缓冲,大多数应用程序都从中获益,因此缓冲是默认设置。要获得无缓冲输出,您可以
    flush()
    套接字,但取决于数据量和物理传输,您将通过有线传输大量、大量字节的报头,其中只有几个字节的有效负载加上数据包之间的延迟加上其他开销。缓冲是一个实现细节。主要的一点是,它是一个流协议,没有数据包边界,因此读写次数可能不一样,并且与流的实现方式无关(有或没有缓冲)。它甚至可能不会在发送端缓冲任何东西,而单次读取是接收方在一次系统调用中处理所有数据包的副作用。我的经验是,缓冲是整个过程中最重要的“细节”,因为这是大多数人都会绊倒的一件事,因为他们不希望它发生(或以不同的方式发生)因此,是的,它是流式传输,并且在其下面使用数据包,但要使用它,首先需要考虑缓冲。那么有没有办法让tcp套接字每次发送一个字符的数据?是的,有,或者至少有一种方法可以告诉tcp实施不要缓冲,在写入数据后立即发送数据。
    tcp\u节点AY
    socket选项在某些实现中可用(尽管它也有其他功能,但在使用之前请阅读您的系统手册,如果您确实需要,请仔细考虑)。但不能保证您一次只能收到一个字符。一般来说,我不建议对交互式TCP会话以外的任何其他会话使用类似的选项,因为这基本上会使您的TCP流行为不正常,从而导致延迟略有增加。
    int
    main(int argc, char **argv)
    {
        int                 sockfd, n;
        char                recvline[MAXLINE + 1];
        struct sockaddr_in  servaddr;
            int count = 0;
    
        if (argc != 2)
            err_quit("usage: a.out <IPaddress>");
    
        if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            err_sys("socket error");
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port   = htons(9999);  /* daytime server */
        if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
            err_quit("inet_pton error for %s", argv[1]);
    
        if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
            err_sys("connect error");
    
        while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
            recvline[n] = 0;    /* null terminate */
                    count++;
            if (fputs(recvline, stdout) == EOF)
                err_sys("fputs error");
        }
        if (n < 0)
            err_sys("read error");
            printf("read time:%d\n", count);
    
        exit(0);
    }