iOS移动端开发IM & Network

iOS-Socket-UDP/TCP:客户端点对点和客户端对服务

2017-06-13  本文已影响1328人  doublej_yjj

前奏:socket两套库,GCDAsyncSocket,GCDAsyncUdpSocket和AsyncSocket,AsyncUdpSocket。
前者支持ARC。什么TCP,UDP基础知识就不在这里啰嗦了。
*** pods导入方式:pod 'CocoaAsyncSocket' ***

iOS-Socket-UDP篇

一进入正题先上(AsyncUdpSocket)非ARC的版本1:客户端点对点

#import "AsyncUdpSocket.h" //导入头文件
AsyncUdpSocketDelegate //加入需要遵循的协议代理
// 收发套接字对象
AsyncUdpSocket * _sendSocket;//套接字发送对象
AsyncUdpSocket * _recvSocket;//套接字接收对象
收发套接字对象实例化
- (void)initSocket
{
    _sendSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
    // 绑定一个端口 发数据
    [_sendSocket bindToPort:0x1234 error:nil];
    _recvSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
    [_recvSocket bindToPort:0x4321 error:nil];
    // 0x1234端口做数据发送  0x4321端口接受数据
    // 开始监听
    [_recvSocket receiveWithTimeout:-1 tag:200];
    // 将聊天内容展示在表格上 自己说的显示在左边 别人说的现在在右边 聊天内容越长Cell高度越高
}

指定目标端口向其发送数据
- (IBAction)sendMsg:(UIButton *)sender {
    // 发送消息
    // 发送消息对应的二进制 目标主机 目标主机对应的端口号 超时时间 消息的标签
    // 消息必须发送到接受端绑定的端口之上才能收到数据
    [_sendSocket sendData:[inputField.text dataUsingEncoding:NSUTF8StringEncoding] toHost:destIPField.text port:0x4321 withTimeout:60 tag:100];
}
AsyncUdpSocket相关代理方法
- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
    NSLog(@"消息发送成功回调");
}

// 成功接受到数据回调
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
    // data发送内容的二进制  tag消息标签 host发送消息的主机IP port发送方对应的端口
    NSString * content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"收到目标IP%@端口%x说:%@",host,port,content);
    // _recvSocket对象接受完数据后 必须继续监听 否则不能收到数据
    // -1 表示一直等 直到有数据到来
    [_recvSocket receiveWithTimeout:-1 tag:200];
    
    return YES;
}

点对点基本操作就这么简单,实际通信开发中需要的也许远远不止如此了,这里纯抽出的是点对点点对点关键代码没有贴UI相关。操作效果截图如下:

2017-06-13 11_43_37.gif

二.ARC的版本1:客户端点对点

#define recvPort  2346 //接收socket端口
#define sendPort  6432 //发送socket端口
@import CocoaAsyncSocket;//导入头文件 
#***      pods导入方式:pod 'CocoaAsyncSocket'        ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理协议
GCDAsyncUdpSocket *_sendSocket;//发送socket
GCDAsyncUdpSocket *_recvSocket;//接收socket
@property (strong, nonatomic)  UITextField *ipText;//发送目标的IP地址
- (void)viewDidLoad {
    [super viewDidLoad];
[self initSocket];
_submitText=[[UITextField alloc] initWithFrame:CGRectMake(10, 70+64, 300, 30)];
    _submitText.delegate=self;
    _submitText.borderStyle = UITextBorderStyleRoundedRect;
    _submitText.text = @"192.168.1.21";//这里是我测试的发送目标的IP地址
    [self.view addSubview:_submitText];
}

-(void)initSocket
{
//收发socket实例化
    _recvSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_recvSocket bindToPort:recvPort error:nil];
//在这里接收的套接字需要开启一个接收消息的监听如下:
    if (![_recvSocket beginReceiving:nil]) {
        [_recvSocket close];
        NSLog(@"Error starting server");
        return;
    }
    
    _sendSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_sendSocket bindToPort:sendPort error:nil];
    
}
-(void)buttonClicked:(UIButton*)button
{
    if (button.tag==100){
        [self connectUDPTest];
    }else if(button.tag==101){
        [self sendUDPTouched];
    }
}

