美文网首页
Unix下基于SCTP socket的通信:一对多场景

Unix下基于SCTP socket的通信:一对多场景

作者: EVANMORE | 来源:发表于2017-12-23 14:04 被阅读93次

    1. 一对多的通信场景


    一个SCTP服务端socket,多个SCTP客户端可以连接到这个socket上,不需要有显示的建立连接的过程;每个客户端接入进来时,都会建立一个对应的association

    2. Server端代码

    2.1 Socket建立

    建立socket的时候设置通信语义:SOCK_SEQPACKET

     socket( AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP );
    

    和一对一通信场景最大的区别是,没有accept()等待连接请求的过程,

    2.2 完整的Server端程序

    //SCTP Server: SOCK_SEQPACKET
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <netinet/sctp.h>
    #include "common.h"
    #include <errno.h>
    #include <string.h>
    
    int Socket()
    {
        /* Create SCTP TCP-Style Socket */
        int sock_fd = socket( AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP );
        
        if (-1 == sock_fd){
            printf("create socket failed: %s \n", strerror(errno));
        }       
        
        return sock_fd;
    }
    
    void Bind(int sock_fd)
    {
        struct sockaddr_in servaddr;
        int addr_count = 1;
    
        /* Accept connections from any interface */
        bzero( (void *)&servaddr, sizeof(servaddr) );
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
        servaddr.sin_port = htons(MY_PORT_NUM);
    
        // replace bind with sctp_bindx
        if(-1 == sctp_bindx( sock_fd, (struct sockaddr *)&servaddr, addr_count,  SCTP_BINDX_ADD_ADDR))
        {
            printf("bind socket failed: %s \n", strerror(errno));
            return;
        }
    }
    
    int SetSocketOpt(int sock_fd)
    {
        struct sctp_event_subscribe evnts;  
    
        bzero(&evnts, sizeof(evnts));
        evnts.sctp_data_io_event = 1;
    
        if(0 != setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts)))
        {
            printf("Error setsockopt(IPPROTO_SCTP): %s\n", strerror(errno));
            return 0;
        }
    
        return 1;
    }
    
    void Listen(int sock_fd)
    {
        int listenQueLen = 5;
        listen( sock_fd, listenQueLen);
    }
    
    void SctpRecevMsg(int sock_fd)
    {
        int msg_flags;
        size_t rd_sz;
    
        char readbuf[MAX_BUFFER+1];
        struct sockaddr_in cliaddr;
        struct sctp_sndrcvinfo sri;
        socklen_t len;
        
        len = sizeof(struct sockaddr_in);
        
        while(1)
        {
            printf("[Server]: wait for message!\n");
            int rz = sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf),
                                  &cliaddr, &len, &sri, &msg_flags);
            
            if (rz == 0)
            {
                printf("[Server]: EOF received!\n");
                return;
            }
            else if(rz < 0)
            {
                printf("[Server]: Error sctp_recvmsg: %s\n", strerror(errno));
                return;
            }
            
            printf("[Server]: Message(size = %d): '%s' received, assoid = %x \n", rz , readbuf, sri.sinfo_assoc_id);
        }
    }
    
    void Close(int sock_fd)
    {
        close(sock_fd);
        
        printf("[Server]: Close connect socket!\n");
    }
    
    int main()
    {
        int list_sockfd, conn_sockfd;
      
        list_sockfd = Socket(); 
        Bind(list_sockfd);
        SetSocketOpt(list_sockfd);
        Listen(list_sockfd);
        
        /* Server loop... */
        while( 1 ) {        
            SctpRecevMsg(list_sockfd);
            
            Close(list_sockfd);
        }
    
        return 0;
    }
    

    3. Client端代码

    3.1 发送消息

    这里发送消息使用的是另一个函数sctp_sendmsg(),在这个函数里会填写目的地址,发送消息payload之前会先进行四次握手建立连接;

    struct sockaddr_in servaddr;
        
     bzero( (void *)&servaddr, sizeof(servaddr));
     servaddr.sin_family = AF_INET;
     servaddr.sin_port = htons(MY_PORT_NUM);
     servaddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
    sctp_sendmsg(sock_fd, buf, strlen(buf), 
                        &servaddr, sizeof(servaddr), 0, 0, stream, 0, 0);
    

    3.2 完整的client端代码

    //SCTP client: SOCK_SEQPACKET
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <netinet/sctp.h>
    #include <arpa/inet.h>
    #include "common.h"
    #include <errno.h>
    
    int Socket()
    {
        return socket( AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP );
    }
    
    void SetSocketOpt(int sock_fd)
    {
        struct sctp_initmsg initmsg;
        struct sctp_event_subscribe events;
        int opt = 1;
        int len = sizeof(opt);
    
        /* Specify that a maximum of 5 streams will be available per socket */
        memset( &initmsg, 0, sizeof(initmsg) );
        initmsg.sinit_num_ostreams = 5;
        initmsg.sinit_max_instreams = 5;
        initmsg.sinit_max_attempts = 4;
        if(0 != setsockopt( sock_fd, IPPROTO_SCTP, SCTP_INITMSG,
                         &initmsg, sizeof(initmsg) ))
        {
          printf("[Client]: error setsocketopt IPPROTO_SCTP, %s\n", strerror(errno));
          return;
        }
    
        /* Enable receipt of SCTP Snd/Rcv Data via sctp_recvmsg */
        memset( (void *)&events, 0, sizeof(events) );
        events.sctp_data_io_event = 1;
        events.sctp_association_event = 1;
        
        if(0 != setsockopt( sock_fd, SOL_SCTP, SCTP_EVENTS,
                         (const void *)&events, sizeof(events) ))
        {
          printf("[Client]: error setsocketopt SOL_SCTP, %s\n", strerror(errno));
          return;
        }
    }
    
    void SendMsg(int sock_fd)
    {
        char buf[20];
        struct sctp_sndrcvinfo sinfo;
        struct sockaddr_in servaddr;
        
        bzero( (void *)&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(MY_PORT_NUM);
        servaddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
        
        printf("[Client]: Input message to send to server!\n");
        if(NULL != gets(buf))
        {
            int stream = 1;
            sctp_sendmsg(sock_fd, buf, strlen(buf), 
                        &servaddr, sizeof(servaddr), 0, 0, stream, 0, 0);
        }
    }
    
    void Close(int sock_fd)
    {
        char buf[20];
        printf("[Client]: Enter to close socket!\n");
        if(NULL != gets(buf))
        {
            /* Close our socket and exit */
            close(sock_fd);
            printf("[Client]: Close socket!\n");
    
            return;
        }
    }
    
    int main()
    {
        int sock_fd;
        /* Create an SCTP TCP-Style Socket */
        sock_fd = Socket();
        SetSocketOpt(sock_fd);
        
        SendMsg(sock_fd);
        Close(sock_fd);
        return 0;
    }
    

    4. 程序的运行过程

    1. 服务端启动
      建立好socket后,会阻塞等待消息接收,
    [Server]: wait for message!
    
    1. 客户端启动
      往server端发送一条消息
    [Client]: Input message to send to server!
    hello
    
    1. 服务端收到消息
    [Server]: Message(size = 5): 'hell' received, assoid = 39
    [Server]: wait for message!
    

    打印出association ID后继续回到等待下一条消息的状态

    1. 客户端关闭socket
      客户端关闭socket后,不会像一对一通信的那样发送一条EOF消息给服务端,所以服务端并没有任何对应的处理;但是我们知道现在SCTP的连接已经不可用了,那么服务端怎么去处理这条association?

    2. 重新启动一个新的客户端
      同样发送一条新的消息给server,可以看到建立了一个新的association,

    [Server]: Message(size = 7): 'hello 2' received, assoid = 3b
    

    可以通过这个association ID来区分连到这个server端socket的不同的链路。

    相关文章

      网友评论

          本文标题:Unix下基于SCTP socket的通信:一对多场景

          本文链接:https://www.haomeiwen.com/subject/uuhzwxtx.html