c - Linux 上的 UDP 套接字;发送成功但无法接收大缓冲区

标签 c sockets

我有两台机器,我希望它们都能在 C 编程语言下使用套接字进行通信。

我开发了两个样本来代表双方,但我注意到如果它们小于某个数字,我就可以成功发送数据。

我测试过但不起作用的大小是发送和接收 2048 字节,相反,对于其他较小的大小,例如 258 字节、1KByte,它工作正常。

经过一番调查,我发现发送操作没有错误,而接收操作却什么也没得到。

我已经检查了两台机器上的发送和接收缓冲区大小,我想它们是足够的。

这是我的代码的第一面:

/* UDP client in the internet domain */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#define BUFF_SIZE               1800
#define MOBIPASS_L2_IP_ADDRESS  "192.168.13.53"
#define MOBIPASS_L2_PORT_NUM    12001

#define BRIDGE_IP_ADDRESS       "192.168.13.30"
#define BRIDGE_PORT_NUM         12000

#define RESTRICT_SRC_DST_NUM        1
#define TEN_MSEC                    10000

void error(const char *);
void adjustSockParam (int sockFD);

int main(int argc, char *argv[])
{
#if RESTRICT_SRC_DST_NUM

    int bridge_sock_fd = -1, n =-1;
    struct sockaddr_in server_mobipass, client_bridge;
    char buffer[BUFF_SIZE];
    char* choice = NULL;
    size_t size = 1;

    /* create socket descriptor at client machine*/
    bridge_sock_fd= socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (bridge_sock_fd < 0) error("socket");

    /* *********************************************************
     * prepare source information of the socket file descriptor
     * *********************************************************
     */
    client_bridge.sin_family = AF_INET;
    client_bridge.sin_addr.s_addr=inet_addr(BRIDGE_IP_ADDRESS) ;
    client_bridge.sin_port = htons(BRIDGE_PORT_NUM);
    if( bind( bridge_sock_fd, (struct sockaddr *) &client_bridge, sizeof ( client_bridge ) ) < 0 )
    {
        error( "bind" );
    }


    /* *********************************************************
     * prepare destination information of the socket file descriptor
     * *********************************************************
     */
    server_mobipass.sin_family = AF_INET;
    server_mobipass.sin_addr.s_addr=inet_addr(MOBIPASS_L2_IP_ADDRESS) ;
    server_mobipass.sin_port = htons(MOBIPASS_L2_PORT_NUM);
    if( connect( bridge_sock_fd, (struct sockaddr *) &server_mobipass, sizeof ( server_mobipass ) ) < 0 ) {
        error("connect");
    }

    adjustSockParam(bridge_sock_fd);
    do
    {
        printf("sending traffic?[y/n]\n");
        getline(&choice,&size,stdin);
        if(*choice=='n')
            break;

        strncpy( buffer,
                    "Hello Mobipass, this is bridge :)\n",
                    sizeof(buffer));
        n = send( bridge_sock_fd, buffer, sizeof(buffer), MSG_CONFIRM  );
        if( n < 0 )
        {
            error( "send" );
        }

        assert(n == sizeof(buffer));

        usleep(TEN_MSEC);
        /*memset(buffer,0 , sizeof(buffer));

        if( recv( bridge_sock_fd, buffer, sizeof(buffer), 0 ) < 0 )
        {
            error( "recv" );
        }
        else
        {
            printf("Msg received from mobipass is:\n%s",buffer);
        }*/


    }while(*choice == 'y' || *choice == 'Y');

    close( bridge_sock_fd );

#else
   int tx_sock, n, rx_sock;
   unsigned int srv_length;
   struct sockaddr_in server_mobipass, from, server_bridge;
   char buffer[256];

   /* create socket descriptor at client machine*/
   tx_sock= socket(AF_INET, SOCK_DGRAM, 0);
   if (tx_sock < 0) error("socket");

   srv_length=sizeof(struct sockaddr_in);

   /*prepare server (peer entity) of UDP connection*/
   server_mobipass.sin_family = AF_INET;
   server_mobipass.sin_addr.s_addr=inet_addr(MOBIPASS_L2_IP_ADDRESS) ;
   server_mobipass.sin_port = htons(MOBIPASS_L2_PORT_NUM);

   printf("Please enter the message: ");
   bzero(buffer,256);
   fgets(buffer,255,stdin);

   n=sendto(tx_sock,buffer,
            strlen(buffer),0,(const struct sockaddr *)&server_mobipass,srv_length);
   if (n < 0) error("Sendto");

   rx_sock= socket(AF_INET, SOCK_DGRAM, 0);
   if (rx_sock < 0) error("socket");

   server_bridge.sin_family =  AF_INET;
   server_bridge.sin_addr.s_addr = inet_addr(BRIDGE_IP_ADDRESS);
   server_bridge.sin_port = htons(BRIDGE_PORT_NUM);
   if (bind(rx_sock,(struct sockaddr *)&server_bridge,srv_length)<0)
          error("binding");

   n = recvfrom(rx_sock,buffer,256,0,(struct sockaddr *)&from, &srv_length);
   if (n < 0) error("recvfrom");

   /*print to stdout what have been received*/
   write(1,"Got an ack: ",12);
   write(1,buffer,n);





   /* close sockets */
   close(rx_sock);
   close(tx_sock);

#endif /* RESTRICT_SRC_DST_NUM */
   return 0;
}

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

