iOS - CFSocket 的使用


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];
    	}
    

    Socket8

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];
    	}
    

    Socket9


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM