美文网首页网络
利用Socket模拟10086服务器读写数据

利用Socket模拟10086服务器读写数据

作者: Alexander | 来源:发表于2016-03-09 17:33 被阅读172次

    前言

    • 如果想要更加轻松愉悦的深入学习XMPP,必须要从最简单的开始,学习是一个循序渐进的过程,只有清楚各个组成部分的工作原理,我们才能更好的学习更加深层的知识.今天我们来学习一下XMPP中的Socket.
    • 本章整体结构 : 利用一个第三方框架,快速上手Socket,我们就通过模拟一下10086服务器的读写功能,从而侧面了解什么事Scoket.

    步骤 :

    • 1, 导入第三方框架GCDAsyncSocket,注意,我们在github上下载该框架时,我们使用的是source里面的TCP类型的框架,在source中有两种类型的头文件,一种是TCP类型,另一种是UDP类型.
    • 2, 新建一个对象方法,用于监听10086的服务器是否开启
    #import <Foundation/Foundation.h>
    
    @interface WGServicerListener : NSObject
    
    /** 定义一个对象方法,用于开启10086服务器 */
    - (void)start;
    
    @end
    
    
    • 3, 来到main.m文件中,监听服务器是否需要开启
    #import <Foundation/Foundation.h>
    #import "WGServicerListener.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
            // 创建监听100086服务器的对象,只要连接到IP地址,就开启10086服务器
            WGServicerListener *listener = [[WGServicerListener alloc] init];
    
            // 开启服务器
            [listener start];
    
            // 保证开启服务器以后,一直处于开启状态
            [[NSRunLoop mainRunLoop] run];
    
            // 小常识 : 服务器是不会停的(所以, 需要手动开启一个主运行循环)
        }
        return 0;
    }
    
    
    • 4, 实现开启服务器的方法
    
    #import "WGServicerListener.h"
    #import "GCDAsyncSocket.h"
    
    @interface WGServicerListener ()<GCDAsyncSocketDelegate>
    
    // 服务器Soket
    @property(nonatomic, strong) GCDAsyncSocket *serverSocket;
    
    // 保存客户端所有的Socket对象
    @property(nonatomic, strong) NSMutableArray *clientSockets;
    
    @end
    
    @implementation WGServicerListener
    
    #pragma mark -  懒加载
    - (NSMutableArray *)clientSockets
    {
        if (_clientSockets == nil) {
    
            _clientSockets = [NSMutableArray array];
        }
        return _clientSockets;
    }
    
    - (void)start {
    
        // 1, 创建服务器的socket对象,serverSocket只会监听有没有客户端请求连接
        GCDAsyncSocket *serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    
        // 2, 绑定和监听客户端的连接
        NSError *error = nil;
        [serverSocket acceptOnPort:1688 error:&error];
    
        // 判断error是否有值,如果有值表示开启失败
        if (!error) {
    
            // 开启成功
            NSLog(@"10086服务器已开启");
        } else
        {
        // 开启失败
            NSLog(@"10086服务器开启失败 : %@",error);
        }
    
        // 保存服务器的Socket对象
        self.serverSocket = serverSocket;
    }
    
    • 注意 :

    • 1,导入第三方框架时,最好编译一下,判断有没有存在依赖的框架.

    • 2, 定义了两个属性,一个是服务器的Socket对象,另一个是保存所有连接上来的客户端Socket对象(它需要懒加载,因为连接上来的客户端不止一个,所以需要懒加载,用到时才加载).

    • 3, 端口号1688是随便写的,只要是大于1024小于65535即可,但是, 有可能连接有问题,或者连接失败,原因是可能端口号已被占用.

    • 5, 开启服务器之后,需要监听服务器是否有和客户端连接

    // 需要监听服务器是否有连接到新的客户端,所以, 监听方法是属于协议中的方法,要遵守协议
    
    /**
     * 只要有新的端口和服务器连接就会调用该方法.
     * 前面个sock是服务器的端口, 后面个newSocket是客户端的端口,可以自定义参数名
     */
    - (void)socket:(GCDAsyncSocket *)serverSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket
    {
    
        // 将连接进来的客户端的Socket对象保存到数组中
        [self.clientSockets addObject:clientSocket];
    
        // 只要开启服务就提示用户有什么类型的服务
        NSMutableString *promptStr = [NSMutableString string];
        [promptStr appendString:@"欢迎来到10086在线服务,请输入下面的数字选择服务\n"];
        [promptStr appendString:@"[0]在线充值\n"];
        [promptStr appendString:@"[1]在线投诉\n"];
        [promptStr appendString:@"[2]优惠信息\n"];
        [promptStr appendString:@"[3]special services\n"];
        [promptStr appendString:@"[4]退出\n"];
    
        // 客户端写入数据
        [clientSocket writeData:[promptStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
    
        // 监听客户端有没有上传数据
        [clientSocket readDataWithTimeout:-1 tag:0];
    }
    
    • 注意 :

    • 1, 只要是有新的客户端与服务器连接就一定会来到这个方法.

    • 2, 值得注意的是,客户端clientSocket对象并不是和服务器的serverSocket对应的,换句话说,服务器读取客户端上传的数据时并不是serverSocket,而是服务器中的clientSocket对象来读取的(这样说有点抽象,具体看下面的图).

    • 6, 的那个客户端上传数据之后,服务器需要做相应的响应

    /**
     * 监听客户端有没有输入数据,只要客户端上传了数据,那么就一定会调用下面这个方法
     *
     */
    - (void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag
    {
    
        NSLog(@"客户端输入数据了");  // 测试用
    
        // 此时,服务器接收到的是一个NSData,我们需要将NSData转为一个字符串
        NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
        // 将字符串转为数字,使用switch输出对应的字符串
        NSInteger number = [str integerValue];
        NSString *responseStr = nil;
        switch (number) {
            case 0:
                responseStr = @"充值服务进行中...\n";
                break;
            case 1:
                responseStr = @"10086的投诉服务几乎零处理...\n";
                break;
            case 2:
                responseStr = @"几乎所有优惠信息都是骗人的...\n";
                break;
            case 3:
                responseStr = @"特殊服务倒是做的勤快...\n";
                break;
            case 4:
                responseStr = @"您已经退出了,并且扣了50块钱\n";
                break;
            default:
    
                break;
        }
    
        // 服务器读取客户端上传的数据
        [clientSocket writeData:[responseStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
    
        // 当number = 4 说明用户需要退出,但是我们需要将客户端和服务器之间的连接断开
        if (number == 4) {
    
            // 将数组中的客户端全部移除掉
            [self.clientSockets removeObject: clientSocket];
        }
    
        // 只要客户端上传1次数据,服务器都需要监听它
        [clientSocket readDataWithTimeout:-1 tag:0];
    }
    
    • 注意 :

    • 1, 这个方法的调用时刻 : 只要客户端上传数据,就会调用该方法

    • 2, 传进来的一个NSData类型,需要转出字符串,需要理解是怎么转变的

    • 3, 字符串是如何转变为整型的

    • 4, 如何将服务器和客户端断开连接

    • 5, 注意最后一行代码,它表示的意思是只要客户端上传数据,它都去读取,如果不写最后一句,不管客户端输入什么,服务器都不会做出任何反应.

    • 终端图片表示运行结果.


      {}.png
    • Scoket的通信流程图


      Socket通信流程图.png

    相关文章

      网友评论

      • 042a0e1be73f:你好,你的main.m为什么和我的不一样?main.m不是系统生成的吗?

      本文标题:利用Socket模拟10086服务器读写数据

      本文链接:https://www.haomeiwen.com/subject/vdftlttx.html