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 #import <Foundation/Foundation.h> 10 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()<GCDAsyncSocketDelegate> 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在在线服务 请输入下面的数字选择服务 "]; 95 [options appendString:@"[0]在线充值 "]; 96 [options appendString:@"[1]在线投诉 "]; 97 [options appendString:@"[2]优惠信息 "]; 98 [options appendString:@"[3]special services "]; 99 [options appendString:@"[4]退出 "]; 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:%@ ",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:@"充值服务暂停,系统维护... "]; 120 break; 121 case 1: 122 [self writeDataWithSocket:clientSocket str:@"投诉服务暂停,系统维护 "]; 123 break; 124 case 2: 125 [self writeDataWithSocket:clientSocket str:@"最近优惠服务,充一万,送5千。。 "]; 126 break; 127 case 3: 128 [self writeDataWithSocket:clientSocket str:@"你傻啊,还真以为有特殊服务... "]; 129 break; 130 case 4: 131 [self exitWithSocket:clientSocket]; 132 break; 133 134 default: 135 [self writeDataWithSocket:clientSocket str:@"请说人话... "]; 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:@"成功退出 "]; 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 #import <Foundation/Foundation.h> 10 #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 ()<GCDAsyncSocketDelegate,UITableViewDataSource> 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 *)dataSource 106 { 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 ()<GCDAsyncSocketDelegate> 13 14 @property (nonatomic, strong) GCDAsyncSocket *serverSocket; 15 //客户端所有socket对象 16 @property (nonatomic, strong) NSMutableArray *clients; 17 18 @end 19 20 @implementation ChatServer 21 22 - (void)start 23 { 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 - GCDAsyncSocketDelegate 37 - (void)socket:(GCDAsyncSocket *)severSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket 38 { 39 //NSLog(@" sever:%@----client:%@",severSocket, clientSocket); 40 NSLog(@"host:%@, port:%d", clientSocket.connectedHost, clientSocket.connectedPort); 41 //1.保存客户端的socket 42 [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)tag 51 { 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 *)clients 67 { 68 if(!_clients){ 69 _clients = [NSMutableArray array]; 70 71 } 72 return _clients; 73 } 74 @end