有一段时间没有认真总结和写博客了
前段时间找工作、进入工作阶段。比较少静下来认真总结,现在静下心来总结一下最近的一些心得
前言 AsyncSocket介绍 AsyncSocket详解 AsyncSocket示例 一、前言
公司的项目用到了Socket编程,之前在学习的过程当中,用到的更多的还是http请求的方式。但是既然用到了就必须学习一下,所以就在网上找一些例子,然后想自己写一个demo。可是发现很多写iOS Socket的博客并没有很详细的说明,也可能是大神们觉得其他东西都浅显易懂。
自己专研了一下,将自己的一些理解总结出来,一方面整理自己的学习思路,另一方面,为一些和我有同样困惑的小伙伴们,稍做指引。
二、AsyncSocket介绍
1⃣️iOS中Socket编程的方式有哪些?
-BSD Socket
BSD Socket 是UNIX系统中通用的网络接口,它不仅支持各种不同的网络类型,而且也是一种内部进程之间的通信机制。而iOS系统其实本质就是UNIX,所以可以用,但是比较复杂。
-CFSocket
CFSocket是苹果提供给我们的使用Socket的方式,但是用起来还是会不太顺手。当然想使用的话,可以细细研究一下。
-AsyncSocket
这次博客的主讲内容,也是我们在开发项目中经常会用到的。
2⃣️为什么选择AsyncSocket?
iphone的CFNetwork编程比较艰深。使用AsyncSocket开源库来开发相对较简单,帮助我们封装了很多东西。
三、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.断开连接
主要就是上述的几个方法,只是说在真正开发当中,很可能我们在收发数据的时候,我们收发的数据并不仅仅是一个字符串包装成NSData即可,我们很可能会发送结构体等类型,这个时候我们就需要和服务器端的人员协作来开发:定义怎样的结构体。
四、AsyncSocket示例
客户端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 @interface ViewController () { NSString *_content; } -(int) connectServer: (NSString *) hostIP port:(int) hostPort; -(void)showMessage:(NSString *) msg; @end @implementation ViewController @synthesize clientSocket,tbInputMsg,lblOutputMsg; - (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; } } // 发送数据 - (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]; } - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port { [clientSocket read DataWithTimeout:-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 read DataWithTimeout:-1 tag:0 ]; } @end
服务器端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 @interface SocketView (PrivateAPI) - (void)log Error:(NSString *)msg; - (void)log Info:(NSString *)msg; - (void)log Message:(NSString *)msg; @end @implementation SocketView // 初始化 - (void)awakeFromNib { listenSocket = [[AsyncSocket alloc] initWithDelegate:self]; [listenSocket set RunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; connectedSockets = [[NSMutableArray alloc] initWithCapacity:1 ]; isRunning = NO; [log View set String:@"" ]; // [portField set String:@"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 log Error:FORMAT(@"Error starting server: %@" , error)]; return ; } [self log Info:FORMAT(@"Echo server started on port %hu" , [listenSocket local Port])]; isRunning = YES; [portField set Enabled:NO]; [startStopButton set Title:@"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 log Info:@"Stopped Echo server" ]; isRunning = false ; [portField set Enabled:YES]; [startStopButton set Title:@"Start" ]; } } - (void)scrollToBottom { NSScrollView *scrollView = [log View 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)log Error:(NSString *)msg { NSString *paragraph = [NSString stringWithFormat:@"%@\n" , msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1 ]; [attributes set Object:[NSColor redColor] for Key:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes]; [as autorelease]; [[log View textStorage] appendAttributedString:as]; [self scrollToBottom]; } - (void)log Info:(NSString *)msg { NSString *paragraph = [NSString stringWithFormat:@"%@\n" , msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1 ]; [attributes set Object:[NSColor purpleColor] for Key:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes]; [as autorelease]; [[log View textStorage] appendAttributedString:as]; [self scrollToBottom]; } - (void)log Message:(NSString *)msg { NSString *paragraph = [NSString stringWithFormat:@"%@\n" , msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1 ]; [attributes set Object:[NSColor blackColor] for Key:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes]; [as autorelease]; [[log View 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 log Info: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 read DataToData:withTimeout:tag: here - that would be perfectly fine. // If we did this, we 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 read DataToData:[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 log Message:recvMsg]; } else { [self log Error:@"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 log Info:FORMAT(@"Client Disconnected: %@:%hu" , [sock connectedHost], [sock connectedPort])]; } - (void)onSocketDidDisconnect:(AsyncSocket *)sock { [connectedSockets removeObject:sock]; } @end
界面搭建界面搭建
源代码 (/CodeSource/AsyncSocket/AsyncSocket.zip)