- (void)connectUDPTest{
}

- (void)sendUDPTouched
{
//向目标IP发送消息
   NSString *sengStr = [NSString stringWithFormat:@"%@-发来消息",_ipText.text];
    [_sendSocket sendData:[sengStr dataUsingEncoding:NSUTF8StringEncoding] toHost:_ipText.text port:recvPort withTimeout:-1 tag:1];
    NSLog(@"localPort = %hu", [_sendSocket localPort]);
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
    NSLog(@"Message didReceiveData :%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}
操作效果截图如下:
屏幕快照 2017-06-13 下午2.40.20.png

二.ARC的版本2:客户端对服务器

1.新建两个工程一个客户端,一个模拟服务器。首先上服务器
#define PORT  8008 //服务器端口设置
@import CocoaAsyncSocket;//导入头文件 
#***      pods导入方式:pod 'CocoaAsyncSocket'        ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理协议
GCDAsyncUdpSocket *serverSocket;//服务器socket
2.服务器socket实例化 在PORT端口监听数据
    serverSocket=[[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [serverSocket bindToPort:PORT error:nil];
    if (![serverSocket beginReceiving:nil]) {
        [serverSocket close];
        NSLog(@"Error starting server");
        return;
    }

3.GCDAsyncUdpSocket相关代理方法
// 网络连接成功后  自动回调
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address
{
   NSLog(@"已连接到用户:ip:%@",[[NSString alloc]initWithData:address encoding:NSUTF8StringEncoding]);
}

-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
    NSString *datastr=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    // 将数据回写给发送数据的用户
    NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    [sock sendData:[[NSString stringWithFormat:@"服务器收到客户端消息返回%@",datastr] dataUsingEncoding:NSUTF8StringEncoding] toAddress:address withTimeout:-1 tag:300];
}

4客户端相关
#define HOST @"192.168.1.108" //服务器地址
#define PORT  8008 //服务器端口
@import CocoaAsyncSocket;//导入头文件 
#***      pods导入方式:pod 'CocoaAsyncSocket'        ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理协议
GCDAsyncUdpSocket *_udpSocket;//客户端socket
@property (strong, nonatomic)  UITextField *ipText;//发送目标的IP地址
GCDAsyncUdpSocket.h
#define HOST @"192.168.1.108"
#define PORT  8008
#import <UIKit/UIKit.h>
#import "GCDAsyncUdpSocket.h"
@interface UDPServerCommunicationViewController : UIViewController<GCDAsyncUdpSocketDelegate>
{
    GCDAsyncUdpSocket *_udpSocket;
}

@property (strong, nonatomic)  UITextField *ipText;
@end
GCDAsyncUdpSocket.m
#import "UDPServerCommunicationViewController.h"

@interface UDPServerCommunicationViewController ()
@end

@implementation UDPServerCommunicationViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    if (!_udpSocket)
    {
        _udpSocket=nil;
    }
    _udpSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    NSArray *nameArray=[NSArray arrayWithObjects:@"连接",@"发送",nil];
    for (int i=0;i<[nameArray count];i++) {
        UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
        [button setTitle:[nameArray objectAtIndex:i] forState:UIControlStateNormal];
        [button setFrame:CGRectMake(10+50*i, 30+64, 50, 30)];
        button.tag=100+i;
        [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:button];
    }
    
    _ipText=[[UITextField alloc] initWithFrame:CGRectMake(10, 70+64, 300, 30)];
    _ipText.borderStyle = UITextBorderStyleRoundedRect;
    _ipText.text = HOST;
    [self.view addSubview:_ipText];
}

-(void)buttonClicked:(UIButton*)button
{
    if (button.tag==100){
        [self connectUDPTest];
    }else if(button.tag==101){
        [self sendUDPTouched];
    }
}

-(void)connectUDPTest
{
    NSError *error = nil;
    if(![_udpSocket connectedHost]){
        if (![_udpSocket connectToHost:HOST onPort:PORT error:&error]) {
            if (error==nil){
                NSLog(@"连接成功");
            }else{
                NSLog(@"连接失败:%@",error);
            }
        }else{
            NSLog(@"已经连接");
        }
    }else{
        NSLog(@"已经连接:%@",[_udpSocket connectedHost]);
    }
    
}
//客户端发送本机IP地址给服务器
- (void)sendUDPTouched{
    
    [_udpSocket sendData:[_ipText.text dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:1];
//    [_udpSocket sendData:[_ipText.text dataUsingEncoding:NSUTF8StringEncoding] toHost:HOST port:PORT withTimeout:-1 tag:1];
    NSLog(@"Udp Echo server started on port %hu", [_udpSocket localPort]);
}

-(void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address
{
    NSError *error = nil;
    NSLog(@"Message didConnectToAddress: %@",[[NSString alloc]initWithData:address encoding:NSUTF8StringEncoding]);
    [_udpSocket beginReceiving:&error];
}

-(void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error
{
    NSLog(@"Message didNotConnect: %@",error);
}

-(void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error
{
    NSLog(@"Message didNotSendDataWithTag: %@",error);
}

-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
    NSLog(@"Message didReceiveData :%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}

-(void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
     NSLog(@"Message 发送成功");
}
@end
操作效果截图如下:
屏幕快照 2017-06-13 下午5.21.50.png

iOS-Socket-TCP篇

TCP篇这里把服务器模拟和客户端点对点放在一起
图解方式为:

未标题-1.jpg

一.(GCDAsyncSocket)ARC的版本

1.服务器
#import <UIKit/UIKit.h>
@import CocoaAsyncSocket;

@interface ServerViewController : UIViewController<GCDAsyncSocketDelegate,UITableViewDataSource,UITableViewDelegate>
{
    GCDAsyncSocket *serverSocket;
    
    UITableView *clientTableView;
    NSMutableArray *socketArray;
}
@end

#import "ServerViewController.h"

@interface ServerViewController ()

@end

@implementation ServerViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    clientTableView=[[UITableView alloc] initWithFrame:CGRectMake(0,100+64,Screen_Width,Screen_Height-164) style:UITableViewStylePlain];
    clientTableView.delegate=self;
    clientTableView.dataSource=self;
    [self.view addSubview:clientTableView];
// 存储连接到服务器的socket的数组
    socketArray =[[NSMutableArray alloc] initWithCapacity:0];
    // 服务器socket实例化  在0x1234端口监听数据
    serverSocket=[[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [serverSocket acceptOnPort:0x1234 error:nil];
}

// 有新的socket向服务器链接自动回调
-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
    [socketArray addObject:newSocket];
    [clientTableView reloadData];
    // 如果下面的方法不写 只能接收一次socket链接
    [newSocket readDataWithTimeout:-1 tag:100];
}

// 网络连接成功后  自动回调
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"已连接到用户:ip:%@",host);
}

// 接收到了数据 自动回调  sock客户端
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"收到%@发来的消息:%@",[sock connectedHost],message);
    
    // 相当与向服务起索取  在线用户数据
    // 将连上服务器的所有ip地址 组成一个字符串 将字符串回写到客户端
    if ([message isEqualToString:@"GetClientList"]) {
        NSMutableString *clientList=[[NSMutableString alloc] initWithCapacity:0];
        int i=0;
        // 每一个客户端连接服务器成功后 socketArray保存客户端的套接字
        // [newSocket connectedHost] 获取套接字对应的IP地址
        for (GCDAsyncSocket *newSocket in socketArray) {
            // 以字符串形式分割ip地址  192..,192...,
            if (i!=0) {
                [clientList appendFormat:@",%@",[newSocket connectedHost]];
            }
            else{
                [clientList appendFormat:@"%@",[newSocket connectedHost]];
            }
            i++;
        }
        // 将服务端所有的ip连接成一个字符串对象
        NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
        // 将在线的所有用户  以字符串的形式一次性发给客户端
        // 哪个客户端发起数据请求sock就表示谁
        [sock writeData:newData withTimeout:-1 tag:300];
    }
    else{
        // 将数据回写给发送数据的用户
        NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        
        [sock writeData:data withTimeout:-1 tag:300];
    }
    // 继续读取socket数据
    [sock readDataWithTimeout:-1 tag:200];
}

/*重连
 
 实现代理方法
 
 -(void)onSocketDidDisconnect:(GCDAsyncSocket *)sock
 {
 NSLog(@"sorry the connect is failure %ld",sock.userData);
 if (sock.userData == SocketOfflineByServer) {
 // 服务器掉线,重连
 [self socketConnectHost];
 }
 else if (sock.userData == SocketOfflineByUser) {
 // 如果由用户断开,不进行重连
 return;
 }
 
 }*/
// 连接断开时  服务器自动回调
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock
{
    [socketArray removeObject:sock];
    [clientTableView reloadData];
}

// 向用户发出的消息  自动回调
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"向用户%@发出消息",[sock connectedHost]);
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [socketArray count];
}

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellName=@"CellName";
    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellName];
    if (cell==nil) {
        cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellName];
    }
    GCDAsyncSocket *socket=[socketArray objectAtIndex:indexPath.row];
    // 根据连接服务端套接字socket对象 获取对应的客户端IP地址
    cell.textLabel.text=[NSString stringWithFormat:@"用户:%@",[socket connectedHost]];
    return cell;
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

