Socket描述了一个IP、端口对。它简化了程序员的操作,知道对方的IP以及PORT就可以给对方发送消息,再由服务器端来处理发送的这些消息。所以,Socket一定包含了通信的双发,即客户端(Client)与服务端(server)。
1)服务端利用Socket监听端口;
2)客户端发起连接;
3)服务端返回信息,建立连接,开始通信;
4)客户端,服务端断开连接。
1套接字(socket)概念
套接字(socket)是通信的基石(通信连接),是支持TCP/IP协议的网络通信的基本操作单元。
客户端与服务器之间的数据交互需要一个双向的通信连接
应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应 用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
2 建立socket连接
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发 给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
4、SOCKET连接与TCP连接
创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。
5、Socket连接与HTTP连接
由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网 络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。
而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。
一、网络编程
传输格式:
<from>zhangsan<from>
<to>lisi<to>
<body>一起吃晚上</body>
{
“from”:”zhangsan”,
”to”:”lisi”,
”body”:”中午一起吃饭”
}
服务器的工作流程:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,然后调用listen在相应的socket上监听,当accpet接收到一个连接服务请求时,将生成一个新的socket。服务器显示该客户机的IP地址,并通过新的socket向客户端发送字符串” hi,I am server!”。最后关闭该socket。
客户端的工作流程:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,请求连接服务器,通过新的socket向客户端发送字符串” hi,I am client!”。最后关闭该socket。 UDPSocket的具体实现
服务器的工作流程:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,接收到一个客户端时,服务器显示该客户端的IP地址,并将字串返回给客户端。
客户端的工作流程:首先调用socket函数创建一个Socket,填写服务器地址及端口号,从标准输入设备中取得字符串,将字符串传送给服务器端,并接收服务器端返回的字符串。最后关闭该socket。
1 // 2 // ServertListener.h 3 // 服务器监听 4 // 5 // Created by Vincent_Guo on 15/9/11. 6 // Copyright (c) 2015年 稳食哥 wechat:vg-ios. All rights reserved. 7 // 8 9 #import10 11 @interface ServertListener : NSObject 12 13 /**开始监听*/ 14 -(void)start; 15 16 @end 17 18 19 // 20 // ServertListener.m 21 // 服务器监听 22 // 23 // Created by Vincent_Guo on 15/9/11. 24 // Copyright (c) 2015年 稳食哥 wechat:vg-ios. All rights reserved. 25 // 26 27 #import "ServertListener.h" 28 #import "GCDAsyncSocket.h" 29 30 @interface ServertListener() 31 32 /** 33 * 服务端Socket 34 */ 35 @property(nonatomic,strong)GCDAsyncSocket *serverSocket; 36 37 /* 38 * 所有的客户端 39 */ 40 @property(nonatomic,strong)NSMutableArray *clientSockets; 41 42 @property(nonatomic,strong)NSMutableString *log;//日志 43 @end 44 45 @implementation ServertListener 46 47 -(NSMutableString *)log{ 48 if (!_log) { 49 _log = [NSMutableString string]; 50 } 51 52 return _log; 53 } 54 55 -(NSMutableArray *)clientSockets{ 56 if (!_clientSockets) { 57 _clientSockets = [NSMutableArray array]; 58 } 59 60 return _clientSockets; 61 } 62 63 -(instancetype)init{ 64 if (self = [super init]) { 65 self.serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)]; 66 } 67 68 return self; 69 } 70 71 72 -(void)start{ 73 NSError *error = nil; 74 BOOL success = [self.serverSocket acceptOnPort:5288 error:&error]; 75 if (success) { 76 NSLog(@"5288端口开启成功,并监听客户端请求连接..."); 77 }else{ 78 NSLog(@"5288端口开启失..."); 79 } 80 } 81 82 #pragma mark -代理 83 -(void)socket:(GCDAsyncSocket *)serverSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket{ 84 85 NSLog(@"%@ IP: %@: %zd 客户端请求连接...",clientSocket,clientSocket.connectedHost,clientSocket.connectedPort); 86 // 1.将客户端socket保存起来 87 [self.clientSockets addObject:clientSocket]; 88 89 // 2.一旦同意连接,监听数据读取,如果有数据会调用下面的代理方法 90 [clientSocket readDataWithTimeout:-1 tag:0]; 91 92 // 3.返回 "服务" 选项 93 NSMutableString *options = [NSMutableString string]; 94 [options appendString:@"欢迎来到10086在在线服务 请输入下面的数字选择服务\n"]; 95 [options appendString:@"[0]在线充值\n"]; 96 [options appendString:@"[1]在线投诉\n"]; 97 [options appendString:@"[2]优惠信息\n"]; 98 [options appendString:@"[3]special services\n"]; 99 [options appendString:@"[4]退出\n"];100 NSData *data = [options dataUsingEncoding:NSUTF8StringEncoding];101 [clientSocket writeData:data withTimeout:-1 tag:0];102 }103 104 105 106 -(void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag{107 108 // 1.客户端 传递的数据 转成字符串109 NSString *clientStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];110 111 NSString *log = [NSString stringWithFormat:@"IP:%@ %zd data:%@\n",clientSocket.connectedHost,clientSocket.connectedPort, clientStr];112 NSLog(@"%@",log);113 [self.log appendString:log];114 [self.log writeToFile:@"/Users/Fung/Desktop/server.log" atomically:NO encoding:NSUTF8StringEncoding error:nil];115 116 NSInteger serverCode = [clientStr integerValue];117 switch (serverCode) {118 case 0:119 [self writeDataWithSocket:clientSocket str:@"充值服务暂停,系统维护...\n"];120 break;121 case 1:122 [self writeDataWithSocket:clientSocket str:@"投诉服务暂停,系统维护\n"];123 break;124 case 2:125 [self writeDataWithSocket:clientSocket str:@"最近优惠服务,充一万,送5千。。\n"];126 break;127 case 3:128 [self writeDataWithSocket:clientSocket str:@"你傻啊,还真以为有特殊服务...\n"];129 break;130 case 4:131 [self exitWithSocket:clientSocket];132 break;133 134 default:135 [self writeDataWithSocket:clientSocket str:@"请说人话...\n"];136 break;137 }138 139 // 2.监听数据读取140 [clientSocket readDataWithTimeout:-1 tag:0];141 }142 143 144 145 -(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{146 147 NSLog(@"数据发送成功..");148 }149 150 -(void)exitWithSocket:(GCDAsyncSocket *)clientSocket{151 [self writeDataWithSocket:clientSocket str:@"成功退出\n"];152 [self.clientSockets removeObject:clientSocket];153 154 NSLog(@"当前在线用户个数:%ld",self.clientSockets.count);155 }156 157 #pragma mark -私有方法158 #pragma mark -写数据159 -(void)writeDataWithSocket:(GCDAsyncSocket *)clientSocket str:(NSString *)str{160 161 [clientSocket writeData:[str dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];162 163 }164 165 @end
1 // 2 // main.m 3 // 服务器监听 4 // 5 // Created by Vincent_Guo on 15/9/11. 6 // Copyright (c) 2015年 稳食哥 wechat:vg-ios. All rights reserved. 7 // 8 9 #import10 #import "ServertListener.h"11 12 int main(int argc, const char * argv[]) {13 @autoreleasepool {14 // insert code here...15 NSLog(@"Hello, World!");16 // 1.创建服务监听器17 ServertListener *listener = [[ServertListener alloc] init];18 19 // 2.开始监听20 [listener start];21 22 // 开启主运行循环23 [[NSRunLoop mainRunLoop] run];24 }25 return 0;26 }
1 // 2 // ViewController.m 3 // IOS_0406_群聊客户端 4 // 5 // Created by ma c on 16/4/6. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 实现聊天室 8 9 #import "ViewController.h" 10 #import "GCDAsyncSocket.h" 11 12 @interface ViewController ()13 14 @property (weak, nonatomic) IBOutlet UITextField *textField; 15 - (IBAction)send:(id)sender; 16 @property (weak, nonatomic) IBOutlet UITableView *tableView; 17 @property (nonatomic, strong) GCDAsyncSocket *clientSocket; 18 @property (nonatomic, strong) NSMutableArray *dataSource; 19 20 21 @end 22 23 @implementation ViewController 24 25 - (void)viewDidLoad { 26 [super viewDidLoad]; 27 //1.连接到群服务器 28 //1.1创建一个客户端的socket对象 29 GCDAsyncSocket *clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)]; 30 self.clientSocket = clientSocket; 31 //1.2发送连接请求 32 NSError *error = nil; 33 [clientSocket connectToHost:@"127.0.0.1" onPort:5288 error:&error]; 34 if (!error) { 35 NSLog(@"%@",error); 36 } 37 } 38 39 #pragma mark - GCDAsyncSocketDelegate 40 //连接成功 41 - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port 42 { 43 NSLog(@"连接成功"); 44 //监听读取数据 45 [sock readDataWithTimeout:-1 tag:0]; 46 } 47 //断开连接 48 - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err 49 { 50 NSLog(@"断开连接"); 51 } 52 53 - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag 54 { 55 NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 56 NSLog(@"%@",str); 57 //把消息添加到数据源 58 if (str) { 59 [self.dataSource addObject:str]; 60 61 //主线程中刷新表格 62 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 63 [self.tableView reloadData]; 64 }]; 65 } 66 //读完数据之后,继续监听读取数据 67 [sock readDataWithTimeout:-1 tag:0]; 68 69 } 70 71 72 73 #pragma mark - UITableViewDataSource 74 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 75 { 76 return self.dataSource.count; 77 } 78 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 79 { 80 static NSString *ID = @"cell"; 81 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; 82 //显示文字 83 cell.textLabel.text = self.dataSource[indexPath.row]; 84 return cell; 85 } 86 87 88 89 - (IBAction)send:(id)sender { 90 91 NSString *str = self.textField.text; 92 93 if (str.length == 0) { 94 NSLog(@"无数据发送"); 95 } else { 96 //发送数据 97 [self.clientSocket writeData:[str dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0]; 98 //把数据添加到数据源 99 [self.dataSource addObject:str];100 [self.tableView reloadData];101 }102 }103 104 #pragma mark - 懒加载105 - (NSMutableArray *)dataSource106 {107 if(!_dataSource){108 _dataSource = [NSMutableArray array];109 110 }111 return _dataSource;112 }113 114 @end
群聊服务器
1 // 2 // ChatServer.m 3 // IOS_0406_群聊天服务端 4 // 5 // Created by ma c on 16/4/6. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import "ChatServer.h"10 #import "GCDAsyncSocket.h"11 12 @interface ChatServer ()13 14 @property (nonatomic, strong) GCDAsyncSocket *serverSocket;15 //客户端所有socket对象16 @property (nonatomic, strong) NSMutableArray *clients;17 18 @end19 20 @implementation ChatServer21 22 - (void)start23 {24 //1.创建一个监听者对象25 GCDAsyncSocket *serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];26 //2.绑定端口并监听27 NSError *error = nil;28 [serverSocket acceptOnPort:5288 error:&error];29 if (!error) {30 NSLog(@"10086服务开启成功");31 }else{32 NSLog(@"10086服务开启失败");33 }34 self.serverSocket = serverSocket;35 }36 #pragma mark - GCDAsyncSocketDelegate37 - (void)socket:(GCDAsyncSocket *)severSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket38 {39 //NSLog(@"\nsever:%@----client:%@",severSocket, clientSocket);40 NSLog(@"host:%@, port:%d", clientSocket.connectedHost, clientSocket.connectedPort);41 //1.保存客户端的socket42 [self.clients addObject:clientSocket];43 NSLog(@"已有%ld人上线",self.clients.count);44 //2.监听客户端有没有数据上传45 //timeout=-1代表不超时 tag标识作用46 [clientSocket readDataWithTimeout:-1 tag:0];47 48 }49 //读取客户端请求的数据50 -(void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag51 {52 //NSLog(@"client:%@",clientSocket);53 NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];54 NSLog(@"%@",str);55 for (GCDAsyncSocket *socket in self.clients) {56 if (socket != clientSocket) {57 //处理请求,返回数据给客户端58 [socket writeData:data withTimeout:-1 tag:0];59 }60 }61 //每次读完数据后都要调用一次监听方法62 [clientSocket readDataWithTimeout:-1 tag:0];63 }64 65 #pragma mark - 懒加载66 - (NSMutableArray *)clients67 {68 if(!_clients){69 _clients = [NSMutableArray array];70 71 }72 return _clients;73 }74 @end