iOS 即时通信demo(CFNetwork)

服务器:

#import "ViewController.h"
#import <sys/socket.h>
#import <arpa/inet.h>

@interface ViewController ()
{

  
}
@end

@implementation ViewController

CFWriteStreamRef oStream;

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];
    
    [self initSocket];
    
    
}

-(void)initSocket{

    @autoreleasepool {
        
        //建立Socket,指定TCPServerAcceptCallBack
        //做为kCFFSocketAcceptCallBack事件的监听函数
        CFSocketRef _socket = CFSocketCreate(kCFAllocatorDefault,
                                             PF_INET,/*指定协议族,若是参数为0或者负数,则默认为PF_INET*/
                                             SOCK_STREAM,/*指定Socket类型,若是协议族为PF_INET,且该参数为0或者负数,则它会默认为SOCK_STREAM,若是要使用UDP协议,则该参数指定为SOCK_DGRAM*/
                                             IPPROTO_TCP ,/*指定通信协议。若是前一个参数为SOCK_STREAM,则默认为使用TCP协议,若是前一个参数为SOCK_DGRAM,则默认使用UDP协议*/
                                             kCFSocketAcceptCallBack,/*指定下一个函数所监听的事件类型*/
                                             TCPServerAcceptCallBack,
                                             NULL);
        
        if (_socket == NULL) {
            
            NSLog(@"建立Socket失败!");
            return;
        }
        
        int optval = 1;
        //设置容许重用本地地址和端口
        setsockopt(CFSocketGetNative(_socket), SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(optval));
        
        //定义sockaddr_in类型的变量,该变量将做为CFSocket的地址
        struct sockaddr_in addr4;
        memset(&addr4, 0, sizeof(addr4));
        addr4.sin_len = sizeof(addr4);
        addr4.sin_family = AF_INET;
        //设置该服务器监听本机任意可用的IP地址
//        addr4.sin_addr.s_addr = htonl(INADDR_ANY);
        //设置服务器监听地址
        addr4.sin_addr.s_addr = inet_addr("192.168.199.171");
        //设置服务器监听端口
        addr4.sin_port = htons(30000);
        //将IPv4的地址转换为CFDataRef
        CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4, sizeof(addr4));
        //将CFSocket绑定到指定IP地址
        if (CFSocketSetAddress(_socket, address) != kCFSocketSuccess) {
            
            NSLog(@"地址绑定失败!");
            //若是_socket不为NULL,则释放_socket
            if (_socket) {
                
                CFRelease(_socket);
                exit(1);
            }
            _socket = NULL;
            
        }
        
        NSLog(@"----启动循环监听客户端链接---");
        //获取当前线程的CFRunLoop
        CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
        //将_socket包装成CFRunLoopSource
        CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);
        //为CFRunLoop对象添加source
        CFRunLoopAddSource(cfRunLoop, source, kCFRunLoopCommonModes);
        CFRelease(source);
        //运行当前线程的CFRunLoop
        CFRunLoopRun();
        
    }
    


}


void readStream(CFReadStreamRef iStream,
                CFStreamEventType evenType,
                void *clientCallBackInfo)
{
    UInt8 buff[2048];
    CFIndex hasRead = CFReadStreamRead(iStream, buff, 2048);
    if (hasRead > 0) {
        
        //强制只处理hasRead前面的数据
        buff[hasRead] = '\0';
        printf("接收到数据:%s\n",buff);
        
        const char *str = "OK!你收到了Mac服务器的消息!\n";
        
        //向客户端输出数据
        CFWriteStreamWrite(oStream, (UInt8 *)str, strlen(str) + 1);
    }


}

