有一段時間沒有認真總結和寫博客了編程
前段時間找工做、進入工做階段。比較少靜下來認真總結,如今靜下心來總結一下最近的一些心得服務器
1、前言網絡
公司的項目用到了Socket編程,以前在學習的過程中,用到的更多的仍是http請求的方式。可是既然用到了就必須學習一下,因此就在網上找一些例子,而後想本身寫一個demo。但是發現不少寫iOS Socket的博客並無很詳細的說明,也多是大神們以爲其餘東西都淺顯易懂。app
本身專研了一下,將本身的一些理解總結出來,一方面整理本身的學習思路,另外一方面,爲一些和我有一樣困惑的小夥伴們,稍作指引。iphone
2、AsyncSocket介紹socket
1⃣️iOS中Socket編程的方式有哪些?函數
-BSD Socketoop
BSD Socket 是UNIX系統中通用的網絡接口,它不只支持各類不一樣的網絡類型,並且也是一種內部進程之間的通訊機制。而iOS系統其實本質就是UNIX,因此能夠用,可是比較複雜。學習
-CFSocketthis
CFSocket是蘋果提供給咱們的使用Socket的方式,可是用起來仍是會不太順手。固然想使用的話,能夠細細研究一下。
-AsyncSocket
此次博客的主講內容,也是咱們在開發項目中常常會用到的。
2⃣️爲何選擇AsyncSocket?
iphone的CFNetwork編程比較艱深。使用AsyncSocket開源庫來開發相對較簡單,幫助咱們封裝了不少東西。
3、AsyncSocket詳解
1⃣️說明
在咱們開發當中,咱們主要的任務是開發客戶端。因此詳解裏主要將客戶端的整個鏈接創建過程,以及在說明時候回調哪些函數。在後面的示例代碼中,也會給出服務器端的簡單開發。
2⃣️過程詳解
1.創建鏈接
- (int)connectServer:(NSString *)hostIP port:(int)hostPort
2.鏈接成功後,會回調的函數
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
3.發送數據
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
4.接受數據
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
5.斷開鏈接
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
- (void)onSocketDidDisconnect:(AsyncSocket *)sock
主要就是上述的幾個方法,只是說在真正開發當中,極可能咱們在收發數據的時候,咱們收發的數據並不只僅是一個字符串包裝成NSData便可,咱們極可能會發送結構體等類型,這個時候咱們就須要和服務器端的人員協做來開發:定義怎樣的結構體。
4、AsyncSocket示例
客戶端代碼
#import "ViewController.h" #define SRV_CONNECTED 0 #define SRV_CONNECT_SUC 1 #define SRV_CONNECT_FAIL 2 #define HOST_IP @"192.168.83.40" #define HOST_PORT 8008 @interface ViewController () { NSString *_content; } -(int) connectServer: (NSString *) hostIP port:(int) hostPort; -(void)showMessage:(NSString *) msg; @end @implementation ViewController @synthesize clientSocket,tbInputMsg,lblOutputMsg; #pragma mark - view lifecycle - (void)viewDidLoad { [super viewDidLoad]; [self connectServer:HOST_IP port:HOST_PORT]; } - (void)viewDidUnload { [super viewDidUnload]; [clientSocket release], clientSocket = nil; [tbInputMsg release], tbInputMsg = nil; [lblOutputMsg release], lblOutputMsg = nil; } - (int)connectServer:(NSString *)hostIP port:(int)hostPort { if (clientSocket == nil) { // 在須要聯接地方使用connectToHost聯接服務器 clientSocket = [[AsyncSocket alloc] initWithDelegate:self]; NSError *err = nil; if (![clientSocket connectToHost:hostIP onPort:hostPort error:&err]) { NSLog(@"Error %d:%@", err.code, [err localizedDescription]); UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[@"Connection failed to host" stringByAppendingString:hostIP] message:[NSString stringWithFormat:@"%d:%@",err.code,err.localizedDescription] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; return SRV_CONNECT_FAIL; } else { NSLog(@"Connected!"); return SRV_CONNECT_SUC; } } else { return SRV_CONNECTED; } } #pragma mark - IBAction // 發送數據 - (IBAction) sendMsg:(id)sender { NSString *inputMsgStr = tbInputMsg.text; NSString * content = [inputMsgStr stringByAppendingString:@"\r\n"]; NSLog(@"%@",content); NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding]; // NSData *data = [content dataUsingEncoding:NSISOLatin1StringEncoding]; [clientSocket writeData:data withTimeout:-1 tag:0]; } // 鏈接/從新鏈接 - (IBAction) reconnect:(id)sender { int stat = [self connectServer:HOST_IP port:HOST_PORT]; switch (stat) { case SRV_CONNECT_SUC: [self showMessage:@"connect success"]; break; case SRV_CONNECTED: [self showMessage:@"It's connected,don't agian"]; break; default: break; } } - (void)showMessage:(NSString *)msg { UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"Alert!" message:msg delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; } - (IBAction)textFieldDoneEditing:(id)sender { [tbInputMsg resignFirstResponder]; } - (IBAction)backgroundTouch:(id)sender { [tbInputMsg resignFirstResponder]; } #pragma mark socket delegate - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port { [clientSocket readDataWithTimeout:-1 tag:0]; } - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err { NSLog(@"Error"); } - (void)onSocketDidDisconnect:(AsyncSocket *)sock { NSString *msg = @"Sorry this connect is failure"; [self showMessage:msg]; [msg release]; clientSocket = nil; } - (void)onSocketDidSecure:(AsyncSocket *)sock { } // 接收到數據(能夠經過tag區分) -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { NSString* aStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; _content = lblOutputMsg.text; NSLog(@"Hava received datas is :%@",aStr); NSString *newStr = [NSString stringWithFormat:@"\n%@", aStr]; lblOutputMsg.text = [_content stringByAppendingString:newStr]; [aStr release]; [clientSocket readDataWithTimeout:-1 tag:0]; } @end
服務器端代碼
#import "SocketView.h" #import "AsyncSocket.h" #define WELCOME_MSG 0 #define ECHO_MSG 1 #define FORMAT(format, ...) [NSString stringWithFormat:(format), ##__VA_ARGS__] @interface SocketView (PrivateAPI) - (void)logError:(NSString *)msg; - (void)logInfo:(NSString *)msg; - (void)logMessage:(NSString *)msg; @end @implementation SocketView // 初始化 - (void)awakeFromNib { listenSocket = [[AsyncSocket alloc] initWithDelegate:self]; [listenSocket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; connectedSockets = [[NSMutableArray alloc] initWithCapacity:1]; isRunning = NO; [logView setString:@""]; // [portField setString:@"8080"]; } - (IBAction)startStop:(id)sender { if(!isRunning) { int port = [portField intValue]; if(port < 0 || port > 65535) { port = 0; // 會隨即取端口 } NSError *error = nil; if(![listenSocket acceptOnPort:port error:&error]) { [self logError:FORMAT(@"Error starting server: %@", error)]; return; } [self logInfo:FORMAT(@"Echo server started on port %hu", [listenSocket localPort])]; isRunning = YES; [portField setEnabled:NO]; [startStopButton setTitle:@"Stop"]; } else { // Stop accepting connections [listenSocket disconnect]; // Stop any client connections int i; for(i = 0; i < [connectedSockets count]; i++) { // Call disconnect on the socket, // which will invoke the onSocketDidDisconnect: method, // which will remove the socket from the list. [[connectedSockets objectAtIndex:i] disconnect]; } [self logInfo:@"Stopped Echo server"]; isRunning = false; [portField setEnabled:YES]; [startStopButton setTitle:@"Start"]; } } - (void)scrollToBottom { NSScrollView *scrollView = [logView enclosingScrollView]; NSPoint newScrollOrigin; if ([[scrollView documentView] isFlipped]) newScrollOrigin = NSMakePoint(0.0, NSMaxY([[scrollView documentView] frame])); else newScrollOrigin = NSMakePoint(0.0, 0.0); [[scrollView documentView] scrollPoint:newScrollOrigin]; } - (void)logError:(NSString *)msg { NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1]; [attributes setObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes]; [as autorelease]; [[logView textStorage] appendAttributedString:as]; [self scrollToBottom]; } - (void)logInfo:(NSString *)msg { NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1]; [attributes setObject:[NSColor purpleColor] forKey:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes]; [as autorelease]; [[logView textStorage] appendAttributedString:as]; [self scrollToBottom]; } - (void)logMessage:(NSString *)msg { NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1]; [attributes setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes]; [as autorelease]; [[logView textStorage] appendAttributedString:as]; [self scrollToBottom]; } - (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket { [connectedSockets addObject:newSocket]; } // 客戶鏈接成功! - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port { [self logInfo:FORMAT(@"Accepted client %@:%hu", host, port)]; NSString *welcomeMsg = @"恭喜您,已經經過scoket鏈接上服務器!"; NSData *welcomeData = [welcomeMsg dataUsingEncoding:NSUTF8StringEncoding]; [sock writeData:welcomeData withTimeout:-1 tag:WELCOME_MSG]; // We could call readDataToData:withTimeout:tag: here - that would be perfectly fine. // If we did this, we'd want to add a check in onSocket:didWriteDataWithTag: and only // queue another read if tag != WELCOME_MSG. } - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag { [sock readDataToData:[AsyncSocket CRLFData] withTimeout:-1 tag:0]; } // 接收到數據 - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length] - 2)]; NSString *recvMsg = [[[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding] autorelease]; if(recvMsg) { [self logMessage:recvMsg]; } else { [self logError:@"Error converting received data into UTF-8 String"]; } NSString *backStr = nil; for (AsyncSocket *socket in connectedSockets) { if ([sock isEqualTo:socket]) { backStr = [NSString stringWithFormat:@"我說: %@",recvMsg]; } else { backStr = [NSString stringWithFormat:@"他說: %@",recvMsg]; } } // 回發數據 NSData* backData = [backStr dataUsingEncoding:NSUTF8StringEncoding]; [sock writeData:backData withTimeout:-1 tag:ECHO_MSG]; } - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err { [self logInfo:FORMAT(@"Client Disconnected: %@:%hu", [sock connectedHost], [sock connectedPort])]; } - (void)onSocketDidDisconnect:(AsyncSocket *)sock { [connectedSockets removeObject:sock]; } @end
界面搭建