1、CFSocket
苹果对对底层 BSD Socket 进行轻量级的封装(纯 C)。
主要使用的 API:CFSocekt 用于建立连接,CFStream 用于读写数据。
2、基本使用
2.1 Client 客户端
TCP 客户端
// 包含头文件 #import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> @property (weak, nonatomic) IBOutlet UITextField *desIPTF; @property (weak, nonatomic) IBOutlet UITextField *desPortTF; @property (weak, nonatomic) IBOutlet UITextView *recvTextView; @property (weak, nonatomic) IBOutlet UITextField *sendTF; @property (nonatomic, assign) CFSocketRef clientSockfd; #pragma mark - 创建 TCP 连接 - (IBAction)connectTCPServer:(id)sender { [NSThread detachNewThreadSelector:@selector(connectTCPSer) toTarget:self withObject:nil]; } - (void)connectTCPSer { BOOL success; // 创建 socket self.clientSockfd = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketConnectCallBack, ServerConnectCallBack, NULL); success = (self.clientSockfd != nil); NSString *logStr = nil; if (success == NO) { logStr = @"创建 socket 失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"创建 socket 成功...\n"; [self showMessage:logStr]; // 服务器地址 struct sockaddr_in ser_addr; memset(&ser_addr, 0, sizeof(ser_addr)); ser_addr.sin_len = sizeof(ser_addr); ser_addr.sin_family = AF_INET; ser_addr.sin_port = htons(_desPortTF.text.intValue); ser_addr.sin_addr.s_addr = inet_addr(_desIPTF.text.UTF8String); CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&ser_addr, sizeof((ser_addr))); // 连接 CFSocketError result = CFSocketConnectToAddress(self.clientSockfd, address, 5); CFRelease(address); success = (result == kCFSocketSuccess); } if (success == NO) { logStr = @"socket 连接失败...\n"; [self showMessage:logStr]; return; } else { logStr = [NSString stringWithFormat:@"socket 连接 %@ 成功...\n", _desIPTF.text]; [self showMessage:logStr]; char buf[2048]; do { // 接收数据 ssize_t recvLen = recv(CFSocketGetNative(self.clientSockfd), buf, sizeof(buf), 0); if (recvLen > 0) { logStr = [NSString stringWithFormat:@"recv:%@\n", [NSString stringWithFormat:@"%s", buf]]; [self showMessage:logStr]; } } while (strcmp(buf, "exit") != 0); CFRunLoopRef cfrl = CFRunLoopGetCurrent(); CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, self.clientSockfd, 0); CFRunLoopAddSource(cfrl, source, kCFRunLoopCommonModes); CFRelease(source); CFRunLoopRun(); } } // 连接成功的回调函数 void ServerConnectCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void * data, void *info) { if (data != NULL) { NSLog(@"connect\n"); } else { NSLog(@"connect success\n"); } } #pragma mark - 发送数据 - (IBAction)btnClick:(id)sender { if (_sendTF.text.length == 0) { return; } else { // 发送数据 ssize_t sendLen = send(CFSocketGetNative(self.clientSockfd), _sendTF.text.UTF8String, strlen(_sendTF.text.UTF8String), 0); if (sendLen > 0) { NSString *logStr = [NSString stringWithFormat:@"send:%@\n", _sendTF.text]; [self showMessage:logStr]; } } } // 显示信息 - (void)showMessage:(NSString *)msg { dispatch_async(dispatch_get_main_queue(), ^{ _recvTextView.text = [_recvTextView.text stringByAppendingString:msg]; }); } // 键盘回收 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.view endEditing:YES]; }
2.2 Server 服务端
TCP 服务端
// 包含头文件 #import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> #import <ifaddrs.h> static ViewController *selfClass = nil; @property (weak, nonatomic) IBOutlet UITextField *locIPTF; @property (weak, nonatomic) IBOutlet UITextField *locPortTF; @property (weak, nonatomic) IBOutlet UITextView *recvTextView; @property (weak, nonatomic) IBOutlet UITextField *sendTF; @property (nonatomic, assign) CFSocketRef serverSockfd; @property (nonatomic, assign) CFWriteStreamRef outputStream; - (void)viewDidLoad { [super viewDidLoad]; NSString *ip_addr = [self getIPAddress]; _locIPTF.text = ip_addr; // 函数指针指向本身 selfClass = self; } #pragma mark - 创建 TCP 监听 - (IBAction)createdTCPServer:(id)sender { [sender setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [NSThread detachNewThreadSelector:@selector(createdTCPSer) toTarget:self withObject:nil]; } - (void)createdTCPSer { BOOL success; // 创建 socket self.serverSockfd = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, TCPServerAcceptCallBack, NULL); success = (self.serverSockfd != NULL); NSString *logStr = nil; if (success == NO) { logStr = @"创建 socket 失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"创建 socket 成功...\n"; [self showMessage:logStr]; int optVal = 1; setsockopt(CFSocketGetNative(self.serverSockfd), SOL_SOCKET, SO_REUSEADDR, (void*)&optVal,sizeof(optVal)); // 本地地址 struct sockaddr_in loc_addr; memset(&loc_addr, 0, sizeof(loc_addr)); loc_addr.sin_family = AF_INET; loc_addr.sin_port = htons(_locPortTF.text.intValue); loc_addr.sin_addr.s_addr = inet_addr(_locIPTF.text.UTF8String); CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8*)&loc_addr, sizeof(loc_addr)); // 绑定 CFSocketError err = CFSocketSetAddress(self.serverSockfd, address); CFRelease(address); success = (err == kCFSocketSuccess); } if (success == NO) { logStr = @"socket 绑定失败...\n"; [self showMessage:logStr]; CFRelease(self.serverSockfd); self.serverSockfd = NULL; return; } else { logStr = @"socket 绑定成功...\n"; [self showMessage:logStr]; CFRunLoopRef cfRunloop = CFRunLoopGetCurrent(); CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, self.serverSockfd, 0); CFRunLoopAddSource(cfRunloop, source, kCFRunLoopCommonModes); CFRelease(source); CFRunLoopRun(); } } // 客户端连接成功的回调函数 void TCPServerAcceptCallBack(CFSocketRef socket ,CFSocketCallBackType type,CFDataRef address,const void *data, void *info) { // 客户端连接 if (kCFSocketAcceptCallBack == type) { // data the handle of socket CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data; uint8_t name[SOCK_MAXADDRLEN]; socklen_t nameLen = sizeof(name); // 获取对方 socket 信息,还有 getsocketname() 函数用于获取本程序所在 socket 信息 if (getpeername(nativeSocketHandle, (struct sockaddr *)name, &nameLen) != 0) { exit(1); } // 获取连接的客户端信息 struct sockaddr_in * addr_in = (struct sockaddr_in *)name; char *ip_addr = inet_ntoa(addr_in->sin_addr); int ip_port = addr_in->sin_port; NSString *logStr = [NSString stringWithFormat:@"已连接:%s,port:%d\n", ip_addr, ip_port]; [selfClass showMessage:logStr]; // 创建一对输入输出流用于读写数据 CFReadStreamRef iStream; CFWriteStreamRef oStream; // 创建一组可读/写的 CFStreame CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &iStream, &oStream); if (iStream && oStream) { // 打开输入流和输出流 CFReadStreamOpen(iStream); CFWriteStreamOpen(oStream); CFStreamClientContext streamContext = {0, NULL, NULL, NULL}; // if have data to read call the readStream function if (!CFReadStreamSetClient(iStream, kCFStreamEventHasBytesAvailable, readStream, &streamContext)) { exit(1); } CFReadStreamScheduleWithRunLoop(iStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); selfClass.outputStream = oStream; } } } // 读取数据的回调函数 void readStream(CFReadStreamRef iStream, CFStreamEventType eventType, void * clientCallBackInfo){ char buf[2048]; bzero(buf, sizeof(buf)); do { // 接收数据 CFIndex dex = CFReadStreamRead(iStream, (UInt8 *)buf, sizeof(buf)); if (dex > 0) { NSString *logStr = [NSString stringWithFormat:@"recv:%@\n", [NSString stringWithFormat:@"%s", buf]]; [selfClass showMessage:logStr]; } } while (strcmp(buf, "exit") != 0); } #pragma mark - 发送数据 - (IBAction)btnClick:(id)sender { if (_sendTF.text.length == 0) { return; } else { // 发送数据 CFIndex dex = CFWriteStreamWrite(selfClass.outputStream, (UInt8 *)_sendTF.text.UTF8String, strlen(_sendTF.text.UTF8String)+1); if (dex > 0) { NSString *logStr = [NSString stringWithFormat:@"send:%@\n", _sendTF.text]; [self showMessage:logStr]; } } } #pragma mark - 获取本地 IP 地址 - (NSString *)getIPAddress { NSString *address = @"error"; struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL; int success = 0; // retrieve the current interfaces - returns 0 on success success = getifaddrs(&interfaces); if (success == 0) { // Loop through linked list of interfaces temp_addr = interfaces; while (temp_addr != NULL) { if (temp_addr->ifa_addr->sa_family == AF_INET) { // Check if interface is en0 which is the wifi connection on the iPhone if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { // Get NSString from C String address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; } } temp_addr = temp_addr->ifa_next; } } // Free memory freeifaddrs(interfaces); return address; } // 显示信息 - (void)showMessage:(NSString *)msg { dispatch_async(dispatch_get_main_queue(), ^{ _recvTextView.text = [_recvTextView.text stringByAppendingString:msg]; }); } // 键盘回收 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.view endEditing:YES]; }