Socket:網絡上的兩個程序通過一個雙向的通信連接實現(xiàn)數(shù)據(jù)的交換温亲,這個連接的一端稱為一個socket豪娜。
網絡通信要素
- IP地址:網絡上主機設備的唯一標識
- 端口號:服務器上有不同的應用程序泣棋,用于標示進程的邏輯地址败砂,不同進程的標示
傳輸協(xié)議
- TCP:需要建立連接,傳輸數(shù)據(jù)大小不受限制猜扮,可靠協(xié)議、安全送達监婶,效率低
a旅赢、HTTP底層就是通過socket建立連接通信管道,實現(xiàn)數(shù)據(jù)傳輸
b、HTTP是一個TCP的傳輸協(xié)議(方式)鲜漩,它是一個可靠源譬,安全的協(xié)議 - UDP:不需要建立連接集惋,數(shù)據(jù)大小有限制孕似,不可靠協(xié)議,傳輸速度快
TCP/UDP是數(shù)據(jù)傳輸?shù)姆绞焦涡蹋鳫TTP/XMPP等一種數(shù)據(jù)傳輸?shù)母袷胶砑溃瑸榱朔奖憬邮蘸妥x取,你可以自己定義協(xié)議格式
網上看了小碼哥的視頻實現(xiàn)一個簡單的聊天室雷绢,列下核心代碼搞清楚中間發(fā)生了哪些事
// 服務端代碼
#import "XMGServiceListener.h"
#import "GCDAsyncSocket.h" // 基于Scoket原生的框架 CocoaAsyncSocket github上有下
@interface XMGServiceListener()<GCDAsyncSocketDelegate>
@property (nonatomic, strong) GCDAsyncSocket *serverSocket; /** 服務端的socket */
@property (nonatomic, strong) NSMutableArray *clientSockets; //客戶端的所有socket對象
@end
@implementation XMGServiceListener
-(void)start{ // 開啟服務端
// 1.創(chuàng)建一個socket對象
// serverSocket 服務端的socket只監(jiān)聽 有沒有客戶端請求連接
GCDAsyncSocket *serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
// 2.綁定端口泛烙,并開啟監(jiān)聽,代表服務端已經開啟 端口號使用1024以上的 0-1024為系統(tǒng)端口
NSError *error = nil;
[serverSocket acceptOnPort:5288 error:&error];
if (!error) {
NSLog(@"服務開啟成功");
}else{
//失敗原因是端口被其它程序占用
NSLog(@"服務開啟失敗 %@",error);
}
self.serverSocket = serverSocket;
}
#pragma mark 有客戶端的socket連接到服務器
-(void)socket:(GCDAsyncSocket *)serverSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket{
NSLog(@"serverSocket %@ ",serverSocket);
NSLog(@"clientSocket %@ host:%@ port:%d",clientSocket,clientSocket.connectedHost,clientSocket.connectedPort);
//1.保存客戶端的socket
[self.clientSockets addObject:clientSocket];
// 2.監(jiān)聽客戶端有沒有數(shù)據(jù)上傳
//timeout -1 代表不超時
//tag 標識作用翘紊,現(xiàn)在不用蔽氨,就寫0
[clientSocket readDataWithTimeout:-1 tag:0];
NSLog(@"當前有%ld 客戶已經連接到服務器",self.clientSockets.count);
}
#pragma mark 讀取客戶端請求的數(shù)據(jù)
-(void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag{
// 1.把NSData轉NSString
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// 2.把當前客戶端的數(shù)據(jù) 轉發(fā)給 其它的客戶端
NSLog(@"接收到客戶端上傳的數(shù)據(jù):%@",str);
for (GCDAsyncSocket *socket in self.clientSockets) {
if (socket != clientSocket) { // 不發(fā)送給發(fā)消息的客戶端
[socket writeData:data withTimeout:-1 tag:0];
}
#warning 每次讀完數(shù)據(jù)后,都要調用一次監(jiān)聽數(shù)據(jù)的方法
[clientSocket readDataWithTimeout:-1 tag:0];
}
@end
// 客戶端代碼
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 實現(xiàn)聊天室
// 1.連接到群聊服務器
// 1.1.創(chuàng)建一個客戶端的socket對象
GCDAsyncSocket *clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
self.clientSocket = clientSocket;
// 1.2 發(fā)送連接請求
NSError *error = nil;
[clientSocket connectToHost:@"192.168.0.108" onPort:5288 error:&error];
if (!error) {
NSLog(@"%@",error);
}
// 2.發(fā)送聊天消息和接收聊天消息
}
-(void)socket:(GCDAsyncSocket *)clientSocket didConnectToHost:(NSString *)host port:(uint16_t)port{
NSLog(@"與服務器連接成功");
// 監(jiān)聽讀取數(shù)據(jù)
[clientSocket readDataWithTimeout:-1 tag:0];
}
// Disconnect 斷開連接
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
NSLog(@"與服務器斷開連接 %@",err);
}
#pragma mark 讀取消息
-(void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag{
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",str);
NSLog(@"%@",[NSThread currentThread]);
// 把消息添加到數(shù)據(jù)源
if (str) {
[self.dataSources addObject:str];
// 刷新表格
#warning 要在主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.tableView reloadData];
}];
}
// 監(jiān)聽讀取數(shù)據(jù)
[clientSocket readDataWithTimeout:-1 tag:0];
}
- (IBAction)sendAction:(id)sender {
// 發(fā)數(shù)據(jù)
[self.clientSocket writeData:[@"hello world" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
}
上面的聊天室是基于TCP的長鏈接帆疟,將客戶端socket從數(shù)組中移除就可以退出
XMPPFramework框架也是一種類似GCDAsyncSocket的框架鹉究,提供了一系列的方法去實現(xiàn)與服務端的連接和收發(fā)