【问题标题】:UDP client unable to receive data from the serverUDP客户端无法从服务器接收数据
【发布时间】:2013-03-14 07:09:01
【问题描述】:

我有一个 UDP 客户端和一个 UDP 服务器。程序的流程是:首先从终端执行服务器,终端创建一个套接字并绑定它并等待来自客户端的文件名。在另一个终端中执行客户端。这里还创建了一个套接字,并与服务器建立了连接。然后将文件名提供给客户端。使用 sendto() 函数将此文件名发送到服务器。服务器能够从客户端接收文件名,并且服务器还将文件中的数据发送给客户端。然而,另一端的接收者一直在等待来自服务器的数据。

UDP客户端和服务器的代码如下所示。

UDP 服务器:

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<fcntl.h>
int main()
{
    int cont,create_socket,new_socket,addrlen,fd;
    int bufsize = 1024;
    int nameLen=0;
    int client_address_size=0;
    char *buffer = malloc(10);
    char fname[256];
    struct sockaddr_in address,client;

    if ((create_socket = socket(AF_INET,SOCK_DGRAM,0)) > 0)
    printf("The socket was created\n");

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(15000);

    if (bind(create_socket,(struct sockaddr *)&address,sizeof(address))== 0)
        printf("Binding Socket\n");

    nameLen=sizeof(address);

    if (getsockname(create_socket,(struct sockaddr *)&address,&nameLen)<0)
    {
        printf("\n\ngetsockname() error\n");
        exit(3);
    }

    printf("Port assigned is %d\n", ntohs(address.sin_port));

    client_address_size=sizeof(client);

    if(recvfrom(create_socket,fname, 255,0,(struct sockaddr *) &client,&client_address_size)<0)
    {
        printf("\n\nrecvfrom() failed\n");
        exit(4);
    }

    printf("A request for filename %s Received..\n", fname);

    if ((fd=open(fname, O_RDONLY))<0)
    {
        perror("File Open Failed");
        exit(0);
    }

    while((cont=read(fd, buffer, 10))>0) 
    {
        //sleep(1);
        sendto(create_socket,buffer,cont,0,(struct sockaddr *) &client,&client_address_size);
        printf("\n\nPacket sent\n");
    }

    printf("Request Completed\n");

    return close(create_socket);
}

UDP 客户端:

#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>

int main()
{
    int create_socket,cont;
    char *arg="127.0.0.1";
    int bufsize = 1024;
    int server_address_size=0;
    char *buffer = malloc(10);
    char fname[256];
    struct sockaddr_in address,server;

    if ((create_socket = socket(AF_INET,SOCK_DGRAM,0)) > 0)
        printf("The Socket was created\n");

    address.sin_family = AF_INET;
    address.sin_port = htons(15000);
    address.sin_addr.s_addr=inet_addr(arg);

    if (connect(create_socket,(struct sockaddr *) &address,sizeof(address)) == 0)
        printf("The connection was accepted with the server %s...\n",arg);

    printf("Enter The Filename to Request : ");
    scanf("%s",fname);
    sendto(create_socket, fname, sizeof(fname), 0,(struct sockaddr *) &address,sizeof(address));
    printf("Request Accepted... Receiving File...\n\n");

    server_address_size=sizeof(server);


    printf("The contents of file are...\n\n");

    while((cont=recvfrom(create_socket, buffer, 10, 0,(struct sockaddr *) &address,sizeof(address)))>0) 
    {
        write(1, buffer, cont);
    }

    printf("\nEOF\n");
    return close(create_socket);
}

我哪里出错了?请为此提供适当的解决方案。

提前致谢。

【问题讨论】:

  • 非常糟糕的代码。与其打印成功案例中发生的情况并忽略失败案例,不如建议您检查失败案例并打印errnostrerror 或致电perror(),,以便了解失败情况。目前,任何事情都可能出错,你永远不会知道。当您实际所做的事情被发送时打印诸如“请求接受”之类的东西,或者当您所做的只是创建一个无连接套接字时打印“连接接受”,同样没有意义。
  • 同时编译所有警告,然后修复代码。

标签: c sockets networking udp


【解决方案1】:

