• IOS-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地址
    端口号
    传输协议
     
    TCPUDP:数据传输方式
    HTTPXMPP:数据传输格式
     
    二、网络通讯要素
    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在在线服务 请输入下面的数字选择服务
    "];
     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
     
     
     
     
  • 相关阅读:
    VPython—旋转坐标系
    分布式锁简单入门以及三种实现方式介绍
    win10 64bit安装redis及redis desktop manager的方法
    Kafka史上最详细原理总结
    idea常用快捷键
    十大Intellij IDEA快捷键
    Spark(一): 基本架构及原理
    Idea Live Templates代码模板
    IntelliJ IDEA 常用快捷键列表及技巧大全
    Win10 下 RabbitMQ 的 安装 配置
  • 原文地址:https://www.cnblogs.com/oc-bowen/p/5266277.html
Copyright © 2020-2023  润新知