zoukankan      html  css  js  c++  java
  • iOS

    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 失败...
      ";
      	        [self showMessage:logStr];
      	        
      	        return;
      	        
      	    } else {
      	        
      	        logStr = @"创建 socket 成功...
      ";
      	        [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 连接失败...
      ";
      	        [self showMessage:logStr];
      	        
      	        return;
      	        
      	    } else {
      	        
      	        logStr = [NSString stringWithFormat:@"socket 连接 %@ 成功...
      ", _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:%@
      ", [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
      ");
      	    } else {
      	        NSLog(@"connect success
      ");
      	    }
      	}
      	
      	#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:%@
      ", _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 失败...
      ";
      	        [self showMessage:logStr];
      	        
      	        return;
      	        
      	    } else {
      	        
      	        logStr = @"创建 socket 成功...
      ";
      	        [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 绑定失败...
      ";
      	        [self showMessage:logStr];
      	        
      	        CFRelease(self.serverSockfd);
      	        self.serverSockfd = NULL;
      	        
      	        return;
      	        
      	    } else {
      	        
      	        logStr = @"socket 绑定成功...
      ";
      	        [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
      ", 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:%@
      ", [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:%@
      ", _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

  • 相关阅读:
    写了一个随机图片API接口,用来做博客园随机背景,欢迎使用,禁止爬取,需要套图可以直接联系博主
    CentOS7.5 部署Flask项目, 并且安装selenium和Chrome、 Chromedriver、tesseract和MongoDB,执行服务和脚本
    重新写了一个东南大学体育场馆的定时预约脚本,使用selenium和chromedriver实现,tesseract识别验证码
    Python基础到进阶之02 文件读写和JSON格式
    Python基础到进阶之01类函数、实例函数和静态函数
    nmap终极使用手册(超详细)
    基于serverless+hexo三分钟部署博客
    【转】Serverless 的运行原理与组件架构
    【转】Serverless 基本概念入门
    什么是服务端渲染,为什么要使用服务端渲染
  • 原文地址:https://www.cnblogs.com/QianChia/p/6391989.html
Copyright © 2011-2022 走看看