您在recvfrom() 中使用了错误的值结果参数。编译器应该非常大声地警告你。 Recvfrom() 将尝试在第 6 个参数中向您返回一个数字,因此您不能将使用 sizeof() 创建的常量传递给它。

来自手册页:

参数 addrlen 是 value-result 参数,调用者应该在调用 与 src_addr 关联的缓冲区的大小,并在返回时修改以指示 源地址的实际大小。

我像这样更改了 recvfrom() 循环,并成功发送和接收了一个文件。

int serv_addr_size = sizeof(address);
while((cont=recvfrom(create_socket, buffer, 10, 0,(struct sockaddr *) &address,&serv_addr_size))>0) 
{
    write(1, buffer, cont);
}

由于您在套接字上调用 connect(),因此您也可以使用 recv(),如下所示:

recv(create_socket, buffer, 10, 0)

作为一般建议,请务必仔细检查系统和库调用的返回值(printf() 除外),并为函数手册页“返回值”下列出的所有情况做好准备。

EDIT 服务器端在另一个方向上犯了类似的错误。 sendto() 的参数应该只是传递的结构的长度,作为指针传递。

sendto(create_socket,buffer,cont,0,(struct sockaddr *) &client,&client_address_size);

应该是

sendto(create_socket,buffer,cont,0,(struct sockaddr *) &client,client_address_size);

【讨论】:

  • 您是否在服务器端进行了任何修改?因为即使在客户端进行了上述更改之后,我也得到了相同的输出。即客户端仍在等待接收数据。
  • 既然你提到了它,我确实删除了 sendto() 中第 6 个参数前面的 &amp; 运算符。这是对编译器警告的自动反射,所以我一开始不记得了。就像您问题上的其他 cmets 所说,代码不是很健壮,您应该仔细检查编译器警告。
  • 谢谢它的工作...还有一件事...收到所有数据后,客户端仍在等待来自服务器的更多数据。我的意思是 EOF 没有被打印出来。请澄清一下。
  • UDP 不是面向连接的协议。当数据结束时,传输没有提供标记。
  • 因此,换句话说,您的文件传输协议必须以某种方式在文件传输时发出信号。我不会为您设计,但流行的替代方案是 1)在传输开始时发送要传输的数据大小,然后在剩余数据时接收 2)扫描接收到的数据以查找某些特殊字符,这意味着传输完成。
【解决方案2】:

让我写一个有效的例子。我试图重写你自己的代码,但我更愿意给你看一个很好的例子。只需几分钟...

---编辑---

好的,我来了。请原谅我花费的时间,但我更愿意仔细阅读代码。

这是你的两个文件:

Server implementation

Client implementation

真的希望我能帮到你。

【讨论】:

  • 好的...我很高兴有一些工作示例。提前致谢。
  • 如果您觉得它有用,请告诉我!
  • 非常感谢...这实际上为改进我的代码和创建更好客户端的新想法提供了一个非常好的想法。我在代码中遇到的一个问题是:您的代码是否可以在通过 LAN 连接的 2 台不同机器上使用,如果可以的话,您的代码需要进行哪些更改? (因为我发布的上述代码仅适用于同一台机器中的客户端服务器)。请同样回复。
  • 第二个问题:服务器在收到他的第一条消息时获取客户端IP地址(它将客户端信息保存在客户端结构中)。然后它尝试打开文件(如果存在)。成功后,它首先发送文件长度,然后发送其内容。对于第一个,我只能说我只在我的 PC 上尝试了我的代码,但我认为可以通过 LAN 来实现。如果您还有疑问,请不要犹豫。
  • UDP 是无连接的,这意味着您不需要像 TCP 那样使用诸如 listen()、accept() 和 connect() 之类的函数。当 UDP 服务器绑定时,它已准备好从每个客户端接收数据,但不能保证所有数据都会到达目的地。这是因为 IP 协议(​​在 UDP/TCP 下)不保证任何安全性,而 UDP 也不保证。我的意思是说,当客户端将他的文件名发送到服务器时,它会存储其地址以与客户端通信。在您的服务器实现中,唯一的(套接字)文件描述符是服务器的。