美文网首页iOS移动端开发IM & Network
iOS-Socket-UDP/TCP:客户端点对点和客户端对服务

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

作者: doublej_yjj | 来源:发表于2017-06-13 11:45 被阅读1328次

    前奏: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

    相关文章

      网友评论

      • liuyun333:楼主你好,这个可以穿透吗?
      • 动感超人丶:前奏:socket两套库,GCDAsyncSocket,GCDAsyncUdpSocket和AsyncSocket,AsyncUdpSocket。 ???????
      • 达若漠沙:楼主,你好~想请问一下,如果通过soket发送图片,那么该如何取接收数据呢? 如何把图片数据写成一个文件呢?
        达若漠沙:@doublej_yjj :直接加载确实可以,但是加载出来的图片只是一小部分,一直想使用outStream拼接起来,可是只能写进去一部分,图片只有1/3 大小 ,被困在如何知道单张图片大小 和 数据接收完毕时机的这两个点上了。
        doublej_yjj:数据接收的回调方法里,接收的就是二进制,直接二进制转图片。
      • 09e277f373c7:不错很详细

      本文标题:iOS-Socket-UDP/TCP:客户端点对点和客户端对服务

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