简体   繁体   中英

SCTP client and server meets error when reading message sent by the other

I'm implementing a simple SCTP client and server with Linux SCTP socket API. Client and server both use one-to-one socket style. After connecting to server, the client sends a hello message to server and the server responds back with its hello message. Here' the code for server and client:

server.c

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

int main(int argc, char *argv[])
{
    int srvr_sock;
    struct sockaddr_in srv_addr;
    struct sockaddr_in clnt_addr;
    struct sctp_sndrcvinfo sndrcvinfo;
    struct sctp_event_subscribe event;
    socklen_t addr_sz;
    char snd_buf[] = "Hello from server";
    char rcv_buf[1024] = {0};
    int new_fd;
    int flags;
    int rd_sz;
    int ret;

    /* One-to-one style */
    /* Create socket */
    srvr_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
    if (srvr_sock < 0)
    {
        perror("Open srvr_sock");
        exit(EXIT_FAILURE);
    }

    /* Bind to server address */
    memset(&srv_addr, 0, sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port = htons(SERVER_PORT_NUM);
    srv_addr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDR_1);
    ret = bind(srvr_sock, (struct sockaddr *) &srv_addr, sizeof(srv_addr));
    if (ret < 0)
    {
        perror("Bind srvr_sock");
        exit(EXIT_FAILURE);
    }

    /* Enable all events */
    event.sctp_data_io_event = 1;
    event.sctp_association_event = 1;
    event.sctp_address_event = 1;
    event.sctp_send_failure_event = 1;
    event.sctp_peer_error_event = 1;
    event.sctp_shutdown_event = 1;
    event.sctp_partial_delivery_event = 1;
    event.sctp_adaptation_layer_event = 1;
    if (setsockopt(srvr_sock, IPPROTO_SCTP, SCTP_EVENTS, &event,
        sizeof(event)) != 0)
    {
        perror("setsockopt failed");
        exit(EXIT_FAILURE);
    }

    /* Listen */
    ret = listen(srvr_sock, 5);
    if (ret < 0)
    {
        perror("Listen on srvr_sock");
        exit(EXIT_FAILURE);
    }

    /* Server loop */
    while (1)
    {
        printf("Waiting for new connection...\n");
        new_fd = accept(srvr_sock, (struct sockaddr *)&clnt_addr, &addr_sz);
        if (new_fd < 0)
        {
            perror("Failed to accept client connection");
            continue;
        }

        memset(rcv_buf, 0, sizeof(rcv_buf));
        rd_sz = sctp_recvmsg(new_fd, (void *)rcv_buf, sizeof(rcv_buf),
                            (struct sockaddr *) NULL,
                            0,
                            &sndrcvinfo,
                            &flags);
        if (rd_sz <= 0)
        {
            continue;
        }

        if (flags & MSG_NOTIFICATION)
        {
            printf("Notification received. rd_sz=%d\n", rd_sz);
        }
        printf("New client connected\n");
        printf("Received %d bytes from client: %s\n", rd_sz, rcv_buf);

        /* Send hello to client */
        ret = sctp_sendmsg(new_fd, /* sd */
                           (void *) snd_buf, /* msg */
                           strlen(snd_buf), /* len */
                           NULL, /* to */
                           0, /* to len */
                           0, /* ppid */
                           0, /* flags */
                           STREAM_ID_1, /* stream_no */
                           0, /* TTL */
                           0 /* context */); 
        if (ret < 0)
        {
            perror("Error when send data to client");
        }
        else
        {
            printf("Send %d bytes to client\n", ret);
        }

        if (close(new_fd) < 0)
        {
            perror("Close socket failed");
        }
    }
    close(srvr_sock);

    return 0;
}

client.c

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

int main(int argc, char *argv[])
{
    int conn_fd;
    struct sockaddr_in srvr_addr;
    struct sctp_sndrcvinfo sndrcvinfo;
    socklen_t addr_sz = sizeof(struct sockaddr_in);
    int flags;
    char rcv_buf[1024] = {0};
    char snd_buf[] = "Hello from client";
    int rcv_cnt;
    int ret;

    /* One-to-one style */
    /* Create socket */
    conn_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
    if (conn_fd < 0)
    {
        perror("Create socket conn_fd");
        exit(EXIT_FAILURE);
    }

    /* Specify the peer endpoint to which we'll connect */
    memset(&srvr_addr, 0, sizeof(srvr_addr));
    srvr_addr.sin_family = AF_INET;
    srvr_addr.sin_port = htons(SERVER_PORT_NUM);
    srvr_addr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDR_1);

    /* Connect */
    ret = connect(conn_fd, (struct sockaddr *)&srvr_addr, sizeof(srvr_addr));
    if (ret < 0)
    {
        perror("Connect failed");
        exit(EXIT_FAILURE);
    }
    printf("Connected to server\n");

    /* Send hello to server */
    ret = sctp_sendmsg(conn_fd, (void *)snd_buf, strlen(snd_buf),
                        (struct sockaddr *) &srvr_addr, sizeof(srvr_addr), 0,
                        0, STREAM_ID_1, 0, 0);
    if (ret < 0)
    {
        perror("Send to server failed");
        close(conn_fd);
        exit(EXIT_FAILURE);
    }
    else
    {
        printf("Send %d bytes to server\n", ret);
    }

    /* Read message from server */
    rcv_cnt = sctp_recvmsg(conn_fd, (void *)rcv_buf, sizeof(rcv_buf),
            (struct sockaddr *) &srvr_addr, &addr_sz,
            &sndrcvinfo, &flags);
    if (rcv_cnt <= 0)
    {
        printf("Socket error or EOF\n");
    }
    else if (flags & MSG_NOTIFICATION)
    {
        printf("Notification received. rcv_cnt=%d\n", rcv_cnt);
    }
    else
    {
        printf("Received %d bytes from server: %s\n", rcv_cnt, rcv_buf);
    }

    /* close socket */
    close(conn_fd);

    return 0;
}

common.h

#define SERVER_PORT_NUM     16789
#define SERVER_IP_ADDR_1    "192.168.56.102"
#define STREAM_ID_1         1

Client and server are running on 2 Debian VMs in the same subnet, client's IP is 192.168.56.101, server's IP is 192.168.56.102.

When I start the server and then run the client, most of the time the client fails with following output:

./client
Connected to server
Send to server failed: Cannot assign requested address

However the server shows that it has read data sent from client and has responded with server hello message:

 ./server
Waiting for new connection...
Notification received. rd_sz=20
New client connected
Received 20 bytes from client: ▒
Send 17 bytes to client
Waiting for new connection...

Also the data received from client is corrupted in this case.

I tried to run the client many times and sometimes it succeeds:

$ ./client
Connected to server
Send 17 bytes to server
Received 17 bytes from server: Hello from server

The server still shows same log messages in this case.

Why would the client fail most of the time while only succeed a few times? The result seems to be unpredictable to me. Also why the data read by server is corrupted in server's output?

Try to bind also client socket. In client.c between socket create and before connect put:

cli_addr.sin_family = AF_INET;
cli_addr.sin_port = 0;
cli_addr.sin_addr.s_addr = inet_addr(INADDR_ANY); /* or inet_addr("192.168.56.101"); 
for multiple ip addresses or network cards */
ret = bind(conn_fd, (struct sockaddr *) &cli_addr, sizeof(cli_addr));
if (ret < 0)
{
  perror("Bind client_sock");
  exit(EXIT_FAILURE);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM