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, ‘\0‘, 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\n", 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\n");
            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\n", 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], ‘\0‘, 1);
                    NSLog(@"client[%d] send:%s\n", 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\n",recvbuf);
                    }
                    else
                    {
                        NSLog(@"app recv error ret = %d\n",ret);
                    }
                }

            }

            // add to fd queue
            if (conn_amount < BACKLOG) {
                fd_A[conn_amount++] = new_fd;
                NSLog(@"new connection client[%d] %s:%d\n", 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\n");
                send(new_fd, "bye", 4, 0);
                close(new_fd);
                break;
            }
        }

        int i;
        NSLog(@"client amount: %d\n", conn_amount);
        for (i = 0; i < BACKLOG; i++) {
            NSLog(@"[%d]:%d  ", i, fd_A[i]);
        }
        NSLog(@"\n\n");    }

    // 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的工作原理还需要继续加深。

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 26.0px Menlo; color: #8b84cf; background-color: #282b35 }

原文地址:https://www.cnblogs.com/doudouyoutang/p/9186280.html

时间: 2024-10-13 00:33:56

ReplayKit2 有线投屏项目-反向Socket实现的相关文章

mac电脑怎么投屏?教你选择适合自己的Mac投屏软件

mac上有什么好的投屏软件嘛?苹果手机ios投屏到mac用哪款投屏软件,mac投屏ipad该用哪款软件怎么操作,macdown小编给大家介绍的这几款Mac投屏软件,各有各的特色,总有一款适合你投屏. 1.AirServer 7 for Mac 第一款,AirServer 7 for Mac,使用简单,利用 AirPlay 技术,iPhone或iPad就可以无线连接到Mac上,不需要在iPhone或iPad上安装任何软件,就可以实时显示iPhone或iPad的屏幕.通过AirServer,可以解决

Android跨平台投屏软件(无需root)--scrcpy

之前一直使用 Chrome 的一个插件「Vysor」进行 Android 手机的投屏,但是有码率限制,高码率需要付费,最近发现一个更好的继任者「scrcpy」,就来推荐一下. 本文将以 Mac 为例进行配置和使用 scrcpy,其他系统请参考官方文档,要求有一定的技术动手能力,觉得过于复杂的用户推荐使用「Apower Mirror」(使用简单,支持 Android 和 iOS). 项目介绍 做过 Android 开发的应该都知道「Genymotion」,没错,scrcpy 就是 Genymoti

安卓手机无线投屏电脑 三种方法轻松搞

在手机上看视频,已经是我们现在最主流的娱乐方式.不过,长时间看小屏幕对眼睛可受不了,而且一些4K.1080P的大片还是在大屏幕上更有视觉效果.这时该怎么办呢,有没有什么办法可以将手机屏幕投射到电脑屏幕上,下面简单几步教你安卓手机无线投屏电脑.手机投屏到电脑其实主要使用的是WiFi传输,这种技术名为"Miracast".简单来说,"Miracast"就是通过"WiFi Display"认证的设备.有了这一功能后,移动端的音视频文件就可以分享到电脑上

安卓投屏到电脑 - 使用scrcpy

scrcpy通过adb调试的方式来将手机屏幕投到电脑上,并可以通过电脑控制您的Android设备.它可以通过USB连接,也可以通过Wifi连接(类似于隔空投屏),而且不需要任何root权限,不需要在手机里安装任何程序.scrcpy同时适用于GNU / Linux,Windows和macOS. 功能特性 简洁(原生,仅显示设备屏幕) 性能(30~60fps) 质量(1920×1080或以上) 低延迟(35~70ms) 启动时间短(显示第一张图像约1秒) 非侵入性(设备上没有安装任何东西) 此项目为

苹果手机的投屏技术

今天尝试了一下苹果手机的投屏技术感觉挺好玩的,下面我来分享一下怎样实现的投屏效果(我的手机为iphone) (1)首先,从网络上下载苹果录屏大师这个软件(网址:http://www.itools.cn/),下载到电脑上. 下载好后的图标如图所示: (我在玩耍这个软件的时候还下载了下面的这个东西<系统提示安装>) (2)在手机App商店中下载苹果投影助手,然后打开软件按照软件中的提示步骤来就行. 点击连接助手会出现一个二维码,使用屏幕投影助手扫描即可 在完成此步骤需要满足一个必要条件: 保证手机

Android应用源码带定位和密码找回的锁屏项目

Android应用源码带定位和密码找回的锁屏项目 首先这是个锁屏的应用项目源码,并且有录制手势的功能不过与传统的九宫格不一样,即使源码里面有注释但是至今我也没有弄明白他的手势录制方法, 项目里面貌似还用到了GPS和五次解锁失败就会向指定手机号发送短信,不过我没有细看,源码有比较详细的注释,正在研究锁屏的朋友不要错过,本项目默认编译版本4.4.2编码GBK. 下载地址:http://www.devstore.cn/code/info/173.html    

投屏协议汇总

DLNA,Digital Living Network Alliance的简称,意在解决pc,家电,移动设备在局域网内的多媒体共享(音频,视频,图片).说DLNA是多屏互动,太牵强了,专业点来讲,应该是媒体共享技术.使用DLNA功能需要满足的条件: 设备需要在同一局域网内: 至少需要一个DMS端,一个DMP端. DLNA的几种产品: 1.DMS, Digital Media Server的缩写,把本设备内的多媒体文件(自己控制需要共享哪些文件)到DLNA服务端, 2.DMP,Digital Me

iphone 与 PC端电脑投屏设置

1. iphone端安装: 屏幕投影助手 下载地址 https://itunes.apple.com/cn/app/ping-mu-tou-ying-zhu-shou/id1152332174?mt=8 2. windows端下载安装 屏幕录屏大师 下载地址 http://www.downza.cn/soft/23591.html 然后打开运行屏幕录屏大师 3. 手机与电脑保持在同一个局域网内(公司局域网禁止了 无线网络互ping所以无解) 手机软件扫码 然后打开说说极端的 airplay 连接

海信电视使用优酷投屏的方法

1. 下载乐播投屏的TV版本 http://www.hpplay.com.cn/Download.html 2. copy到U盘 然后到海信电视上面进行安装 3. 打开应用 4.打开优酷 看视频时右上角有一个 TV的小按钮 点击 TV 会在投屏上面显示一个乐播2的选项,选中 然后即可 5.备注 电视机和手机必须得在同一个无线局域网中. 原文地址:https://www.cnblogs.com/jinanxiaolaohu/p/8525578.html