首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >CFSocket数据回调

CFSocket数据回调
EN

Stack Overflow用户
提问于 2018-01-25 03:05:37
回答 1查看 1.6K关注 0票数 2

在iPhone应用程序中,我从现有的本地UDP套接字创建一个CFSocket对象,并在套接字接收到某些数据时设置一个数据回调。然后,我将其添加到主程序循环中:

代码语言:javascript
复制
    //Set socket descriptor field
    cbData.s = udpSocket.getSocketDescriptor();

    CFSocketContext udpSocketContext;
    memset(&udpSocketContext, 0, sizeof(udpSocketContext));
    udpSocketContext.info = &cbData;

    cbData.socketRef = CFSocketCreateWithNative(NULL, cbData.s, kCFSocketDataCallBack, &getSocketDataCallBack, &udpSocketContext);
    cbData.runLoopSourceRef = CFSocketCreateRunLoopSource( NULL, cbData.socketRef, 0);
    CFRunLoopAddSource(CFRunLoopGetMain(), cbData.runLoopSourceRef, kCFRunLoopCommonModes);

我每5 getSocketDataCallBack从一个单独的Mac服务器应用程序通过WiFi发送1024字节的数据报,并在我的WiFi例程中在iPhone上接收它们。

我希望每5次调用一次getSocketDataCallBack (以匹配从Mac发送的数据报的周期),这种情况在大多数情况下都会发生。但是,这些电话往往会被延迟10秒或10秒的mS。然后,我得到一个快速的回调序列(mS的一部分),以检索在该延迟上堆积的多个数据报。

由于iOS显然保留了延迟的数据报,

  • 有没有办法一次从系统中抓取所有延迟的数据报,而不是一次又一次地调用getSocketDataCallBack? 我确实询问回调ala中有多少字节可用: CFDataRef dataRef =(CFDataRef)数据;numBytesReceived = CFDataGetLength(dataRef); 但是“numBytesReceived”总是被报道为1024。
  • 或者,是否有任何方法通过其他方法来改善/减少套接字回调时间的可变性?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-01-30 01:40:08

我将套接字回调用于UNIX套接字的进程间通信(实际上是线程间通信)。我们使用套接字的方式与TCP/UDP完全相同。

下面的代码是用c/obj-c编写的,并使用posix线程。要把它翻译成Swift/NSThread,应该不难。

注意,下面的程序作为服务器端工作,这意味着程序创建客户端连接到的套接字。一旦客户端连接到套接字,系统就会自动接受连接并分配另一个文件描述符来读取/写入。套接字回调反映了这两个阶段的操作。最初,我们创建套接字,然后添加为运行循环源,这样系统就可以在客户端试图连接时调用该调用。系统接受,然后分配并通知回调文件描述符与客户端进行读写。然后,我们从读/写fd创建另一个运行循环源,并将其添加到运行循环中。当rx/tx数据准备就绪时,将调用第二个回调。

主螺纹:

主线程创建UNIX套接字和工作线程。套接字fd作为工作线程的参数传递。

代码语言:javascript
复制
#import <stdio.h>
#import <string.h>
#import <stdlib.h>
#import <unistd.h>
#import <pthread.h>
#import <sys/socket.h>
#import <sys/un.h>
#import <sys/stat.h>
#import <sys/types.h>
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
int setup(const char *ipcNode) {
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        return -1;
    }
    struct sockaddr_un sa = {0};
    sa.sun_len = sizeof(sa);
    sa.sun_family = AF_UNIX;
    strcpy(sa.sun_path, ipcNode);
    remove(sa.sun_path);
    if (bind(sockfd, (struct sockaddr*)&sa, sizeof(struct sockaddr_un)) == -1) {
        close(sockfd);
        return -1;
    }
    // start up worker thread
    pthread_attr_t at;
    pthread_attr_init(&at);
    pthread_attr_setdetachstate(&at, PTHREAD_CREATE_DETACHED);
    pthread_t th;
    pthread_create(&th, &at, workerThread, (void *)(long)(sockfd));
    return 1;
}

工作线程:

该程序作为服务器工作。因此,它等待客户端连接(通过connect())。一旦连接完毕,系统就会自动调用accept(),并分配读/写fd与客户端通信。这个fd被传递给接受-回调例程socketDataCallback()。然后,使用读/写fd创建另一个回调clientDataCallback()。

代码语言:javascript
复制
// worker thread
//
void *workerThread(void *tprm) {
    int sockfd = (int)tprm;
    int retval = listen(sockfd, 1);  // mark as "server" side. here, accept only 1 connection request at a time
    if (retval != 0) {
        return NULL;
    }
    // create CFSocket and register it as data source.
    CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault, sockfd, kCFSocketAcceptCallBack, socketDataCallback, nil);
    // don't close native fd on CFSocketInvalidate
    CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate);
    // create run loop source
    CFRunLoopSourceRef socketRunLoop = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
    // add to run loop
    CFRunLoopAddSource(CFRunLoopGetCurrent(), socketRunLoop, kCFRunLoopCommonModes);
    CFRelease(socketRunLoop);
    CFRelease(socket);
    CFRunLoopRun();
    // not return here untill run loop stops
    close(sockfd);
    return NULL;
}

// socket connection w/ client side. create another data source and add to run-loop
//
void socketDataCallback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
    CFSocketContext socketContext;
    memset(&socketContext, 0, sizeof(CFSocketContext));
    int clientfd = *((int *)data);   // get file descriptor (fd)
    socketContext.info = (void *)((long)clientfd);  // set fd at info of socketContext
    // create CFSocket for tx/rx w/ connected client
    CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault, clientfd, kCFSocketReadCallBack | kCFSocketWriteCallBack, clientDataCallback, &socketContext);
    CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack);
    CFRunLoopSourceRef socketRunLoop = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), socketRunLoop, kCFRunLoopCommonModes);
    CFRelease(socket);
    CFRelease(socketRunLoop);
}

// data to/from client
//
void clientDataCallback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
    if (callbackType & kCFSocketWriteCallBack) {
        // your own tx data prcess here
        // txDataCallback(s, callbackType, address, data, info);
    }
    if (!(callbackType & kCFSocketReadCallBack))  return;
    // extract fd
    int fd = (int)((long)info);
    // read data, and do some work
    uint8_t rxdata[1024];
    size_t nr = read(fd, rxdata, 1024);
    if (!nr) {
        // socket closed
        handleSocketClosed(s);
        return;
    }
    // your own rx process here
}

// socket closed
//
void handleSocketClosed(CFSocketRef s) {
    // any clean up process here, then
    CFSocketInvalidate(s);
    // stop run loop if necessary
    // CFRunLoopStop(CFRunLoopGetCurrent());
}

如果你在客户端工作,事情会变得容易一些。您将得到一个具有connect()调用的读写fd。然后创建CFSockeRef并使用fd添加运行循环。

希望这能有所帮助。

编辑:如何使用POSIX select()等待。要在工人线程上等待POSIX,select()比套接字回调更简单。如果您在客户端,那么:

代码语言:javascript
复制
int sockfd = socket(...);
bind(sockfd, ...)
connect(sockfd, ...);
while (1) {
    int nfds = sockfd+1;
    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(sockfd, &rfds);
    int retval = select(nfds, &rfds, NULL, NULL, NULL);
    if (retval == -1)  break;
    if (retval > 0) {
        uint8_t rxdata[1024];
        size_t nr = read(sockfd, rxdata, 1024);
        if (!nr) {
            // socket closed.
            break;
        }
        // do your rx process here
    }
}

在工作线程上运行上面的代码。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48434976

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档