结合上一篇的知识。接下来将介绍基于 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 的源代码;至此,那么一个服务端就已经实现了。
未完待续......