2.客户端

#import <UIKit/UIKit.h>
@import CocoaAsyncSocket;

@interface ClientViewController : UIViewController<GCDAsyncSocketDelegate,UITableViewDataSource,UITableViewDelegate,UITextFieldDelegate>
{
    GCDAsyncSocket *serverSocket;//服务器socket
    GCDAsyncSocket *listenSocket;//客户端自身socket(接收消息)
    GCDAsyncSocket *clientSocket;//客户端发送socket(发送消息)
    
    UITableView *clientTableView;
    NSMutableArray *clientArray;
    UITextField* messageTextField;
    NSString *ipCopy;
}

#import "ServerViewController.h"
#import "ClientViewController.h"

@interface ServerViewController ()

@end

@implementation ServerViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    clientTableView=[[UITableView alloc] initWithFrame:CGRectMake(0,100+64,Screen_Width,Screen_Height-164) style:UITableViewStylePlain];
    clientTableView.delegate=self;
    clientTableView.dataSource=self;
    [self.view addSubview:clientTableView];
    
    
    socketArray =[[NSMutableArray alloc] initWithCapacity:0];
    // 服务器socket实例化  在0x1234端口监听数据
    serverSocket=[[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [serverSocket acceptOnPort:PORT error:nil];

}

// 有新的socket向服务器链接自动回调
-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
    [socketArray addObject:newSocket];
    
    [clientTableView reloadData];
    
    // 如果下面的方法不写 只能接收一次socket链接
    [newSocket readDataWithTimeout:-1 tag:100];
}

