Coin163

首页 > Socket通信

Socket通信

2020腾讯云双十一活动,全年最低!!!(领取3500元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1073

2020阿里云最低价产品入口,含代金券(新老用户有优惠),
入口地址https://www.aliyun.com/minisite/goods

Socket:

  • Socket的英文原义是“孔”或“插座”,Socket通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务

  • 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket

  • Socket通信中,消息头/消息体”的分割方式是很常用的,消息头告诉对方这个消息是干什么的,消息体告诉对方怎么干。每一个HTTP包都分为HTTP头和HTTP体两部分,消息体是可选的,而消息头是必须的。
  • 应用程序通常通过”套接字”向网络发出请求或者应答网络请求
    这里写图片描述

概念:

  • 网络上的请求就是通过Socket来建立连接然后互相通信
  • IP地址(网络上主机设备的唯一标识)
  • 端口号(定位程序)
    • 用于标示进程的逻辑地址,不同进程的标示
    • 有效端口:0~65535,其中0~1024由系统使用或者保留端口,开发中建议使用1024以上的端口
    • 传输协议(用什么样的方式进行交互)
  • 通讯的规则
    • 常见协议:TCP、UDP

协议:

TCP–传输控制协议

  • 建立连接,形成传输数据的通道
  • 在连接中进行大数据传输(数据不受限制)
  • 通过三次握手完成连接,是可靠协议,安全送达
    • 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
    • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手,客户端与服务器开始传送数据
  • 必须建立连接,效率会稍低

UDP(用户数据报协议)

  • 将数据及源和目的封装成数据包中,不需要建立连接
  • 每个数据报的大小限制在64K之内
  • 因为无需连接,因此是不可靠协议
  • 不需要建立连接,速度快

Socket通信流程图

这里写图片描述

  • 客户端

    • socket():建立流式套接字
    • connect():将套接字与服务器连接
    • write():发送数据
    • read():接收数据
    • 关闭套接字,结束对话
  • 服务器端

    • socket():建立流式套接字
    • bind():关联本地地址到套接字
    • listen():设置backlog值,进入监听状态
    • read():读取数据,发送给客户端
    • write():发送数据,回应客户端
    • read():读取数据,读取到客户端已经断开
    • close():关闭套接字,服务结束

Socket服务监听

  • 实现socket的监听方法
    • 使用C语言实现,
    • 使用CocoaAsyncSocket第三方框,内部是对C的封装
  • Telnet命令 telnet host port/telnet 192.168.10.20 3666
    • telnet命令是连接服务器上的某个端口对应的服务

示例代码

下载CocoaAsyncSocket框架,新建一个命令行的项目,到CocoaAsyncSocket文件夹中找到GCDAsyncSocket这个类拖放到项目中

这里写图片描述

在这里我们把命令行的项目作为服务端,然后用MAC的Terminal作为客户端去访问.

  • 新建一个ServiceListener对象
ServiceListener.h
#import <Foundation/Foundation.h>

@interface ServiceListener : NSObject

/** 开启服务*/
-(void)start;

@end
ServiceListener.m

#import "ServiceListener.h"
#import "GCDAsyncSocket.h"

@interface ServiceListener()<GCDAsyncSocketDelegate>

@property (nonatomic, strong) GCDAsyncSocket *serverSocket;

@property (nonatomic, strong) NSMutableArray *clientSockets;//客户端的所有socket对象
@end

@implementation ServiceListener

-(NSMutableArray *)clientSockets{
    if (!_clientSockets) {
        _clientSockets = [NSMutableArray array];
    }

    return _clientSockets;
}

-(void)start{
    // 开启服务:3666
    // 1.创建一个socket对象
    // serverSocket 服务端的socket只监听 有没有客户端请求连接
    GCDAsyncSocket *serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];

    // 2.绑定端口,并开启监听,代表服务已经开启
    NSError *error = nil;
    [serverSocket acceptOnPort:3666 error:&error];
    if (!error) {
        NSLog(@"服务开启成功");
    }else{
        //失败原因是端口被其它程序占用
        NSLog(@"服务开启失败 %@",error);
    }

    self.serverSocket = serverSocket;
}

#pragma mark 有客户端的socket连接到服务器
-(void)socket:(GCDAsyncSocket *)serverSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket{
    NSLog(@"serverSocket %@",serverSocket);
    NSLog(@"clientSocket %@",clientSocket);
    //1.保存客户端的socket
    [self.clientSockets addObject:clientSocket];


    // 提供服务
    NSMutableString *serviceStr = [NSMutableString string];
    [serviceStr appendString:@"欢迎来到在线服务,请输入下面的数字选择服务\n"];
    [serviceStr appendString:@"[0]在线充值\n"];
    [serviceStr appendString:@"[1]在线投诉\n"];
    [serviceStr appendString:@"[2]优惠信息\n"];
    [serviceStr appendString:@"[3]特殊服务\n"];
    [serviceStr appendString:@"[4]退出\n"];
    [clientSocket writeData:[serviceStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

    // 2.监听客户端有没有数据上传
    //timeout -1 代表不超时
    //tag 标识作用,现在不用,就写0
    [clientSocket readDataWithTimeout:-1 tag:0];
}

#pragma mark 读取客户端请求的数据
-(void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag{

    // 1.把NSData转NSString
    NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];


    // 2.字符串转数字
    NSInteger code = [str integerValue];
    NSString *responseStr = nil;
    switch (code) {
        case 0:
            responseStr = @"充值服务暂停...\n";
            break;
        case 1:
            responseStr = @"投诉服务暂停...\n";
            break;
        case 2:
            responseStr = @"优惠信息没有...\n";
            break;
        case 3:
            responseStr = @"特殊服务暂停...\n";
            break;
        case 4:
            responseStr = @"成功退出\n";
            break;
        default:
            break;
    }

    NSLog(@"接收到客户端上传的数据:%@",str);

    // 3.处理请求,返回数据给客户端
    [clientSocket writeData:[responseStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

    if (code == 4) {
        // 移除客户端
        [self.clientSockets removeObject:clientSocket];
    }

    //每次读完数据后,都要调用一次监听数据的方法
    [clientSocket readDataWithTimeout:-1 tag:0];
}
  • main函数
main.m

#import <Foundation/Foundation.h>
#import "ServiceListener.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        //创建服务监听对象
        ServiceListener *listener = [[ServiceListener alloc] init];

        //开启监听
        [listener start];

        //开启主运行循环,让服务不能停
        [[NSRunLoop mainRunLoop] run];
    }
    return 0;
}

随后我们在Terminal中输入:$ telnet 127.0.0.1 3666,(127.0.0.1是本机地址)便可以和服务器交互,如下图:

这里写图片描述

原文

Socket: Socket的英文原义是“孔”或“插座”,Socket通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般

------分隔线----------------------------