【问题标题】:Client and Server send() and recv() in CC 中的客户端和服务器 send() 和 recv()
【发布时间】:2021-05-24 05:27:15
【问题描述】:

这是我第一次进行socket编程,有一个关于send和recv的问题。我正在从服务器向客户端发送一个文件,它工作正常。但是,当我想通过更多的 send() 和 recv() 调用(客户端和服务器末尾的注释代码)继续我的程序时,客户端会收到此数据并将其写入 newfile.txt。

所以,我的问题是,如何从服务器调用 send() 仅从 test.txt 发送数据,然后在客户端调用 recv() 仅从 test.txt 接收数据并将其写入 newfile.txt ?在此之后,我想继续我的程序,使用更多的 send() 和 recv() 调用,这些调用不会与文件传输代码混淆。

客户:

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

int main(int argc, char *argv[]){
    int port = 8001;
    int client_socket;
    struct sockaddr_in server_addr;
    int connect_status;

    client_socket = socket(AF_INET, SOCK_STREAM, 0);    //SOCK_STREAM for TCP
    if (client_socket < 0)
    {
        printf("Error creating socket\n");
        exit(1);
    }
    printf("Socket created\n");

    //address for socket to connect to
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);             //8001 arbitrary port
    server_addr.sin_addr.s_addr = INADDR_ANY;

    //connect to address of socket
    connect_status = connect(client_socket, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if(connect_status == -1)
    {
        printf("Error connecting to server\n");
        exit(1);
    }
    printf("Connected to Server\n");




    FILE* fp = fopen( "newfile.txt", "w");
    char data[512];
    int b;

    while((b=recv(client_socket, data, 512,0))>0){
        fwrite(data, 1, b, fp);
    }
    fclose(fp);


    //recv(client_socket, data, 512,0);
    //printf("client buffer: %s\n", data);

    close(client_socket);
    return 0;

}

服务器:

#include <stdio.h>
#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>

int main(int argc, char *argv[]){
    int port = 8001;
    int server_socket;
    struct sockaddr_in server_addr;
    int client_socket, client_addr;
    int bind_status, listen_status;


    server_socket = socket(AF_INET, SOCK_STREAM, 0);    //SOCK_STREAM for TCP
    if (server_socket < 0)
    {
        printf("Error creating socket\n");
        exit(1);
    }
    printf("Socket created\n");

    //address for socket to connect to
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    //bind socket to the IP and port
    bind_status = bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (bind_status < 0)
    {
        printf("Error binding to socket\n");
        exit(1);
    }
    printf("Socket binded\n");

    //listen for client connections
    listen_status = listen(server_socket, 10);
    if (listen_status == -1)
    {
        printf("Error listening to socket\n");
        exit(1);
    }
    printf("Listening\n");

    //accept client connection
    socklen_t addr_len;
    addr_len = sizeof(client_socket);
    client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &addr_len);




    FILE *fp = fopen("test.txt", "r");
    char data[512];
    int b;

    while((b = fread(data, 1, 512, fp))>0){
        send(client_socket, data, b, 0);
    }
    fclose(fp);


   // strcpy(data,"test message");
    //printf("server buffer: %s\n", data);
    //send(client_socket, data, 512, 0);

    close(server_socket);
    return 0;
}

【问题讨论】:

  • 您必须有一个基于 TCP 的协议,它可以安全地识别文件数据的开始和结束。结束后的数据可以被.....whatever消费。
  • 注意 - 你非常接近从悬崖上掉下来:'strcpy(data,"test message");' - 滥用需要以 NUL 结尾的字符数组的库调用。我怀疑您还在为滥用 TCP 八位字节流而步履蹒跚 - 假设在一次 send() 调用中发送的多字节消息将始终导致一个 recv() 调用仅加载完整的消息。
  • 您确实应该检查来自 send / recv 的返回值 - 即使您仅发送 512 字节大小的块,它们也可能仅发送部分数据,从而降低可能性。您不会在本地网络上遇到此问题,但如果您不测试错误,您可能会发现自己很难跟踪问题。
  • 我强烈建议您研究基于 TCP 的协议规范,了解它们如何处理此类问题。查看 IRC、HTTP、SMTP、NNTP、FTP 等协议。他们都必须处理正确使用 TCP 的所有问题。

标签: c sockets


【解决方案1】:

您需要某种方式来指示服务器您已经发送完一个文件,现在您想发送另一个东西。

虽然套接字抽象似乎向您展示了 recv 和 send 调用以某种方式同步,但这意味着您在一次调用 send 中从客户端发送的数据在服务器中 recv'd 仅具有一个 recv,即不正确,从根本上是由于套接字是如何实现的(客户端 tcp 可以决定将您的传输拆分为多个数据包,并且服务器的无人参与可以使所有这些数据包在服务器接收其中的一部分之前缓冲接收器在文本调用中调用 recve 和其他人。

唯一可以确定的是之前发送过的字节,之前接收过的字节,中间没有重复或丢失的字节。但是在某些 recve 调用中接收到的字节数仅取决于连接一侧的缓冲数据量。

这意味着,为了告诉服务器您已完成文件并要发送其他内容,您必须做一些事情,让服务器能够识别数据已经结束,并且关于不同事物的更多数据是即将到来。

这里有几种方法...您可以发送一个带内序列(一些控制序列),当在另一端读取时,它将被识别为数据块的结尾和下一个数据块的开头。一些系统使用分组编码,它只是在每个块前面加上要跟随的字节数,而一个空分组(例如,单个数字 0,意味着要跟随 0 个字节)可以表示这个序列。另一种方法,可以是使用一个你知道不可能出现在数据中的特定序列(例如\n.\m,两个换行,一个点穿插——这已经在 unix 的 ed 编辑器中使用过很多次,例如,或者邮局协议使用它。)如果是这样的序列恰好在文件中,只需将点加倍,以表明它不是分隔符序列。并且两端必须就这个(所谓的)协议达成一致。

其他机制也是有效的,你可以关闭连接并使用另一个套接字进行新的数据交换....例如,FTP协议使用这种方法,将一个控制连接用于FTP命令,同时使用其他连接进行数据传输.

我没有包含代码,因为文献中有大量示例。尝试访问 Richard W. Stevens 的“Unix Network Programming”一书。这是开始学习套接字协议以及如何处理所有这些事情的一个很好的参考。

【讨论】:

    猜你喜欢
    • 2013-03-29
    • 2018-01-20
    • 2020-06-19
    • 2021-01-30
    • 1970-01-01
    • 1970-01-01
    • 2015-10-24
    • 2013-11-12
    • 1970-01-01
    相关资源
    最近更新 更多