// 网络连接成功后  自动回调
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"已连接到用户:ip:%@",host);
}

// 接收到了数据 自动回调  sock客户端
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    NSLog(@"收到%@发来的消息:%@",[sock connectedHost],message);
    
    // 相当与向服务起索取  在线用户数据
    // 将连上服务器的所有ip地址 组成一个字符串 将字符串回写到客户端
    if ([message isEqualToString:@"GetClientList"]) {
        NSMutableString *clientList=[[NSMutableString alloc] initWithCapacity:0];
        int i=0;
        // 每一个客户端连接服务器成功后 socketArray保存客户端的套接字
        // [newSocket connectedHost] 获取套接字对应的IP地址
        for (GCDAsyncSocket *newSocket in socketArray) {
            // 以字符串形式分割ip地址  192..,192...,
            if (i!=0) {
                [clientList appendFormat:@",%@",[newSocket connectedHost]];
            }
            else{
                [clientList appendFormat:@"%@",[newSocket connectedHost]];
            }
            i++;
        }
        // 将服务端所有的ip连接成一个字符串对象
        NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
        // 将在线的所有用户  以字符串的形式一次性发给客户端
        // 哪个客户端发起数据请求sock就表示谁
        [sock writeData:newData withTimeout:-1 tag:300];
    }
    else{
        // 将数据回写给发送数据的用户
        NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        
        [sock writeData:data withTimeout:-1 tag:300];
    }
    
    // 继续读取socket数据
    [sock readDataWithTimeout:-1 tag:200];
    
}

