群聊思路:客户端ABC都连接到服务器,当客户端A发送信息给服务器,服务端把A的信息发送给除A以外的B、C客户端
在这的基础上IOS-基于CocoaAsyncSocket的服务端的监听(二),
服务器方面:唯一变化的就是在服务端读取客户端请求数据的时候要做判断
#pragma mark 服务端读取客户端请求数据
- (void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag{
NSLog(@"clientSocket读取保存在severSocket的clientSocket数据:%@",clientSocket);
//1、NSData转成string
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//2、群聊服务:把当前客户端的数据转发给其他客户端,ABC客户端都连接了服务端,A客户端发送消息给服务端,服务端要遍历存在服务端的客户端数据,把除了A客户端发送的消息转发给BC。这样可以构成一个群聊的服务
NSLog(@"接受到客户端上传的数据responseStr:%@",str);
//3、处理请求,返回数据给客户端
for (GCDAsyncSocket *socket in self.clientSockets) {
if (socket == clientSocket) {
//是自己A 不发送
}else{
//是BC 把存在服务端的A消息发送给BC
[socket writeData:data withTimeout:-1 tag:0];
}
}
#warning 只会读取1次 每次读完数据都要监听
[clientSocket readDataWithTimeout:-1 tag:0];
}
客户端方面
1、实现socket以及发送消息
- (void)viewDidLoad {
[super viewDidLoad];
//实现聊天室雏形
//1.连接到群聊服务器
//1.1.创建客户端socket对象
self.clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
//1.2.发送连接请求
NSError *error;
[self.clientSocket connectToHost:@"192.168.1.75" onPort:5288 error:&error];
if (!error) {
NSLog(@"%@",error);
}
}
2、socket代理判断是否连接成功
#pragma mark socket连接成功
- (void)socket:(GCDAsyncSocket *)clientSocket didConnectToHost:(NSString *)host port:(uint16_t)port{
NSLog(@"与服务器连接成功");
//客户端监听读取数据
[clientSocket readDataWithTimeout:-1 tag:0];
}
#pragma mark socket连接失败
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
NSLog(@"与服务器断开连接%@",err);
self.clientSocket = nil;
self.clientSocket.delegate = nil;
}
3、读取服务端返回的数据
#pragma mark 读取消息,socket客户端监听数据
- (void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag{
NSLog(@"读取消息");
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@读取消息",str);
/*添加消息
if (str) {
[self.dataSource addObject:str];
#warning 要在主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.tableView reloadData];
}];
}*/
#warning 读完数据继续监听。// withTimeout -1 : 无穷大,一直等
[clientSocket readDataWithTimeout:-1 tag:0];
}
4、创建一个Btn手动发送消息
#pragma mark 手动发送
- (IBAction)send:(id)sender {
NSString *str = @"11";
if (str.length ==0 ) {//无数据
return;
}
/* [self.dataSource addObject:str];
// [[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.tableView reloadData];
// }];*/
//发送数据
[self.clientSocket writeData:[str dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
}
当然也可以定时发送,就是所谓的心跳包,每次间隔几秒给服务器发送一条双方协商好的固定格式,如果粘包,那就在固定格式末尾加上某个特定符号,让服务端监听特定符号是否接受成功。因为在某些特殊情况下,包的发送数据过大可能会发n次。
#pragma mark 心跳发送
// 添加定时器
- (void)addTimer
{
// 长连接定时器
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];
// 把定时器添加到当前运行循环,并且调为通用模式
[[NSRunLoop currentRunLoop] addTimer:self.connectTimer forMode:NSRunLoopCommonModes];
}
// 心跳连接
- (void)longConnectToSocket
{
// 发送固定格式的数据,指令@"longConnect",这里要和服务器有固定的格式
float version = [[UIDevice currentDevice] systemVersion].floatValue;
NSString *longConnect = [NSString stringWithFormat:@"ping%f",version];
NSData *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
//发送心跳包
[self.clientSocket writeData:data withTimeout:- 1 tag:0];
}