前言
- 如果想要更加轻松愉悦的深入学习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
网友评论