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

相關文章
相關標籤/搜索