IOS-Socket

Socket通信原理与实践

 

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连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。

 

 

 

一、网络编程

网络模型
OSI(开放系统互联Open System Interconnection)TCP/IP参考模型
 
网络通讯要素
IP地址
端口号
传输协议
 
TCP\UDP:数据传输方式
HTTP\XMPP:数据传输格式
 
二、网络通讯要素
IP地址(唯一标示网络设备的):
网络中设备的标示
不易记忆,可以用主机名
本地回环地址:127.0.0.1 主机名:localhost
端口号(定位程序)
用于标示进程的逻辑地址,不同进程的标示
有效端口:0~65535,其中0~1024由系统使用或者保留端口,开发中不要使用1024以下的端口
传输协议(用什么样的方式进行交互)
通讯的规则
常见协议:TCP、UDP
 
URL(统一资源定位) http://ip:80/ 文件路径
 
三、TCP & UDP
TCP(传输控制协议)
建立连接,形成传输数据的通道
在连接中进行大数据传输(数据大小不收限制)
通过三次握手完成连接,是可靠协议,安全送达
必须建立连接,效率会稍低

 

UDP(用户数据报协议)
将数据及源和目的封装成数据包中,不需要建立连接
每个数据报的大小限制在64K之内
因为无需连接,因此是不可靠协议
不需要建立连接,速度快
 
四、Socket(套接字)
1.Socket就是为网络服务提供的一种机制
通信的两端都是Socket
网络通信其实就是Socket间的通信
数据在两个Socket间通过IO传输
 
2.实现Socket服务端监听
实现socket的监听方法
(1)使用C语言实现,
(2)使用CocoaAsyncSocket第三方框,内部是对C的封装
Telnet命令 telnet host port/telnet 192.168.10.10 5288
telnet命令是连接服务器上的某个端口对应的服务
 
3.Socket层上的协议
Socket层上的协议指的数据传输的格式
HTTP协议
传输格式:假设: 这是假设,实际 http 的格式不是这样的。
http1.1,content-type:multipart/form-data,content-length:188,body:username=zhangsan&password=123456
 
XMPP协议,是一款即时通讯协议
 可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息

  传输格式:

  <from>zhangsan<from>

   <to>lisi<to>

   <body>一起吃晚上</body>

 

自定义即时通讯协议,json格式

{

“from”:”zhangsan”,

”to”:”lisi”,

”body”:”中午一起吃饭”

}

 
 
 
五、Socket 通讯流程图
 
六、使用Socket开发网络通讯
在Web服务(WebServices=>XML)大行其道的今天,调用Web服务的代价是高昂的,尤其是仅仅是抓取少量数据的时候尤其如此。而使用Socket,可以只传送数据本身而不用进行XML封装,大大降低数据传输的开销(JSON)
 
Socket允许使用长连接,允许应用程序运行在异步模式(提高效率),只有在需要的时候才接收数据
 
七、iOS中常用的两种Socket类型
流式Socket(SOCK_STREAM):流式是一种面向连接的Socket,针对于面向连接的TCP服务应用
数据报式Socket(SOCK_DGRAM):数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用
 
八、在iOS中流式Socket连接的方法
在iOS中以NSStream(流)来发送和接收数据
可以设置流的代理,对流状态的变化做出相应
连接建立
接收到数据
连接关闭
 
1.NSStream:数据流的父类,用于定义抽象特性,例如:打开、关闭代理,NSStream继承自CFStream(Core Foundation)
2.NSInputStream:NSStream的子类,用于读取输入
3.NSOutputStream:NSSTream的子类,用于写输出
 
九、开发步骤
1.网络连接设置
   1>设置网络连接,绑定到主机和端口
   2>设置输入流和输出流的代理,监听数据流的状态
   3>将输入输出流添加至运行循环
   4>打开输入流和输出流
2.发送消息给服务器
3.有可读取字节时,读取服务器返回的内容
4.到达流末尾时,关闭流,同时并从主运行循环中删除
 
十、工作流程

服务器的工作流程:首先调用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在在线服务 请输入下面的数字选择服务\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 #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(@"\nsever:%@----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

 

 
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值