#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