void adjustSockParam (int sockFD)
{

    int option_value;
    socklen_t option_len = sizeof(option_value);

    /** Adjust Send Buffer Size**/
    if( getsockopt(sockFD, SOL_SOCKET, SO_SNDBUF,
           &option_value, &option_len)< 0)
    {
        error("get Socket Option error:");
    }
    printf("Initial SO_SNDBUF: option_len = %d option_value = %d\n",option_len,option_value);

    /*option_value = 2048;
    if( setsockopt(sockFD, SOL_SOCKET, SO_SNDBUF,
           &option_value, option_len)< 0)
    {
        error("get Socket Option error:");
    }

    if( getsockopt(sockFD, SOL_SOCKET, SO_SNDBUF,
           &option_value, &option_len)< 0)
    {
        error("get Socket Option error:");
    }
    printf("Final SO_SNDBUF: option_len = %d option_value = %d\n",option_len,option_value);*/

    /** Adjust Receiver Buffer Size **/
    if( getsockopt(sockFD, SOL_SOCKET, SO_RCVBUF,
               &option_value, &option_len)< 0)
        {
            error("get Socket Option error:");
        }
        printf("Initial SO_RCVBUF: option_len = %d option_value = %d\n",option_len,option_value);
}

这是我的代码的第二面:

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

#define BUFF_SIZE               1800
#define MOBIPASS_L2_IP_ADDRESS  "192.168.13.53"
#define MOBIPASS_L2_PORT_NUM    12001

#define BRIDGE_IP_ADDRESS       "192.168.13.30"
#define BRIDGE_PORT_NUM         12000

#define DUMP                        0
#define ACT_AS_STRING               0
#define RESTRICT_SRC_DST_NUM        1
#define TEN_MSEC                    10000

#if DUMP
    #define DUMP_BUFFER(buf,len)                \
    {                                           \
        int i;                                  \
        for(i = 0; i < len; i++)                \
            printf("buf[%d] = 0x%x",i,buf[i]);  \
    }
#else
    #define DUMP_BUFFER(buf,len)        printf("received len=%d\n",len)
#endif

void adjustSockParam (int sockFD);
void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
#if RESTRICT_SRC_DST_NUM
    int mobipass_sock_fd = -1;
    struct sockaddr_in server_mobipass, client_bridge;
    char buffer[BUFF_SIZE];
    int recivedBytes=-1;

    printf("size of buffer = %d\n",sizeof(buffer));
    /* create socket descriptor at client machine*/
    mobipass_sock_fd= socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (mobipass_sock_fd < 0) error("socket");

    /* *********************************************************
     * prepare source information of the socket file descriptor
     * *********************************************************
     */
    client_bridge.sin_family = AF_INET;
    client_bridge.sin_addr.s_addr=inet_addr(MOBIPASS_L2_IP_ADDRESS) ;
    client_bridge.sin_port = htons(MOBIPASS_L2_PORT_NUM);
    if( bind( mobipass_sock_fd, (struct sockaddr *) &client_bridge, sizeof ( client_bridge ) ) < 0 )
    {
        error( "bind" );
    }


    /* *********************************************************
     * prepare destination information of the socket file descriptor
     * *********************************************************
     */
    server_mobipass.sin_family = AF_INET;
    server_mobipass.sin_addr.s_addr=inet_addr(BRIDGE_IP_ADDRESS) ;
    server_mobipass.sin_port = htons(BRIDGE_PORT_NUM);
    if( connect( mobipass_sock_fd, (struct sockaddr *) &server_mobipass, sizeof ( server_mobipass ) ) < 0 ) {
        error("connect");
    }

    adjustSockParam(mobipass_sock_fd);

    printf("waiting for message from bridge:\n");
    do{
        memset(buffer,0 , sizeof(buffer));
        recivedBytes = recv( mobipass_sock_fd, buffer, sizeof(buffer), 0 );
        if( recivedBytes  < 0 )
        {
            error( "recv" );
        }
        else
        {
            assert(recivedBytes == sizeof(buffer));
            DUMP_BUFFER(buffer,recivedBytes);
#if ACT_AS_STRING
            printf("Msg received from bridge is:\n%s",buffer);
#endif
        }
        usleep(TEN_MSEC);
#if ACT_AS_STRING
        strncpy( buffer,
                    "Hello Bridge, this is mobipass :)\n",
                    sizeof(buffer));
        if( send( mobipass_sock_fd, buffer, sizeof(buffer), 0 ) < 0 )
        {
            error( "send" );
        }
#endif
    }while(1);

    close( mobipass_sock_fd );

