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

前言

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

步骤 :

  • 1, 导入第三方框架GCDAsyncSocket,注意,我们在github上下载该框架时,我们使用的是source里面的TCP类型的框架,在source中有两种类型的头文件,一种是TCP类型,另一种是UDP类型.
  • 2, 新建一个对象方法,用于监听10086的服务器是否开启
#import 

@interface WGServicerListener : NSObject

/** 定义一个对象方法,用于开启10086服务器 */
- (void)start;

@end

  • 3, 来到main.m文件中,监听服务器是否需要开启
#import 
#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 ()

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

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



    利用Socket模拟10086服务器读写数据_第1张图片
    {}.png
  • Scoket的通信流程图


    利用Socket模拟10086服务器读写数据_第2张图片
    Socket通信流程图.png

你可能感兴趣的:(利用Socket模拟10086服务器读写数据)