前言:
最近用GCDAsyncSocket寫個小東西,UDP通訊現(xiàn)在大多也使用GCD,很少用Runloop。然后粗略的了解了下UDP通訊。
它是比HTTP更加底層的通訊協(xié)議,特點是:傳輸快,無連接,系統(tǒng)開銷也少。
更詳細的基礎(chǔ)知識:Socket理論知識。
GCDAsyncUdpSocket這個框架很強大,你只需綁定端口通過代理接受消息,和對象方法發(fā)送消息。
Demo:

TCP.gif
簡介
- 第一個界面作為服務(wù)器端,綁定端口號,監(jiān)聽端口號里面的消息。
- 第二個界面作為客戶端發(fā)送消息,給服務(wù)器的端口號和局域網(wǎng)的IP地址(下圖有獲取本機IP地址方法),同時也綁定端口號,監(jiān)聽端口號里面消息。
- 消息發(fā)送后,服務(wù)器通過代理接受到消息,消息里面包括客戶端的IP地址·端口·發(fā)送的內(nèi)容。
- 服務(wù)器再在代理里面回復(fù)消息:“我收到了”,給客戶端的IP地址·端口。
備注:獲取本機IP地址方法。

獲取本機ip地址
代碼
- 服務(wù)端
// 初始化socket
-(void)initSocket {
self.title = @"服務(wù)器";
dispatch_queue_t dQueue = dispatch_queue_create("Server queue", NULL);
receiveSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self
delegateQueue:dQueue];
NSError *error;
// 綁定一個端口(可選),如果不綁定端口, 那么就會隨機產(chǎn)生一個隨機的電腦唯一的端口
// 端口數(shù)字范圍(1024,2^16-1)
[receiveSocket bindToPort:SERVERPORT error:&error];
if (error) {
NSLog(@"服務(wù)器綁定失敗");
}
// 開始接收對方發(fā)來的消息
[receiveSocket beginReceiving:nil];
}
// 接收消息代理
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext {
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
/**
* 更新UI一定要到主線程去操作啊
*/
dispatch_sync(dispatch_get_main_queue(), ^{
self.textView.text = msg;
});
NSLog(@"客戶端ip地址-->%@,port--->%u,內(nèi)容-->%@",
[GCDAsyncUdpSocket hostFromAddress:address],
[GCDAsyncUdpSocket portFromAddress:address],
msg);
NSString *sendStr = @"我收到了";
NSData *sendData = [sendStr dataUsingEncoding:NSUTF8StringEncoding];
// 該函數(shù)只是啟動一次發(fā)送 它本身不進行數(shù)據(jù)的發(fā)送, 而是讓后臺的線程慢慢的發(fā)送 也就是說這個函數(shù)調(diào)用完成后,數(shù)據(jù)并沒有立刻發(fā)送,異步發(fā)送
[receiveSocket sendData:sendData toHost:[GCDAsyncUdpSocket hostFromAddress:address]
port:[GCDAsyncUdpSocket portFromAddress:address]
withTimeout:60
tag:500];
}
- 客戶端
-(void)viewDidLoad {
[super viewDidLoad];
self.title = @"客戶端";
dispatch_queue_t qQueue = dispatch_queue_create("Client queue", NULL);
sendSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self
delegateQueue:qQueue];
NSError *error;
[sendSocket bindToPort:CLIENTPORT error:&error];
if (error) {
NSLog(@"客戶端綁定失敗");
}
[sendSocket beginReceiving:nil];
}
// 發(fā)送消息
-(IBAction)sendMsgClick:(UIButton *)sender {
NSData *sendData = [msgTF.text dataUsingEncoding:NSUTF8StringEncoding];
[sendSocket sendData:sendData
toHost:ipTF.text
port:SERVERPORT
withTimeout:60
tag:200];
}
// 發(fā)送消息失敗回調(diào)
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error {
if (tag == 200) {
NSLog(@"client發(fā)送失敗-->%@",error);
}
}
// 收到消息回調(diào)
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext {
NSString *receiveStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"服務(wù)器ip地址--->%@,host---%u,內(nèi)容--->%@",
[GCDAsyncUdpSocket hostFromAddress:address],
[GCDAsyncUdpSocket portFromAddress:address],
receiveStr);
dispatch_sync(dispatch_get_main_queue(), ^{
receiveLab.text = receiveStr;
});
}
// 關(guān)閉套接字,并銷毀
-(void)dealloc {
[sendSocket close];
sendSocket = nil;
}
要點
- 收到消息的回調(diào)方法里面,如果要更新UI的話,一定要切換到主線程里去實現(xiàn)操作。
- 向?qū)Ψ桨l(fā)消息,方法參數(shù)的端口號要是 對方綁定的端口號。
demo地址:https://github.com/dadahua/Demo