#else
    int tx_sock, n, rx_sock;
    unsigned int srv_length;
    socklen_t fromlen;
    struct sockaddr_in server_mobipass, from, server_bridge;
    char buf[1024];


    rx_sock=socket(AF_INET, SOCK_DGRAM, 0);
    if (rx_sock < 0) error("Opening socket");
    else printf("Creating rx udp socket\n");

    srv_length = sizeof(server_mobipass);
    bzero(&server_mobipass,srv_length);

    server_mobipass.sin_family=AF_INET;
    server_mobipass.sin_addr.s_addr=inet_addr(MOBIPASS_L2_IP_ADDRESS);
    server_mobipass.sin_port=htons(MOBIPASS_L2_PORT_NUM);

    if (bind(rx_sock,(struct sockaddr *)&server_mobipass,srv_length)<0)
    error("binding");
    else
    printf("Binding a socket to a server IP address\n");
    fromlen = sizeof(struct sockaddr_in);

    tx_sock=socket(AF_INET, SOCK_DGRAM, 0);
    if (tx_sock < 0) error("Opening socket");
    else printf("Creating tx udp socket\n");

    server_bridge.sin_family=AF_INET;
    server_bridge.sin_addr.s_addr=inet_addr(BRIDGE_IP_ADDRESS);
    server_bridge.sin_port=htons(BRIDGE_PORT_NUM);

    while (1)
    {
    printf("waiting for a message from client side:\n");
    n = recvfrom(rx_sock,buf,1024,0,(struct sockaddr *)&from,&fromlen);
    if (n < 0) error("recvfrom");

    write(1,"Message received from eNB machince:\n",36);
    write(1,buf,n);

    n = sendto(tx_sock,"hello eNB, I am mobipass\n",27,
              0,(struct sockaddr *)&server_bridge,fromlen);
    if (n  < 0) error("sendto");
    }

#endif
    return 0;
 }

void adjustSockParam (int sockFD)
{

    int option_value;
    socklen_t option_len = sizeof(option_value);

    /** Adjust Send Buffer Size**/
    if( getsockopt(sockFD, SOL_SOCKET, SO_SNDBUF,
           &option_value, &option_len)< 0)
    {
        error("get Socket Option error:");
    }
    printf("Initial SO_SNDBUF: option_len = %d option_value = %d\n",option_len,option_value);

/*  option_value = 2048;
    if( setsockopt(sockFD, SOL_SOCKET, SO_SNDBUF,
           &option_value, option_len)< 0)
    {
        error("get Socket Option error:");
    }

    if( getsockopt(sockFD, SOL_SOCKET, SO_SNDBUF,
           &option_value, &option_len)< 0)
    {
        error("get Socket Option error:");
    }
    printf("Final SO_SNDBUF: option_len = %d option_value = %d\n",option_len,option_value);*/

    /** Adjust Receiver Buffer Size **/
    if( getsockopt(sockFD, SOL_SOCKET, SO_RCVBUF,
               &option_value, &option_len)< 0)
        {
            error("get Socket Option error:");
        }
        printf("Initial SO_RCVBUF: option_len = %d option_value = %d\n",option_len,option_value);
}

这是第一面的输出:

Initial SO_SNDBUF: option_len = 4 option_value = 112640
Initial SO_RCVBUF: option_len = 4 option_value = 112640
sending traffic?[y/n]
y
sending traffic?[y/n]
y

这是第二面的输出:

size of buffer = 1800
Initial SO_SNDBUF: option_len = 4 option_value = 1048576
Initial SO_RCVBUF: option_len = 4 option_value = 1048576
waiting for message from bridge:

我不确定我在这里做错了什么。您有什么建议吗?

最佳答案

虽然 UDP 数据报包大小可达 64K(16 位数据长度字段),但通常的底层数据链路技术 - 以太网 - 的帧大小为 1500 字节。 IP header 至少少 20 个字节,UDP header 少 8 个字节,这样就只剩下 1472 个字节用于 UDP 有效负载,可以在没有 IP 分段的情况下发送,这通常会导致问题,例如您的情况,数据包只是在某处丢失。

正是出于这个原因,大多数基于 UDP 的协议(protocol)都会限制数据报大小。

关于c - Linux 上的 UDP 套接字;发送成功但无法接收大缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14122056/

相关文章:

python - 我需要服务器向所有客户端发送消息(Python、套接字)

c# - 网络流如何检测断开连接

sockets - 使用命令行停止 mosquitto (MQTT) 代理监听端口

c - 输出中忽略 Scanf 和后续行

c: scanf 读取整数

c - 如何读取直到 EOF 并打印输入的偶数/奇数?

python - Socket收不到数据?客户端是使用 Boost.asyio 库编写的。服务器是用python写的

c - 选择行为

c - 如何通过使用和创建函数来计算命令行参数中的字符数

java - 发送和接收 Java 对象