且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

套接字编程:C语言中的UDP客户端-服务器

更新时间:2022-06-18 22:35:47

在开发网络软件时(尤其是在使用BSD套接字接口时),在建立基本通信之前,使事情尽可能简单是很重要的。然后,您可以逐步添加功能,同时确保一路上不会破坏任何东西。

When developing networking software (especially when using the BSD socket interface), it's important to keep things as simple as possible until you've established basic communication. Then you can incrementally add functionality, while making sure that you don't break anything along the way.

在客户端,保持简单意味着

On the client side, keeping things simple means


  • 不要在客户端调用 bind 。操作系统将选择适当的接口并分配一个随机端口号,因此无需绑定套接字。

  • Don't call bind in the client. The OS will choose an appropriate interface and assign a random port number, so there's no need to bind the socket.

使用硬编码的服务器地址(例如127.0.0.1)。地址127.0.0.1(0x7f000001)是本地主机地址,适合将数据包发送到同一台计算机上的服务器。

Use a hard-coded server address (e.g. 127.0.0.1). Address 127.0.0.1 (0x7f000001) is the local host address, suitable for sending packets to a server on the same machine.

使用硬编码的端口号(例如50037)。 星号端口号应大于0xC000十六进制(十进制为49152)。

Use a hard-coded port number (e.g. 50037). Ephemeral port numbers should be greater than 0xC000 hex (49152 decimal).

使用硬编码的消息,例如你好。

Use a hard-coded message, e.g. "hello".

请记住,这是客户端软件的样子

With that in mind, here's what the client software looks like

int main( void )
{
    int fd;
    if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        perror("socket failed");
        return 1;
    }

    struct sockaddr_in serveraddr;
    memset( &serveraddr, 0, sizeof(serveraddr) );
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons( 50037 );              
    serveraddr.sin_addr.s_addr = htonl( 0x7f000001 );  

    for ( int i = 0; i < 4; i++ ) {
        if (sendto( fd, "hello", 5, 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 ) {
            perror( "sendto failed" );
            break;
        }
        printf( "message sent\n" );
    }

    close( fd );
}

在服务器端,保持简单意味着

On the server side, keeping things simple means


  • 绑定到 INADDR_ANY ,即让操作系统选择合适的接口。

  • 绑定到硬编码端口,例如50037(必须与客户端使用的端口相同)。

  • 不要从 recvfrom 请求地址信息,即传递 NULL,最后两个参数为0

  • Bind to INADDR_ANY, i.e. let the OS pick an appropriate interface.
  • Bind to a hard-coded port, e.g. 50037 (must be the same port the client uses).
  • Don't request the address information from recvfrom, i.e. pass NULL, 0 as the last two parameters.

请牢记这一点服务器软件看起来像

With that in mind, here's what the server software looks like

int main( void )
{
    int fd;
    if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        perror( "socket failed" );
        return 1;
    }

    struct sockaddr_in serveraddr;
    memset( &serveraddr, 0, sizeof(serveraddr) );
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons( 50037 );
    serveraddr.sin_addr.s_addr = htonl( INADDR_ANY );

    if ( bind(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 ) {
        perror( "bind failed" );
        return 1;
    }

    char buffer[200];
    for ( int i = 0; i < 4; i++ ) {
        int length = recvfrom( fd, buffer, sizeof(buffer) - 1, 0, NULL, 0 );
        if ( length < 0 ) {
            perror( "recvfrom failed" );
            break;
        }
        buffer[length] = '\0';
        printf( "%d bytes: '%s'\n", length, buffer );
    }

    close( fd );
}