iOS-Socket-UDP/TCP:客戶端點(diǎn)對點(diǎn)和客戶端對服務(wù)器數(shù)據(jù)收發(fā)

前奏:socket兩套庫坞靶,GCDAsyncSocket辽剧,GCDAsyncUdpSocket和AsyncSocket排截,AsyncUdpSocket娱节。
前者支持ARC挠蛉。什么TCP,UDP基礎(chǔ)知識就不在這里啰嗦了。
*** pods導(dǎo)入方式:pod 'CocoaAsyncSocket' ***

iOS-Socket-UDP篇

一進(jìn)入正題先上(AsyncUdpSocket)非ARC的版本1:客戶端點(diǎn)對點(diǎn)

#import "AsyncUdpSocket.h" //導(dǎo)入頭文件
AsyncUdpSocketDelegate //加入需要遵循的協(xié)議代理
// 收發(fā)套接字對象
AsyncUdpSocket * _sendSocket;//套接字發(fā)送對象
AsyncUdpSocket * _recvSocket;//套接字接收對象
收發(fā)套接字對象實(shí)例化
- (void)initSocket
{
    _sendSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
    // 綁定一個端口 發(fā)數(shù)據(jù)
    [_sendSocket bindToPort:0x1234 error:nil];
    _recvSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
    [_recvSocket bindToPort:0x4321 error:nil];
    // 0x1234端口做數(shù)據(jù)發(fā)送  0x4321端口接受數(shù)據(jù)
    // 開始監(jiān)聽
    [_recvSocket receiveWithTimeout:-1 tag:200];
    // 將聊天內(nèi)容展示在表格上 自己說的顯示在左邊 別人說的現(xiàn)在在右邊 聊天內(nèi)容越長Cell高度越高
}

指定目標(biāo)端口向其發(fā)送數(shù)據(jù)
- (IBAction)sendMsg:(UIButton *)sender {
    // 發(fā)送消息
    // 發(fā)送消息對應(yīng)的二進(jìn)制 目標(biāo)主機(jī) 目標(biāo)主機(jī)對應(yīng)的端口號 超時時間 消息的標(biāo)簽
    // 消息必須發(fā)送到接受端綁定的端口之上才能收到數(shù)據(jù)
    [_sendSocket sendData:[inputField.text dataUsingEncoding:NSUTF8StringEncoding] toHost:destIPField.text port:0x4321 withTimeout:60 tag:100];
}
AsyncUdpSocket相關(guān)代理方法
- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
    NSLog(@"消息發(fā)送成功回調(diào)");
}

// 成功接受到數(shù)據(jù)回調(diào)
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
    // data發(fā)送內(nèi)容的二進(jìn)制  tag消息標(biāo)簽 host發(fā)送消息的主機(jī)IP port發(fā)送方對應(yīng)的端口
    NSString * content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"收到目標(biāo)IP%@端口%x說:%@",host,port,content);
    // _recvSocket對象接受完數(shù)據(jù)后 必須繼續(xù)監(jiān)聽 否則不能收到數(shù)據(jù)
    // -1 表示一直等 直到有數(shù)據(jù)到來
    [_recvSocket receiveWithTimeout:-1 tag:200];
    
    return YES;
}

點(diǎn)對點(diǎn)基本操作就這么簡單肄满,實(shí)際通信開發(fā)中需要的也許遠(yuǎn)遠(yuǎn)不止如此了谴古,這里純抽出的是點(diǎn)對點(diǎn)點(diǎn)對點(diǎn)關(guān)鍵代碼沒有貼UI相關(guān)。操作效果截圖如下:

2017-06-13 11_43_37.gif

二.ARC的版本1:客戶端點(diǎn)對點(diǎn)

#define recvPort  2346 //接收socket端口
#define sendPort  6432 //發(fā)送socket端口
@import CocoaAsyncSocket;//導(dǎo)入頭文件 
#***      pods導(dǎo)入方式:pod 'CocoaAsyncSocket'        ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理協(xié)議
GCDAsyncUdpSocket *_sendSocket;//發(fā)送socket
GCDAsyncUdpSocket *_recvSocket;//接收socket
@property (strong, nonatomic)  UITextField *ipText;//發(fā)送目標(biāo)的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";//這里是我測試的發(fā)送目標(biāo)的IP地址
    [self.view addSubview:_submitText];
}

-(void)initSocket
{
//收發(fā)socket實(shí)例化
    _recvSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_recvSocket bindToPort:recvPort error:nil];