/*重连
 
 实现代理方法
 
 -(void)onSocketDidDisconnect:(GCDAsyncSocket *)sock
 {
 NSLog(@"sorry the connect is failure %ld",sock.userData);
 if (sock.userData == SocketOfflineByServer) {
 // 服务器掉线,重连
 [self socketConnectHost];
 }
 else if (sock.userData == SocketOfflineByUser) {
 // 如果由用户断开,不进行重连
 return;
 }
 
 }*/
// 连接断开时  服务器自动回调
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock
{
    [socketArray removeObject:sock];
    
    [clientTableView reloadData];
    
}

// 向用户发出的消息  自动回调
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"向用户%@发出消息",[sock connectedHost]);
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [socketArray count];
}

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellName=@"CellName";
    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellName];
    if (cell==nil) {
        cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellName];
    }
    GCDAsyncSocket *socket=[socketArray objectAtIndex:indexPath.row];
    // 根据连接服务端套接字socket对象 获取对应的客户端IP地址
    cell.textLabel.text=[NSString stringWithFormat:@"用户:%@",[socket connectedHost]];
    
    return cell;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

操作效果截图如下:
屏幕快照 2017-06-13 下午9.19.32.png

二.(AsyncSocket)非ARC的版本

(AsyncSocket)非ARC的版本1.服务器
#import <UIKit/UIKit.h>
#import "AsyncSocket.h"


@interface ServerViewController : UIViewController<AsyncSocketDelegate,UITableViewDataSource,UITableViewDelegate>
{
    AsyncSocket *serverSocket;

    UITableView *clientTableView;
    NSMutableArray *clientArray;
    NSMutableArray *socketArray;
}
@end

#import "ServerViewController.h"

@interface ServerViewController ()

@end

@implementation ServerViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    clientTableView=[[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
    clientTableView.delegate=self;
    clientTableView.dataSource=self;
    [self.view addSubview:clientTableView];
    
    clientArray=[[NSMutableArray alloc] initWithCapacity:0];
    
    socketArray =[[NSMutableArray alloc] initWithCapacity:0];
    
    // 服务器socket实例化  在0x1234端口监听数据
    serverSocket=[[AsyncSocket alloc] init];
    serverSocket.delegate=self;
    [serverSocket acceptOnPort:0x1234 error:nil];
}

// 有新的socket向服务器链接自动回调
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
    [socketArray addObject:newSocket];
    [clientTableView reloadData];
    // 如果下面的方法不写 只能接收一次socket链接
    [newSocket readDataWithTimeout:-1 tag:100];
}

// 网络连接成功后  自动回调
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"已连接到用户:ip:%@",host);
}

// 接收到了数据 自动回调  sock客户端
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    NSLog(@"收到%@发来的消息:%@",[sock connectedHost],message);
    
    // 相当与向服务起索取  在线用户数据
    // 将连上服务器的所有ip地址 组成一个字符串 将字符串回写到客户端
    if ([message isEqualToString:@"GetClientList"]) {
        NSMutableString *clientList=[[NSMutableString alloc] initWithCapacity:0];
        int i=0;
        for (AsyncSocket *newSocket in socketArray) {
            // 以字符串形式分割ip地址  192..,192...,
            
            if (i!=0) {
                [clientList appendFormat:@",%@",[newSocket connectedHost]];
            }
            else{
                [clientList appendFormat:@"%@",[newSocket connectedHost]];
            }
            i++;
        }
        // 将服务端所有的ip连接成一个字符串对象
        NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
        // 将在线的所有用户  以字符串的形式一次性发给客户端
        // 哪个客户端发起数据请求sock就表示谁
        [sock writeData:newData withTimeout:-1 tag:300];
    }
    else{
        // 将数据回写给发送数据的用户
        NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        
        [sock writeData:data withTimeout:-1 tag:300];
    }
    
    // 继续读取socket数据
    [sock readDataWithTimeout:-1 tag:200];
    
}

