客户端Socket-CocoaAsyncSocket建立连接&发送数据 - (Obj-C)

服务端:
1.创建一个用于监听的Socket
2.绑定ip&监听端口&接受新连接
3.监听新的连接
4.接收数据/发送数据

服务端示例代码:

#import "ViewController.h"
#import "GCDAsyncSocket.h"

@interface ViewController () 

// 1. 用于监听的socket
@property (nonatomic,strong) GCDAsyncSocket *listenSocket;
// 用于存放数据交互的socket
@property (nonatomic,strong) NSMutableArray *connectedSockets;

@end

@implementation ViewController

// 点击开始服务器按钮

- (IBAction)clickStartServerButton:(id)sender {
    
    // 2. 绑定ip&监听端口&接受新连接封装在一个方法中
    /*
       参数1: 地址
       参数2: 端口
       参数3: 错误
    */
    BOOL success = [self.listenSocket acceptOnInterface:@"127.0.0.1" port:1234 error:nil];
    
    if (success) {
        NSLog(@"服务器开启成功");
    } else {
        NSLog(@"服务器开启失败");
    }
    
}

- (void)viewDidLoad {
    [super viewDidLoad];

}
- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];
    
}

#pragma mark --  GCDAsyncSocketDelegate


/**
 *  3. 已经接受到新的连接后调用
 *
 *  @param sock      服务端用于监听的socket
 *  @param newSocket 服务端用于数据交互的socket (Socket是一个调用接口,不能被传递的)
 */
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{
    
    /**
     * Called when a socket accepts a connection.
     * Another socket is automatically spawned to handle it.
     *
     * You must retain the newSocket if you wish to handle the connection.
     * Otherwise the newSocket instance will be released and the spawned connection will be closed.
     
--->  如果不强引用用于数据交互的socket,当连接后服务器就会把连接关闭了
     e.g. 终端中通过telnet指令连接服务器:    telnet 127.0.0.1 1234
          终端提示:  Trying 127.0.0.1...
                    Connected to localhost.
                    Escape character is '^]'.
                    Connection closed by foreign host.
      一旦有新的连接就会调用这个方法,所以每一个连接都需要强引用负责数据交互的socket
     
     *
     * By default the new socket will have the same delegate and delegateQueue.
     * You may, of course, change this at any time.
     **/
    
    [self.connectedSockets addObject:newSocket]; // 添加到可变数据,进行强引用
    
    // newSocket.connectedHost 连接的端口号,可以打印出连接这台服务器的主机ip
    NSLog(@"接收到来自%@的连接",newSocket.connectedHost);
    
    // 数据交互操作一定要使用newSocket,使用服务端用于监听的socket将会接收发送失败
    
    // 4.1 发送数据
    NSString *string = [NSString stringWithFormat:@"欢迎连接我的服务器~"];
    
    // withTimeout 超时时间,设置-1代表永远不超时
    // tag 类似于button的tag值
    [newSocket writeData:[string dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
    
    // 接收数据  (只能接收一次)
    [newSocket readDataWithTimeout:-1 tag:0];
    
    // 4.2 接收数据  定时器 轮循,一直来接收数据
    // [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(readData:) userInfo:newSocket repeats:YES];
    
    // 子线程的消息循环默认不会开启,需要手动开启
    // [[NSRunLoop currentRunLoop] run];
    
    //定时器的方式接收数据缺点:1s执行一次,并不实时,受设置的计时约束,另外如果没有新数据也在调用
}


// 发送数据
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
    
    NSLog(@"已经发送数据后调用");
    
}
/**
 *  已经接收到数据后调用
 *
 *  @param sock 数据交互的socket
 *  @param data 接收到的数据
 *  @param tag  标记
 */
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
    
    
    NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    
    // 4.2 接收数据  (按需获取)
    [sock readDataWithTimeout:-1 tag:0];
    
    
    // 转发用户给其他用户
    for (GCDAsyncSocket *connectedSocket in self.connectedSockets) {
        
        if (connectedSocket != sock ) {
            // 参数中的sock是发送数据的主机,将消息发送给发送这条消息外的其他主机
            [connectedSocket writeData:data withTimeout:-1 tag:0];
        }
    }
    
}



// 定时器调用的方法
- (void)readData:(NSTimer *)timer{
    
    // 接收数据
    [timer.userInfo readDataWithTimeout:-1 tag:0];
    
}
#pragma mark -- 懒加载

// 用于监听的socket
- (GCDAsyncSocket *)listenSocket{
    
    if (_listenSocket == nil) {
        
        /*
         delegateQueue:时效性选择主线程,性能更好选择异步线程
         socketQueue:  执行连接,接受再去队列中执行,设置NULL会自动设置队列,使用自己的队列容易出现线程问题
         */
        
        _listenSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0) socketQueue:NULL];
        
    }
    return _listenSocket;
}

// 用于存放数据交互的socket
- (NSMutableArray *)connectedSockets{
    
    if (_connectedSockets == nil) {
        _connectedSockets = [NSMutableArray array];
    }
    return _connectedSockets;
}


@end

客户端:
1.创建客户端socket
2.建立连接
3.发送/读取数据

客户端示例代码:

#import "ViewController.h"
#import "GCDAsyncSocket.h"

@interface ViewController () 

// 客户端的socket
@property (nonatomic,strong) GCDAsyncSocket *clientSocket;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 建立连接
    [self.clientSocket connectToHost:@"127.0.0.1" onPort:1234 error:nil];
    
    
}


#pragma mark -- GCDAsyncSocketDelegate

// 连接成功后调用
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
    
    NSLog(@"已经连接成功");
    // 读取数据
    [sock readDataWithTimeout:-1 tag:0];
    
}

// 读取数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
    
    
    NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    
    
    // 发送数据  (在读取完数据后,演示发送数据)
    NSString *str = @"你好,我是客户端";
    [sock writeData:[str dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
    
    
    
    // 按需获取,接收到一条消息就打印
    [sock readDataWithTimeout:-1 tag:0];
    
}

#pragma mark -- 懒加载

- (GCDAsyncSocket *)clientSocket{
    if (_clientSocket == nil) {
        _clientSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue() socketQueue:NULL];
    }
    return _clientSocket;
}

@end

你可能感兴趣的:(客户端Socket-CocoaAsyncSocket建立连接&发送数据 - (Obj-C))