//在這里接收的套接字需要開啟一個接收消息的監(jiān)聽如下:
    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
{
//向目標(biāo)IP發(fā)送消息
   NSString *sengStr = [NSString stringWithFormat:@"%@-發(fā)來消息",_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:客戶端對服務(wù)器

1.新建兩個工程一個客戶端稠歉,一個模擬服務(wù)器掰担。首先上服務(wù)器
#define PORT  8008 //服務(wù)器端口設(shè)置
@import CocoaAsyncSocket;//導(dǎo)入頭文件 
#***      pods導(dǎo)入方式:pod 'CocoaAsyncSocket'        ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理協(xié)議
GCDAsyncUdpSocket *serverSocket;//服務(wù)器socket
2.服務(wù)器socket實(shí)例化 在PORT端口監(jiān)聽數(shù)據(jù)
    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相關(guān)代理方法
// 網(wǎng)絡(luò)連接成功后  自動回調(diào)
-(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];
    // 將數(shù)據(jù)回寫給發(fā)送數(shù)據(jù)的用戶
    NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    [sock sendData:[[NSString stringWithFormat:@"服務(wù)器收到客戶端消息返回%@",datastr] dataUsingEncoding:NSUTF8StringEncoding] toAddress:address withTimeout:-1 tag:300];
}

4客戶端相關(guān)
#define HOST @"192.168.1.108" //服務(wù)器地址
#define PORT  8008 //服務(wù)器端口
@import CocoaAsyncSocket;//導(dǎo)入頭文件 
#***      pods導(dǎo)入方式:pod 'CocoaAsyncSocket'        ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理協(xié)議
GCDAsyncUdpSocket *_udpSocket;//客戶端socket
@property (strong, nonatomic)  UITextField *ipText;//發(fā)送目標(biāo)的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:@"連接",@"發(fā)送",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(@"已經(jīng)連接");
        }
    }else{
        NSLog(@"已經(jīng)連接:%@",[_udpSocket connectedHost]);
    }
    
}
//客戶端發(fā)送本機(jī)IP地址給服務(wù)器
- (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 發(fā)送成功");
}
@end
操作效果截圖如下:
屏幕快照 2017-06-13 下午5.21.50.png

iOS-Socket-TCP篇

TCP篇這里把服務(wù)器模擬和客戶端點(diǎn)對點(diǎn)放在一起
圖解方式為:

未標(biāo)題-1.jpg

一.(GCDAsyncSocket)ARC的版本

1.服務(wù)器
#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];
// 存儲連接到服務(wù)器的socket的數(shù)組
    socketArray =[[NSMutableArray alloc] initWithCapacity:0];
    // 服務(wù)器socket實(shí)例化  在0x1234端口監(jiān)聽數(shù)據(jù)
    serverSocket=[[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [serverSocket acceptOnPort:0x1234 error:nil];
}

// 有新的socket向服務(wù)器鏈接自動回調(diào)
-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
    [socketArray addObject:newSocket];
    [clientTableView reloadData];
    // 如果下面的方法不寫 只能接收一次socket鏈接
    [newSocket readDataWithTimeout:-1 tag:100];
}

// 網(wǎng)絡(luò)連接成功后  自動回調(diào)
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"已連接到用戶:ip:%@",host);
}

// 接收到了數(shù)據(jù) 自動回調(diào)  sock客戶端
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"收到%@發(fā)來的消息:%@",[sock connectedHost],message);
    
    // 相當(dāng)與向服務(wù)起索取  在線用戶數(shù)據(jù)
    // 將連上服務(wù)器的所有ip地址 組成一個字符串 將字符串回寫到客戶端
    if ([message isEqualToString:@"GetClientList"]) {
        NSMutableString *clientList=[[NSMutableString alloc] initWithCapacity:0];
        int i=0;
        // 每一個客戶端連接服務(wù)器成功后 socketArray保存客戶端的套接字
        // [newSocket connectedHost] 獲取套接字對應(yīng)的IP地址
        for (GCDAsyncSocket *newSocket in socketArray) {
            // 以字符串形式分割ip地址  192..,192...,
            if (i!=0) {
                [clientList appendFormat:@",%@",[newSocket connectedHost]];
            }
            else{
                [clientList appendFormat:@"%@",[newSocket connectedHost]];
            }
            i++;
        }
        // 將服務(wù)端所有的ip連接成一個字符串對象
        NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
        // 將在線的所有用戶  以字符串的形式一次性發(fā)給客戶端
        // 哪個客戶端發(fā)起數(shù)據(jù)請求sock就表示誰
        [sock writeData:newData withTimeout:-1 tag:300];
    }
    else{
        // 將數(shù)據(jù)回寫給發(fā)送數(shù)據(jù)的用戶
        NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        
        [sock writeData:data withTimeout:-1 tag:300];
    }
    // 繼續(xù)讀取socket數(shù)據(jù)
    [sock readDataWithTimeout:-1 tag:200];
}