// 连接断开时  服务器自动回调
-(void)onSocketDidDisconnect:(AsyncSocket *)sock
{
    [socketArray removeObject:sock];
    
    [clientTableView reloadData];
    
}

// 向用户发出的消息  自动回调
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"向用户%@发出消息",[sock connectedHost]);
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [socketArray count];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellName=@"CellName";
    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellName];
    if (cell==nil) {
        cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellName];
    }
    AsyncSocket *socket=[socketArray objectAtIndex:indexPath.row];
    // 根据连接服务端套接字socket对象 获取对应的客户端IP地址
    cell.textLabel.text=[NSString stringWithFormat:@"用户:%@",[socket connectedHost]];
    
    return cell;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

1.(AsyncSocket)非ARC的版本客户端
#import <UIKit/UIKit.h>
#import "AsyncSocket.h"
@interface ClientViewController : UIViewController<AsyncSocketDelegate,UITableViewDataSource,UITableViewDelegate,UITextFieldDelegate>
{
    AsyncSocket *serverSocket;
    AsyncSocket *listenSocket;
    AsyncSocket *clientSocket;
    
    UITableView *clientTableView;
    NSMutableArray *clientArray;
    UITextField* messageTextField;
}
@end

#import "ClientViewController.h"

@interface ClientViewController ()

@end

@implementation ClientViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    // A(监听收对方发来的数据)  server(连接服务器)  B(对方)
    
    // 服务器socket
    serverSocket =[[AsyncSocket alloc] init];
    serverSocket.delegate=self;
    // 本身
    listenSocket=[[AsyncSocket alloc] initWithDelegate:self];
    [listenSocket acceptOnPort:0x1235 error:nil];
    // 点对点通讯时 对方的socket
    clientSocket=[[AsyncSocket alloc] initWithDelegate:self];
    
    NSArray *nameArray=[NSArray arrayWithObjects:@"连接",@"断开",@"在线用户", nil];
    for (int i=0;i<[nameArray count];i++) {
        UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
        [button setTitle:[nameArray objectAtIndex:i] forState:UIControlStateNormal];
        [button setFrame:CGRectMake(10+50*i, 30, 50, 30)];
        button.tag=100+i;
        [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:button];
    }
    
    clientTableView=[[UITableView alloc] initWithFrame:CGRectMake(0, 80, 320,380) style:UITableViewStylePlain];
    clientTableView.delegate=self;
    clientTableView.dataSource=self;
    [self.view addSubview:clientTableView];
    
    clientArray=[[NSMutableArray alloc] initWithCapacity:0];
    
    messageTextField=[[UITextField alloc] initWithFrame:CGRectMake(10, 50, 300, 30)];
    messageTextField.delegate=self;
    messageTextField.borderStyle = UITextBorderStyleRoundedRect;
    [self.view addSubview:messageTextField];
    
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}
// 发数据  结束编辑  就是键盘隐藏的时候  自动调用
-(void)textFieldDidEndEditing:(UITextField *)textField
{

    if (clientSocket && [clientSocket isConnected]) {
        
        NSData *data=[textField.text dataUsingEncoding:NSUTF8StringEncoding];

        // 向socket写数据
        [clientSocket writeData:data withTimeout:-1 tag:100];
        //[serverSocket writeData:data withTimeout:-1 tag:200];
    }
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [clientArray count];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellName=@"CellName";
    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellName];
    if (cell==nil) {
        cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellName];
    }
    cell.textLabel.text=[clientArray objectAtIndex:indexPath.row];
    
    return cell;
}
-(void)buttonClicked:(UIButton*)button
{
    if (button.tag==100)
    {
        // 连接服务器
        if (![serverSocket isConnected])
        {
            //[serverSocket disconnect];
            // 指定 ip 指定端口 发起一个TCP连接
            [serverSocket connectToHost:@"192.168.1.108" onPort:0x1234 error:nil];
            // 向服务器写数据
//            [serverSocket writeData:<#(NSData *)#> withTimeout:<#(NSTimeInterval)#> tag:<#(long)#>]
        }
          else
        {
            NSLog(@"已经和服务器连接");
        }
    }
    // 断开与服务器连接
         else if(button.tag==101)
    {
        // 断开TCP连接
        [serverSocket disconnect];
    }
    // 获取在线用户
            else if(button.tag==102)
    {
        NSString *message=@"GetClientList";
        NSData *data=[message dataUsingEncoding:NSUTF8StringEncoding];
        // 向服务器获取在线用户信息
        // 向服务端写字符串 GetClientList
        [serverSocket writeData:data withTimeout:-1 tag:400];
    }
}
// 接收到了一个新的socket连接 自动回调
// 接收到了新的连接  那么释放老的连接
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
    
    if (sock==listenSocket) {
        NSLog(@"收到用户%@的连接请求",[newSocket connectedHost]);
        if (clientSocket && [clientSocket isConnected]) {
            [clientSocket disconnect];
            
            [clientSocket release];
        }
        // 保存发起连接的客户端socket
        clientSocket=[newSocket retain];
    }
}

