zoukankan      html  css  js  c++  java
  • ReplayKit2 有线投屏项目-反向Socket实现

    一、需求

      我们在使用RTMP协议进行推流的时候,底层仍然采用的是TCP协议或者QUICK协议,有客户端主动发起请求。但是在有线投屏中,需要PC端向手机发起请求建立连接

    二、实现

      在客户端主动发起请求之前,我们首先启动一个socket监听来自PC的连接,如果连接成功,那么我们使用这个已经建立好的连接,继续后面的流程

      在实现中,我们需要设计一个超时的机制,一般socket可以对send和recv设置超时,当然超时都是对同步的socket生效的。

      正常设置如下:

       struct timeval tv, recv_timeout;
       tv.tv_sec = timeout / 1000;
       tv.tv_usec = static_cast<int>(timeout % 1000 * 1000);
       int ret = ::setsockopt(m_nRealServerSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv));
       ret = ::setsockopt(m_nRealServerSocket, SOL_SOCKET, SO_SNDTIMEO, (const char*) &tv, sizeof(tv));

      但是这个设置对accept在iOS平台是不生效的,必须采用select的方式

      select监听描述符如下所示

    #define MYPORT 1937    // the port users will be connecting to
    #define BACKLOG 1     // how many pending connections queue will hold
    #define BUF_SIZE 200
    int fd_A[BACKLOG];     // accepted connection fd
    int conn_amount;    // current connection amount
    
    - (void)startSelectDemo2
    {
        int sock_fd, new_fd;  // listen on sock_fd, new connection on new_fd
        struct sockaddr_in server_addr;    // server address information
        struct sockaddr_in client_addr; // connector's address information
        socklen_t sin_size;
        int yes = 1;
        char buf[BUF_SIZE];
        int ret;
        int i;
        
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            NSLog(@"socket");
            exit(1);
        }
        
        if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
            NSLog(@"setsockopt");
            exit(1);
        }
        
        server_addr.sin_family = AF_INET;         // host byte order
        server_addr.sin_port = htons(MYPORT);     // short, network byte order
        server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
        memset(server_addr.sin_zero, '', sizeof(server_addr.sin_zero));
        
        if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
            NSLog(@"bind");
            exit(1);
        }
        
        if (listen(sock_fd, BACKLOG) == -1) {
            NSLog(@"listen");
            exit(1);
        }
        
        NSLog(@"listen port %d
    ", MYPORT);
        
        fd_set fdsr;
        int maxsock;
        struct timeval tv;
        
        conn_amount = 0;
        sin_size = sizeof(client_addr);
        maxsock = sock_fd;
        while (1) {
            // initialize file descriptor set
            FD_ZERO(&fdsr);
            FD_SET(sock_fd, &fdsr);
            
            // timeout setting
            tv.tv_sec = 5;
            tv.tv_usec = 0;
            
            // add active connection to fd set
            for (i = 0; i < BACKLOG; i++) {
                if (fd_A[i] != 0) {
                    FD_SET(fd_A[i], &fdsr);
                }
            }
            
            ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);
            if (ret < 0) {
                NSLog(@"select");
                break;
            } else if (ret == 0) {
                NSLog(@"timeout
    ");
                continue;
            }
            
            // check every fd in the set
            for (i = 0; i < conn_amount; i++) {
                if (FD_ISSET(fd_A[i], &fdsr)) {
                    ret = recv(fd_A[i], buf, sizeof(buf), 0);
                    if (ret <= 0) {        // client close
                        NSLog(@"client[%d] close
    ", i);
                        close(fd_A[i]);
                        FD_CLR(fd_A[i], &fdsr);
                        fd_A[i] = 0;
                    } else {        // receive data
                        if (ret < BUF_SIZE)
                            memset(&buf[ret], '', 1);
                        NSLog(@"client[%d] send:%s
    ", i, buf);
                    }
                }
            }
            
            // check whether a new connection comes
            if (FD_ISSET(sock_fd, &fdsr)) {
                new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
                if (new_fd <= 0) {
                    NSLog(@"accept");
                    continue;
                }
                else
                {
                    const char *hello = "hello from clent";
                    send(new_fd, hello, strlen(hello), 0);
                    
                    char recvbuf[4096];
                    int retry = 0;
                    while (retry++ < 3) {
                        
                        memset(recvbuf, 0, 4096);
                        
                        int ret = recv(new_fd, recvbuf, 4096 - 1, 0);
                        
                        if(ret > 0)
                        {
                            NSLog(@"app recv:%s
    ",recvbuf);
                        }
                        else
                        {
                            NSLog(@"app recv error ret = %d
    ",ret);
                        }
                    }
                    
                    
                }
                
                // add to fd queue
                if (conn_amount < BACKLOG) {
                    fd_A[conn_amount++] = new_fd;
                    NSLog(@"new connection client[%d] %s:%d
    ", conn_amount,
                           inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
                    if (new_fd > maxsock)
                        maxsock = new_fd;
                }
                else {
                    NSLog(@"max connections arrive, exit
    ");
                    send(new_fd, "bye", 4, 0);
                    close(new_fd);
                    break;
                }
            }
            
            int i;
            NSLog(@"client amount: %d
    ", conn_amount);
            for (i = 0; i < BACKLOG; i++) {
                NSLog(@"[%d]:%d  ", i, fd_A[i]);
            }
            NSLog(@"
    
    ");    }
        
        // close other connections
        for (i = 0; i < BACKLOG; i++) {
            if (fd_A[i] != 0) {
                close(fd_A[i]);
            }
        }
        
        exit(0);
    }

      在成功收到连接之后,我们确保连接变成同步方式

    unsigned long ul = 0;
    ioctl(Sock, FIONBIO, &ul)

      并且在recv的时候,最后一个参数也可以决定是否是同步还是异步的方式,0 表示默认方式 ; 0x80表示异步的方式

    ssize_t    recv(int, void *, size_t, int) __DARWIN_ALIAS_C(recv);

      

    三、总结

      对于理解网络底层的编程,理解select的工作原理还需要继续加深。

    四、代码

    https://github.com/liqiushui/iOSSocketAcceptTimeout

  • 相关阅读:
    日常点滴
    Django基础之forms组件中的ModelForm组件
    你想了解的轮询、长轮询和websocket都在这里了
    python并发编程之协程
    聊聊五大IO模型
    python并发编程之线程
    网络编程
    python并发编程之进程
    python中的异常处理
    flask实现文件的上传
  • 原文地址:https://www.cnblogs.com/doudouyoutang/p/9186280.html
Copyright © 2011-2022 走看看