/*重連
 
 實(shí)現(xiàn)代理方法
 
 -(void)onSocketDidDisconnect:(GCDAsyncSocket *)sock
 {
 NSLog(@"sorry the connect is failure %ld",sock.userData);
 if (sock.userData == SocketOfflineByServer) {
 // 服務(wù)器掉線怒炸,重連
 [self socketConnectHost];
 }
 else if (sock.userData == SocketOfflineByUser) {
 // 如果由用戶斷開带饱,不進(jìn)行重連
 return;
 }
 
 }*/
// 連接斷開時  服務(wù)器自動回調(diào)
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock
{
    [socketArray removeObject:sock];
    [clientTableView reloadData];
}

// 向用戶發(fā)出的消息  自動回調(diào)
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"向用戶%@發(fā)出消息",[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];
    // 根據(jù)連接服務(wù)端套接字socket對象 獲取對應(yīng)的客戶端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;//服務(wù)器socket
    GCDAsyncSocket *listenSocket;//客戶端自身socket(接收消息)
    GCDAsyncSocket *clientSocket;//客戶端發(fā)送socket(發(fā)送消息)
    
    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];
    // 服務(wù)器socket實(shí)例化  在0x1234端口監(jiān)聽數(shù)據(jù)
    serverSocket=[[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [serverSocket acceptOnPort:PORT error:nil];

}

// 有新的socket向服務(wù)器鏈接自動回調(diào)
-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
    [socketArray addObject:newSocket];
    
    [clientTableView reloadData];
    
    // 如果下面的方法不寫 只能接收一次socket鏈接
    [newSocket readDataWithTimeout:-1 tag:100];
}

// 網(wǎng)絡(luò)連接成功后  自動回調(diào)
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"已連接到用戶:ip:%@",host);
}

// 接收到了數(shù)據(jù) 自動回調(diào)  sock客戶端
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    NSLog(@"收到%@發(fā)來的消息:%@",[sock connectedHost],message);
    
    // 相當(dāng)與向服務(wù)起索取  在線用戶數(shù)據(jù)
    // 將連上服務(wù)器的所有ip地址 組成一個字符串 將字符串回寫到客戶端
    if ([message isEqualToString:@"GetClientList"]) {
        NSMutableString *clientList=[[NSMutableString alloc] initWithCapacity:0];
        int i=0;
        // 每一個客戶端連接服務(wù)器成功后 socketArray保存客戶端的套接字
        // [newSocket connectedHost] 獲取套接字對應(yīng)的IP地址
        for (GCDAsyncSocket *newSocket in socketArray) {
            // 以字符串形式分割ip地址  192..,192...,
            if (i!=0) {
                [clientList appendFormat:@",%@",[newSocket connectedHost]];
            }
            else{
                [clientList appendFormat:@"%@",[newSocket connectedHost]];
            }
            i++;
        }
        // 將服務(wù)端所有的ip連接成一個字符串對象
        NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
        // 將在線的所有用戶  以字符串的形式一次性發(fā)給客戶端
        // 哪個客戶端發(fā)起數(shù)據(jù)請求sock就表示誰
        [sock writeData:newData withTimeout:-1 tag:300];
    }
    else{
        // 將數(shù)據(jù)回寫給發(fā)送數(shù)據(jù)的用戶
        NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        
        [sock writeData:data withTimeout:-1 tag:300];
    }
    
    // 繼續(xù)讀取socket數(shù)據(jù)
    [sock readDataWithTimeout:-1 tag:200];
    
}

/*重連
 
 實(shí)現(xiàn)代理方法
 
 -(void)onSocketDidDisconnect:(GCDAsyncSocket *)sock
 {
 NSLog(@"sorry the connect is failure %ld",sock.userData);
 if (sock.userData == SocketOfflineByServer) {
 // 服務(wù)器掉線纠炮,重連
 [self socketConnectHost];
 }
 else if (sock.userData == SocketOfflineByUser) {
 // 如果由用戶斷開灯蝴,不進(jìn)行重連
 return;
 }
 
 }*/
