zoukankan      html  css  js  c++  java
  • iOS中基于 Socket 的 C/S 结构网络通信(中)

                   结合上一篇的知识。接下来将介绍基于 TCP 协议的 Socket  编程。因为 Socket 须要有client和服务端,那么如今实现的是关于服务端的简单程序。服务端採用的是CFStream 类来实现的。

         这个服务端是把Xcode中的 Command Line Tool 来作为服务端的;当然,你也能够把 iPhone 作为服务端。可是要利用其它的框架,比方 AsyncSocket (https://github.com/roustem/AsyncSocket) ,里面有分为 UDP 和 TCP 实现的 Socket。源程序里也有很多关于数据流的操作,能够作为深入理解来用,当然也是比較复杂的。

         以下来看一下简单实现的 TCP 服务端程序:(TCPServer.m)

    <pre name="code" class="objc">#import <CoreFoundation/CoreFoundation.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #define PORT 8734
    
    
    void AcceptCallBack(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
    
    void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void *);
    
    void ReadStreamClientCallBack (CFReadStreamRef stream, CFStreamEventType eventType,void *);
    
    int main(int argc, const char * argv[])
    {
        /* 定义一个Server Socket引用 */
        CFSocketRef sserver;
    
        /* 创建socket context */
        CFSocketContext CTX = { 0, NULL, NULL, NULL, NULL };
        
        /* 创建server socket  TCP IPv4 设置回调函数 */
        sserver = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP,
                                 kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &CTX);
        if (sserver == NULL)
            return -1;
        
        
        /* 设置是否又一次绑定标志 */
        int yes = 1;    
        /* 设置socket属性 SOL_SOCKET是设置tcp SO_REUSEADDR是又一次绑定。yes 是否又一次绑定*/
        setsockopt(CFSocketGetNative(sserver), SOL_SOCKET, SO_REUSEADDR,
                   (void *)&yes, sizeof(yes));
        
        /* 设置端口和地址 */
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));             //memset函数对指定的地址进行内存拷贝
        addr.sin_len = sizeof(addr);
        addr.sin_family = AF_INET;                  //AF_INET是设置 IPv4
        addr.sin_port = htons(PORT);                //htons函数 无符号短整型数转换成“网络字节序”
        addr.sin_addr.s_addr = htonl(INADDR_ANY);   //INADDR_ANY有内核分配。htonl函数 无符号长整型数转换成“网络字节序”
            
        /* 从指定字节缓冲区复制。一个不可变的CFData对象*/
        CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8*)&addr, sizeof(addr));
        
        /* 设置Socket*/
        if (CFSocketSetAddress(sserver, (CFDataRef)address) != kCFSocketSuccess) {
            fprintf(stderr, "Socket绑定失败
    ");
            CFRelease(sserver);
            return -1;
        }
        
        /* 创建一个Run Loop Socket源 */
        CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sserver, 0);
        /* Socket源加入到Run Loop中 */
        CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
        CFRelease(sourceRef);
        
        printf("Socket listening on port %d
    ", PORT);
        /* 执行Loop */
        CFRunLoopRun();
        
        
    }
    
    /* 接收client请求后,回调函数  */
    void AcceptCallBack(
                        CFSocketRef socket,
                        CFSocketCallBackType type,
                        CFDataRef address,
                        const void *data,
                        void *info)
    {
        CFReadStreamRef readStream = NULL;
        CFWriteStreamRef writeStream = NULL;
        
        /* data 參数涵义是,假设是kCFSocketAcceptCallBack类型,data是CFSocketNativeHandle类型的指针 */
        CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data;
        
        /* 创建读写Socket流 */
        CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock,
                                     &readStream, &writeStream);
        
        if (!readStream || !writeStream) {
            close(sock);
            fprintf(stderr, "CFStreamCreatePairWithSocket() 失败
    ");
            return;
        }
        
        CFStreamClientContext streamCtxt = {0, NULL, NULL, NULL, NULL};
        // 注冊两种回调函数
        CFReadStreamSetClient(readStream, kCFStreamEventHasBytesAvailable, ReadStreamClientCallBack, &streamCtxt);
        CFWriteStreamSetClient(writeStream, kCFStreamEventCanAcceptBytes, WriteStreamClientCallBack, &streamCtxt);
        
        //加入到循环其中
        CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
        CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
        
        CFReadStreamOpen(readStream);
        CFWriteStreamOpen(writeStream);
        
    }
    
    /* 读取流操作 client有数据过来时候调用 */
    void ReadStreamClientCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo){
        
        UInt8 buff[255];
        CFReadStreamRef inputStream = stream;
        
        if(NULL != inputStream)
        {        
            CFReadStreamRead(stream, buff, 255);        
            printf("接受到数据:%s
    ",buff);        
            CFReadStreamClose(inputStream);
            CFReadStreamUnscheduleFromRunLoop(inputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
            inputStream = NULL;
        }
    }
    
    /* 写入流操作 client在读取数据时候调用 */
    void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo)
    {
        CFWriteStreamRef    outputStream = stream;
        //输出
        UInt8 buff[] = "你好,client!";       //向client发送的信息
        if(NULL != outputStream)
        {
            CFWriteStreamWrite(outputStream, buff, strlen((const char*)buff)+1);
            //关闭输出流
            CFWriteStreamClose(outputStream);
            CFWriteStreamUnscheduleFromRunLoop(outputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
            outputStream = NULL;
        }
    }


    
                   此段程序仅仅涉及比較简单的数据流操作,具体的数据流操作请參考 AsyncSocket 的源代码;至此,那么一个服务端就已经实现了。


          未完待续......

  • 相关阅读:
    [转]汇编语言的准备知识给初次接触汇编者 4
    Javascript实现页面跳转的几种方式收藏
    [转]汇编语言的准备知识给初次接触汇编者 1
    jQuery常用的函数的简单描述 便于查阅
    解决win7光驱驱动找不到的问题
    tar
    liunx64运行飞信的问题
    centos6禁用ipv6
    仍然是yum问题rhel6使用centos的yum源
    【MyBatis】使用MyBatis的分页组件PageHelper时,多表关联下使用别名查询时,前台传参过来,根据参数排序的解决方案
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/7112629.html
Copyright © 2011-2022 走看看