服務(wù)端:
1.創(chuàng)建一個用于監(jiān)聽的Socket
2.綁定ip&監(jiān)聽端口&接受新連接
3.監(jiān)聽新的連接
4.接收數(shù)據(jù)/發(fā)送數(shù)據(jù)
服務(wù)端示例代碼:
#import "ViewController.h"
#import "GCDAsyncSocket.h"
@interface ViewController () <GCDAsyncSocketDelegate>
// 1. 用于監(jiān)聽的socket
@property (nonatomic,strong) GCDAsyncSocket *listenSocket;
// 用于存放數(shù)據(jù)交互的socket
@property (nonatomic,strong) NSMutableArray *connectedSockets;
@end
@implementation ViewController
// 點擊開始服務(wù)器按鈕
- (IBAction)clickStartServerButton:(id)sender {
// 2. 綁定ip&監(jiān)聽端口&接受新連接封裝在一個方法中
/*
參數(shù)1: 地址
參數(shù)2: 端口
參數(shù)3: 錯誤
*/
BOOL success = [self.listenSocket acceptOnInterface:@"127.0.0.1" port:1234 error:nil];
if (success) {
NSLog(@"服務(wù)器開啟成功");
} else {
NSLog(@"服務(wù)器開啟失敗");
}
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
}
#pragma mark -- GCDAsyncSocketDelegate
/**
* 3. 已經(jīng)接受到新的連接后調(diào)用
*
* @param sock 服務(wù)端用于監(jiān)聽的socket
* @param newSocket 服務(wù)端用于數(shù)據(jù)交互的socket (Socket是一個調(diào)用接口,不能被傳遞的)
*/
- (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.
---> 如果不強(qiáng)引用用于數(shù)據(jù)交互的socket,當(dāng)連接后服務(wù)器就會把連接關(guān)閉了
e.g. 終端中通過telnet指令連接服務(wù)器: telnet 127.0.0.1 1234
終端提示: Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.
一旦有新的連接就會調(diào)用這個方法,所以每一個連接都需要強(qiáng)引用負(fù)責(zé)數(shù)據(jù)交互的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]; // 添加到可變數(shù)據(jù),進(jìn)行強(qiáng)引用
// newSocket.connectedHost 連接的端口號,可以打印出連接這臺服務(wù)器的主機(jī)ip
NSLog(@"接收到來自%@的連接",newSocket.connectedHost);
// 數(shù)據(jù)交互操作一定要使用newSocket,使用服務(wù)端用于監(jiān)聽的socket將會接收發(fā)送失敗
// 4.1 發(fā)送數(shù)據(jù)
NSString *string = [NSString stringWithFormat:@"歡迎連接我的服務(wù)器~"];
// withTimeout 超時時間,設(shè)置-1代表永遠(yuǎn)不超時
// tag 類似于button的tag值
[newSocket writeData:[string dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
// 接收數(shù)據(jù) (只能接收一次)
[newSocket readDataWithTimeout:-1 tag:0];
// 4.2 接收數(shù)據(jù) 定時器 輪循,一直來接收數(shù)據(jù)
// [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(readData:) userInfo:newSocket repeats:YES];
// 子線程的消息循環(huán)默認(rèn)不會開啟,需要手動開啟
// [[NSRunLoop currentRunLoop] run];
//定時器的方式接收數(shù)據(jù)缺點:1s執(zhí)行一次,并不實時,受設(shè)置的計時約束,另外如果沒有新數(shù)據(jù)也在調(diào)用
}
// 發(fā)送數(shù)據(jù)
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
NSLog(@"已經(jīng)發(fā)送數(shù)據(jù)后調(diào)用");
}
/**
* 已經(jīng)接收到數(shù)據(jù)后調(diào)用
*
* @param sock 數(shù)據(jù)交互的socket
* @param data 接收到的數(shù)據(jù)
* @param tag 標(biāo)記
*/
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
// 4.2 接收數(shù)據(jù) (按需獲取)
[sock readDataWithTimeout:-1 tag:0];
// 轉(zhuǎn)發(fā)用戶給其他用戶
for (GCDAsyncSocket *connectedSocket in self.connectedSockets) {
if (connectedSocket != sock ) {
// 參數(shù)中的sock是發(fā)送數(shù)據(jù)的主機(jī),將消息發(fā)送給發(fā)送這條消息外的其他主機(jī)
[connectedSocket writeData:data withTimeout:-1 tag:0];
}
}
}
// 定時器調(diào)用的方法
- (void)readData:(NSTimer *)timer{
// 接收數(shù)據(jù)
[timer.userInfo readDataWithTimeout:-1 tag:0];
}
#pragma mark -- 懶加載
// 用于監(jiān)聽的socket
- (GCDAsyncSocket *)listenSocket{
if (_listenSocket == nil) {
/*
delegateQueue:時效性選擇主線程,性能更好選擇異步線程
socketQueue: 執(zhí)行連接,接受再去隊列中執(zhí)行,設(shè)置NULL會自動設(shè)置隊列,使用自己的隊列容易出現(xiàn)線程問題
*/
_listenSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0) socketQueue:NULL];
}
return _listenSocket;
}
// 用于存放數(shù)據(jù)交互的socket
- (NSMutableArray *)connectedSockets{
if (_connectedSockets == nil) {
_connectedSockets = [NSMutableArray array];
}
return _connectedSockets;
}
@end
客戶端:
1.創(chuàng)建客戶端socket
2.建立連接
3.發(fā)送/讀取數(shù)據(jù)
客戶端示例代碼:
#import "ViewController.h"
#import "GCDAsyncSocket.h"
@interface ViewController () <GCDAsyncSocketDelegate>
// 客戶端的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
// 連接成功后調(diào)用
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
NSLog(@"已經(jīng)連接成功");
// 讀取數(shù)據(jù)
[sock readDataWithTimeout:-1 tag:0];
}
// 讀取數(shù)據(jù)
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
// 發(fā)送數(shù)據(jù) (在讀取完數(shù)據(jù)后,演示發(fā)送數(shù)據(jù))
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