// 連接斷開時  服務(wù)器自動回調(diào)
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock
{
    [socketArray removeObject:sock];
    
    [clientTableView reloadData];
    
}

// 向用戶發(fā)出的消息  自動回調(diào)
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"向用戶%@發(fā)出消息",[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];
    // 根據(jù)連接服務(wù)端套接字socket對象 獲取對應(yīng)的客戶端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.服務(wù)器
#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];
    
    // 服務(wù)器socket實(shí)例化  在0x1234端口監(jiān)聽數(shù)據(jù)
    serverSocket=[[AsyncSocket alloc] init];
    serverSocket.delegate=self;
    [serverSocket acceptOnPort:0x1234 error:nil];
}

// 有新的socket向服務(wù)器鏈接自動回調(diào)
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
    [socketArray addObject:newSocket];
    [clientTableView reloadData];
    // 如果下面的方法不寫 只能接收一次socket鏈接
    [newSocket readDataWithTimeout:-1 tag:100];
}

// 網(wǎng)絡(luò)連接成功后  自動回調(diào)
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"已連接到用戶:ip:%@",host);
}

// 接收到了數(shù)據(jù) 自動回調(diào)  sock客戶端
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    NSLog(@"收到%@發(fā)來的消息:%@",[sock connectedHost],message);
    
    // 相當(dāng)與向服務(wù)起索取  在線用戶數(shù)據(jù)
    // 將連上服務(wù)器的所有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++;
        }
        // 將服務(wù)端所有的ip連接成一個字符串對象
        NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
        // 將在線的所有用戶  以字符串的形式一次性發(fā)給客戶端
        // 哪個客戶端發(fā)起數(shù)據(jù)請求sock就表示誰
        [sock writeData:newData withTimeout:-1 tag:300];
    }
    else{
        // 將數(shù)據(jù)回寫給發(fā)送數(shù)據(jù)的用戶
        NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        
        [sock writeData:data withTimeout:-1 tag:300];
    }
    
    // 繼續(xù)讀取socket數(shù)據(jù)
    [sock readDataWithTimeout:-1 tag:200];
    
}

// 連接斷開時  服務(wù)器自動回調(diào)
-(void)onSocketDidDisconnect:(AsyncSocket *)sock
{
    [socketArray removeObject:sock];
    
    [clientTableView reloadData];
    
}

// 向用戶發(fā)出的消息  自動回調(diào)
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"向用戶%@發(fā)出消息",[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];
    // 根據(jù)連接服務(wù)端套接字socket對象 獲取對應(yīng)的客戶端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(監(jiān)聽收對方發(fā)來的數(shù)據(jù))  server(連接服務(wù)器)  B(對方)
    
    // 服務(wù)器socket
    serverSocket =[[AsyncSocket alloc] init];
    serverSocket.delegate=self;
    // 本身
    listenSocket=[[AsyncSocket alloc] initWithDelegate:self];
    [listenSocket acceptOnPort:0x1235 error:nil];
    // 點(diǎn)對點(diǎn)通訊時 對方的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;
}
// 發(fā)數(shù)據(jù)  結(jié)束編輯  就是鍵盤隱藏的時候  自動調(diào)用
-(void)textFieldDidEndEditing:(UITextField *)textField
{

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

        // 向socket寫數(shù)據(jù)
        [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)
    {
        // 連接服務(wù)器
        if (![serverSocket isConnected])
        {
            //[serverSocket disconnect];
            // 指定 ip 指定端口 發(fā)起一個TCP連接
            [serverSocket connectToHost:@"192.168.1.108" onPort:0x1234 error:nil];
            // 向服務(wù)器寫數(shù)據(jù)
//            [serverSocket writeData:<#(NSData *)#> withTimeout:<#(NSTimeInterval)#> tag:<#(long)#>]
        }
          else
        {
            NSLog(@"已經(jīng)和服務(wù)器連接");
        }
    }
    // 斷開與服務(wù)器連接
         else if(button.tag==101)
    {
        // 斷開TCP連接
        [serverSocket disconnect];
    }
    // 獲取在線用戶
            else if(button.tag==102)
    {
        NSString *message=@"GetClientList";
        NSData *data=[message dataUsingEncoding:NSUTF8StringEncoding];
        // 向服務(wù)器獲取在線用戶信息
        // 向服務(wù)端寫字符串 GetClientList
        [serverSocket writeData:data withTimeout:-1 tag:400];
    }
}
// 接收到了一個新的socket連接 自動回調(diào)
// 接收到了新的連接  那么釋放老的連接
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
    
    if (sock==listenSocket) {
        NSLog(@"收到用戶%@的連接請求",[newSocket connectedHost]);
        if (clientSocket && [clientSocket isConnected]) {
            [clientSocket disconnect];
            
            [clientSocket release];
        }
        // 保存發(fā)起連接的客戶端socket
        clientSocket=[newSocket retain];
    }
}