// 写数据成功 自动回调
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    // 获取用户列表
    if (sock==serverSocket) {
        NSLog(@"向服务器%@发送消息成功",[sock connectedHost]);
    }
    // 客户端与客户端通讯
    else if(sock==clientSocket){
        NSLog(@"向客户%@发送消息成功",[sock connectedHost]);
    }
    // 继续监听
    [sock readDataWithTimeout:-1 tag:500];
    
}

// 成功连接后自动回调
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    [sock readDataWithTimeout:-1 tag:200];
    NSLog(@"已经连接到服务器:%@",host);
}

// 客户端接收到了数据
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    // 如果时服务器给的消息  必然是在线用户的消息
    if (sock==serverSocket) {
        NSString *message=[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
        // 将字符串以","分割 到数组中
        // 分割出IP地址
        NSArray *array=[message componentsSeparatedByString:@","];
        [clientArray removeAllObjects];
        [clientArray addObjectsFromArray:array];
        [clientTableView reloadData];
        
        NSLog(@"在线用户列表:%@",clientArray);
    }
    // 点对点通讯
    else{
        NSString *message=[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
        NSLog(@"收到客户%@的消息:%@",[sock connectedHost],message);
    }

    // 继续监听
    [sock readDataWithTimeout:-1 tag:100];
}

// 点击一行时   向该行对应的ip地址发起连接
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *ip=[clientArray objectAtIndex:indexPath.row];
    if (clientSocket ) {
        // 如果之前连接上了一个好友 那么先断开
        if ([clientSocket isConnected]) {
            [clientSocket disconnect];
        }
        // 向TableViewCell上指定ip发起TCP连接
        [clientSocket connectToHost:ip onPort:0x1235 error:nil];
        // clientSocket已经指向了好友 如果需要和好友发消息
        NSData *data=[@"didSelectRowAtIndexPath" dataUsingEncoding:NSUTF8StringEncoding];
        [clientSocket writeData:data withTimeout:-1 tag:100];
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

操作效果截图如下:和ARC一样

1.GitHub地址:GCD ARC版本 UDP&TCP demo

https://github.com/Yjunjie/Socket-GCD-TCP-UDPdemo

2.GitHub地址:非ARC版本 UDP&TCP demo

https://github.com/Yjunjie/Socket-TCP-UDPdemo

上一篇下一篇

猜你喜欢

热点阅读