//有客户端链接进来的回调函数
void TCPServerAcceptCallBack(CFSocketRef socket,
                             CFSocketCallBackType type,
                             CFDataRef address,
                             const void *data,
                             void *info)
{

    //若是有客户端Socket链接进来
    if (kCFSocketAcceptCallBack == type) {
        
        //获取本地Socket的Handle
        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 ) {
            
            NSLog(@"error");
            exit(1);
        }
        
        //获取链接信息
        struct sockaddr_in *addr_in = (struct sockaddr_in *)name;
        NSLog(@"%s:%d链接进来了",inet_ntoa(addr_in->sin_addr),addr_in->sin_port);
        CFReadStreamRef iStream;
        //建立一组可读/写的CFStream
        CFStreamCreatePairWithSocket(kCFAllocatorDefault,
                                     nativeSocketHandle,
                                     &iStream,
                                     &oStream);
        if (iStream && oStream) {
            
            //打开输入流和输出流
            CFReadStreamOpen(iStream);
            CFWriteStreamOpen(oStream);
            CFStreamClientContext streamContext = {0,NULL,NULL,NULL};
            if (!CFReadStreamSetClient(iStream,
                                       kCFStreamEventHasBytesAvailable,
                                       readStream,/*回调函数,当有可读的数据时调用*/
                                       &streamContext))
            {
                
                exit(1);
                
            }
            
            CFReadStreamScheduleWithRunLoop(iStream,
                                            CFRunLoopGetCurrent(),
                                            kCFRunLoopCommonModes);
            
            const char *str = "OK!你收到了Mac服务器的消息!\n";
            
            //向客户端输出数据
            CFWriteStreamWrite(oStream, (UInt8 *)str, strlen(str) + 1);
        }
        
    }

}

@end

客户端:

@interface ViewController ()<UIAlertViewDelegate>
{
    
    CFSocketRef _socket;
    NSString *myName;
    BOOL isOnline;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"名字" message:@"请输入您的名字" delegate:self cancelButtonTitle:@"肯定" otherButtonTitles: nil];
    
    alert.alertViewStyle = UIAlertViewStylePlainTextInput;
    [alert show];
    
}


///////////////////监听来自服务器的信息///////////////////

-(void)ReadStream

{
    
    char buffer[255];
    int hasRead;
    
    //与本机关联的Socket若是已经失败,则返回-1:INVALID_SOCKET
    while ((hasRead = recv(CFSocketGetNative(_socket), buffer, sizeof(buffer), 0))) {
        
        NSString *content = [[NSString alloc] initWithBytes:buffer length:hasRead encoding:NSUTF8StringEncoding];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            self.showView.text = [NSString stringWithFormat:@"%@\n%@",content,self.showView.text];
            
            
            
        });
    }

    
}


- (IBAction)send:(id)sender {
    
    if (isOnline) {
        
        NSString *stringTosend = [NSString stringWithFormat:@"%@说:%@",myName,self.inputField.text];
        self.inputField.text = nil;
        const char* data = [stringTosend UTF8String];
        send(CFSocketGetNative(_socket), data, strlen(data) + 1, 1);
    }
    else{
    
        NSLog(@"暂未链接服务器");
    
    }
}


- (IBAction)finishEdit:(id)sender {
    
    [sender resignFirstResponder];
}


- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{

    myName = [alertView textFieldAtIndex:0].text;
    
    //建立Socket,无须回调函数
    _socket = CFSocketCreate(kCFAllocatorDefault,
                             PF_INET,
                             SOCK_STREAM,
                             IPPROTO_TCP,
                             kCFSocketNoCallBack,
                             nil,
                             NULL);
    if (_socket != nil) {
        
        struct sockaddr_in addr4;
        memset(&addr4, 0, sizeof(addr4));
        addr4.sin_len = sizeof(addr4);
        addr4.sin_family = AF_INET;
        //设置远程服务器的IP地址
        //        addr4.sin_addr.s_addr = htonl(INADDR_ANY);
        //设置远程服务器监听地址
        addr4.sin_addr.s_addr = inet_addr("192.168.199.171");
        //设置远程服务器监听端口
        addr4.sin_port = htons(30000);
        //将IPv4的地址转换为CFDataRef
        CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4, sizeof(addr4));
        
        CFSocketError result = CFSocketConnectToAddress(_socket,
                                                        address,
                                                        5);
        
        //将CFSocket绑定到指定IP地址
        if (result == kCFSocketSuccess) {
            
            isOnline = YES;
            
            //启用新线程来读取服务器响应的数据
            [NSThread detachNewThreadSelector:@selector(ReadStream) toTarget:self withObject:nil];
            
        }

        
        
        
    }
    

}

@end

通信效果:服务器

服务端如图:socket

客户端如图:async

相关文章
相关标签/搜索