// 寫數(shù)據(jù)成功 自動回調(diào)
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    // 獲取用戶列表
    if (sock==serverSocket) {
        NSLog(@"向服務(wù)器%@發(fā)送消息成功",[sock connectedHost]);
    }
    // 客戶端與客戶端通訊
    else if(sock==clientSocket){
        NSLog(@"向客戶%@發(fā)送消息成功",[sock connectedHost]);
    }
    // 繼續(xù)監(jiān)聽
    [sock readDataWithTimeout:-1 tag:500];
    
}

// 成功連接后自動回調(diào)
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    [sock readDataWithTimeout:-1 tag:200];
    NSLog(@"已經(jīng)連接到服務(wù)器:%@",host);
}

// 客戶端接收到了數(shù)據(jù)
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    // 如果時服務(wù)器給的消息  必然是在線用戶的消息
    if (sock==serverSocket) {
        NSString *message=[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
        // 將字符串以","分割 到數(shù)組中
        // 分割出IP地址
        NSArray *array=[message componentsSeparatedByString:@","];
        [clientArray removeAllObjects];
        [clientArray addObjectsFromArray:array];
        [clientTableView reloadData];
        
        NSLog(@"在線用戶列表:%@",clientArray);
    }
    // 點(diǎn)對點(diǎn)通訊
    else{
        NSString *message=[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
        NSLog(@"收到客戶%@的消息:%@",[sock connectedHost],message);
    }

    // 繼續(xù)監(jiān)聽
    [sock readDataWithTimeout:-1 tag:100];
}

// 點(diǎn)擊一行時   向該行對應(yīng)的ip地址發(fā)起連接
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *ip=[clientArray objectAtIndex:indexPath.row];
    if (clientSocket ) {
        // 如果之前連接上了一個好友 那么先斷開
        if ([clientSocket isConnected]) {
            [clientSocket disconnect];
        }
        // 向TableViewCell上指定ip發(fā)起TCP連接
        [clientSocket connectToHost:ip onPort:0x1235 error:nil];
        // clientSocket已經(jīng)指向了好友 如果需要和好友發(fā)消息
        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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市猿诸,隨后出現(xiàn)的幾起案子婚被,更是在濱河造成了極大的恐慌,老刑警劉巖梳虽,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件址芯,死亡現(xiàn)場離奇詭異,居然都是意外死亡窜觉,警方通過查閱死者的電腦和手機(jī)谷炸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來禀挫,“玉大人旬陡,你說我怎么就攤上這事∮镉ぃ” “怎么了描孟?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長砰左。 經(jīng)常有香客問我匿醒,道長,這世上最難降的妖魔是什么菜职? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任青抛,我火速辦了婚禮,結(jié)果婚禮上酬核,老公的妹妹穿的比我還像新娘。我一直安慰自己适室,他們只是感情好嫡意,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捣辆,像睡著了一般蔬螟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汽畴,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音鲁猩,去河邊找鬼廓握。 笑死隙券,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沐飘。 我是一名探鬼主播薪铜,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼隔箍,長吁一口氣:“原來是場噩夢啊……” “哼蜒滩!你這毒婦竟也來了俯艰?” 一聲冷哼從身側(cè)響起锌订,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤啦辐,失蹤者是張志新(化名)和其女友劉穎芹关,沒想到半個月后紧卒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跑芳,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怀樟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年漂佩,在試婚紗的時候發(fā)現(xiàn)自己被綠了投蝉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘩缆。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡庸娱,死狀恐怖熟尉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情剧包,我是刑警寧澤疆液,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站掉缺,受9級特大地震影響局骤,放射性物質(zhì)發(fā)生泄漏峦甩。R本人自食惡果不足惜凯傲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一冰单、第九天 我趴在偏房一處隱蔽的房頂上張望诫欠。 院中可真熱鬧,春花似錦轿偎、人聲如沸被廓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽看疙。三九已至狼荞,卻和暖如春帮碰,著一層夾襖步出監(jiān)牢的瞬間丰涉,已是汗流浹背一死。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工投慈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伪煤,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓扁誓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親足删。 傳聞我的和親對象是個殘疾皇子壹堰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容