IOS框架使用:GCDAsySocket

原創(chuàng):知識(shí)進(jìn)階型文章
創(chuàng)作不易沸毁,請(qǐng)珍惜殿漠,之后會(huì)持續(xù)更新谨朝,不斷完善
個(gè)人比較喜歡做筆記和寫總結(jié)卤妒,畢竟好記性不如爛筆頭哈哈,這些文章記錄了我的IOS成長歷程字币,希望能與大家一起進(jìn)步
溫馨提示:由于簡書不支持目錄跳轉(zhuǎn)则披,大家可通過command + F 輸入目錄標(biāo)題后迅速尋找到你所需要的內(nèi)容

目錄

  • 一、使用Socket建立即時(shí)通訊
    • 1洗出、創(chuàng)建socket建立連接
    • 2士复、發(fā)送消息
    • 3、接收數(shù)據(jù)
    • 4翩活、對(duì)接收信息和發(fā)送信息進(jìn)行格式處理
    • 5判没、即時(shí)通訊Demo運(yùn)行效果
  • 二、使用GCDAsySocket建立即時(shí)通訊
    • 1隅茎、導(dǎo)入框架和創(chuàng)建屬性
    • 2、點(diǎn)擊按鈕觸發(fā)的事件
    • 3嫉沽、GCDAsyncSocketDelegate
    • 4辟犀、演示運(yùn)行效果
  • 三、粘包拆包
    • 1绸硕、點(diǎn)擊按鈕觸發(fā)的事件
    • 2堂竟、發(fā)送數(shù)據(jù)格式化
    • 3、發(fā)送心跳
    • 4玻佩、重連機(jī)制
    • 5出嘹、演示運(yùn)行效果
  • 四、UDP畫板功能應(yīng)用
    • 1咬崔、點(diǎn)擊按鈕觸發(fā)事件
    • 2税稼、接受數(shù)據(jù)的回調(diào)
    • 3、進(jìn)行繪圖將點(diǎn)位信息發(fā)送給服務(wù)器
  • Demo
  • 參考文獻(xiàn)

一垮斯、使用Socket建立即時(shí)通訊

Socket是雙工+開關(guān)(A與B可以相互發(fā)送信息)可以主動(dòng)發(fā)送請(qǐng)求郎仆,支持即使通訊。

1兜蠕、創(chuàng)建socket建立連接

a扰肌、創(chuàng)建socket
  • domain:協(xié)議域。決定了socket的地址類型熊杨,在通信中必須采用對(duì)應(yīng)的地址曙旭,如AF_INET決定了要用ipv4地址(32位的)與端口號(hào)(16位的)的組合盗舰,而AF_UNIX決定了要用一個(gè)絕對(duì)路徑名作為地址。
  • type:指定Socket類型桂躏。流式SocketSOCK_STREAM)是一種面向連接的Socket钻趋,針對(duì)于面向連接的TCP服務(wù)應(yīng)用。數(shù)據(jù)報(bào)式SocketSOCK_DGRAM)是一種無連接的Socket沼头,對(duì)應(yīng)于無連接的UDP服務(wù)應(yīng)用爷绘。
  • protocol:指定協(xié)議。常用協(xié)議有IPPROTO_TCP进倍、IPPROTO_UDP土至,分別對(duì)應(yīng)TCP傳輸協(xié)議、UDP傳輸協(xié)議猾昆。typeprotocol不可以隨意組合陶因,如SOCK_STREAM不可以跟IPPROTO_UDP組合。當(dāng)?shù)谌齻€(gè)參數(shù)為0時(shí)垂蜗,會(huì)自動(dòng)選擇第二個(gè)參數(shù)類型對(duì)應(yīng)的默認(rèn)協(xié)議楷扬。
  • 返回值:如果調(diào)用成功就返回新創(chuàng)建的套接字的描述符,如果失敗就返回INVALID_SOCKET贴见。
int socketID = socket(AF_INET, SOCK_STREAM, 0);
self.clinenId = socketID;
if (socketID == -1)
{
    NSLog(@"創(chuàng)建socket失敗");
    return;
}

b烘苹、創(chuàng)建套接字地址
  • htons:將一個(gè)無符號(hào)短整型的主機(jī)數(shù)值轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序 (big-endian大尾順序、little-endian小尾順序)
  • inet_addr:是一個(gè)計(jì)算機(jī)函數(shù)片部,功能是將一個(gè)點(diǎn)分十進(jìn)制的IP轉(zhuǎn)換成一個(gè)長整數(shù)型數(shù)
#define SocketPort htons(8040)
#define SocketIP   inet_addr("127.0.0.1")
struct sockaddr_in socketAddr;
socketAddr.sin_family = AF_INET;// AF_INET(地址族)PF_INET(協(xié)議族)
socketAddr.sin_port   = SocketPort;// 端口
struct in_addr socketIn_addr;
socketIn_addr.s_addr  = SocketIP;// ip
socketAddr.sin_addr   = socketIn_addr;

c镣衡、建立連接
  • 參數(shù)一:套接字描述符
  • 參數(shù)二:指向數(shù)據(jù)結(jié)構(gòu)sockaddr的指針,其中包括目的端口和IP地址
  • 參數(shù)三:參數(shù)二sockaddr的長度档悠,可以通過sizeof(struct sockaddr)獲得
  • 返回值:成功則返回0廊鸥,失敗返回非0,錯(cuò)誤碼GetLastError()
int result = connect(socketID, (const struct sockaddr *)&socketAddr, sizeof(socketAddr));
if (result != 0)
{
    NSLog(@"鏈接失敗");
    return;
}
NSLog(@"鏈接成功");

d辖所、開啟服務(wù)器通過8040端口進(jìn)行鏈接
// 端口
#define SocketPort htons(8040)
// IP地址
#define SocketIP   inet_addr("127.0.0.1")
// 開啟服務(wù)器
2021-02-22 21:16:16.400957+0800 001---Socket初體驗(yàn)[53153:1499929] 創(chuàng)建socket 成功
2021-02-22 21:16:16.401220+0800 001---Socket初體驗(yàn)[53153:1499929] 綁定socket成功
2021-02-22 21:16:16.401554+0800 001---Socket初體驗(yàn)[53153:1499929] 監(jiān)聽成功
2021-02-22 21:16:20.000913+0800 001---Socket初體驗(yàn)[53153:1500736] 客戶端 in,socket:6
// 未開啟服務(wù)器鏈接失敗
2021-02-22 21:12:46.299454+0800 001---Socket初體驗(yàn)[52927:1488177] 鏈接失敗
// 開啟服務(wù)器后鏈接成功
2021-02-22 21:16:20.000716+0800 001---Socket初體驗(yàn)[52927:1488177] 鏈接成功

e惰说、在子線程異步接收消息
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self recvMessage];
});

2、發(fā)送消息

  • s:一個(gè)用于標(biāo)識(shí)已連接套接口的描述字缘回。
  • buf:包含待發(fā)送數(shù)據(jù)的緩沖區(qū)吆视。
  • len:緩沖區(qū)中數(shù)據(jù)的長度。
  • flags:調(diào)用執(zhí)行方式酥宴。
  • 返回值:如果成功則返回發(fā)送的字節(jié)數(shù)揩环,失敗則返回SOCKET_ERROR
- (void)sendMsgAction
{
    if (self.sendMsgContent_tf.text.length == 0)
    {
        return;
    }
    
    // 計(jì)算發(fā)送的消息長度
    const char *msg = self.sendMsgContent_tf.text.UTF8String;
    ssize_t sendLength = send(self.clinenId, msg, strlen(msg), 0);
    NSLog(@"發(fā)送 %ld 字節(jié)",sendLength);
    
    // 展示發(fā)送的消息
    [self showMsg:self.sendMsgContent_tf.text msgType:0];
    
    // 消息發(fā)送完成后清空文本框
    self.sendMsgContent_tf.text = @"";
}

3幅虑、接收數(shù)據(jù)

  • 參數(shù)一:客戶端socket
  • 參數(shù)二:接收內(nèi)容緩沖區(qū)地址
  • 參數(shù)三:接收內(nèi)容緩存區(qū)長度
  • 參數(shù)四:接收方式丰滑,0表示阻塞,必須等待服務(wù)器返回?cái)?shù)據(jù)
  • 返回值:如果成功,則返回讀入的字節(jié)數(shù)褒墨,失敗則返回SOCKET_ERROR
- (void)recvMessage
{
    // 1:通過循環(huán)的方式模擬長連接(如果不循環(huán)監(jiān)聽炫刷,那么發(fā)送一次消息后即斷開鏈接)
    while (1)
    {
        uint8_t buffer[1024];
        ssize_t recvLength = recv(self.clinenId, buffer, sizeof(buffer), 0);
        
        if (recvLength == 0)
        {
            NSLog(@"接收到了0個(gè)字節(jié)");
            continue;
        }
        
        // buffer -> data -> string
        NSData *data = [NSData dataWithBytes:buffer length:recvLength];
        NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"當(dāng)前線程為:%@,接收到的信息為:%@",[NSThread currentThread],string);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            // 作為接收到的消息進(jìn)行展示
            [self showMessage:string MessageType:1];
            // 接收到消息后清空文本框
            self.sendMessageContentTextField.text = @"";
        });
    }
}

4郁妈、對(duì)接收信息和發(fā)送信息進(jìn)行格式處理

- (void)showMessage:(NSString *)Message MessageType:(int)MessageType
{
    ...
}
a浑玛、將消息發(fā)送到時(shí)間添加到聊天框的文本中
NSMutableAttributedString *dateAttributedString = [[NSMutableAttributedString alloc] initWithString:showTimeStr];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = NSTextAlignmentCenter;// 段落對(duì)齊方式
[dateAttributedString addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13],NSForegroundColorAttributeName:[UIColor blackColor],NSParagraphStyleAttributeName:paragraphStyle} range:NSMakeRange(0, showTimeStr.length)];

[self.totalAttributeString appendAttributedString:dateAttributedString];
[self.totalAttributeString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@"\n"]];
b、我發(fā)送的消息
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.headIndent = 20.f;
NSMutableAttributedString *attributedString;
if (MessageType == 0)// 我發(fā)送的消息
{
    attributedString = [[NSMutableAttributedString alloc] initWithString:Message];
    paragraphStyle.alignment = NSTextAlignmentRight;
    [attributedString addAttributes:@{
                                      NSFontAttributeName:[UIFont systemFontOfSize:15],
                                      NSForegroundColorAttributeName:[UIColor whiteColor],
                                      NSBackgroundColorAttributeName:[UIColor blueColor],
                                      NSParagraphStyleAttributeName:paragraphStyle
                                      }
                              range:NSMakeRange(0, Message.length)];
}
c噩咪、對(duì)方發(fā)送的消息
else// 對(duì)方發(fā)送的消息
{
    Message = [Message substringToIndex:Message.length - 1];
    attributedString = [[NSMutableAttributedString alloc] initWithString:Message];

    [attributedString addAttributes:@{
                                      NSFontAttributeName:[UIFont systemFontOfSize:15],
                                      NSForegroundColorAttributeName:[UIColor blackColor],
                                      NSBackgroundColorAttributeName:[UIColor whiteColor],
                                      NSParagraphStyleAttributeName:paragraphStyle
                                      }
                              range:NSMakeRange(0, Message.length)];
}
d顾彰、將發(fā)送的消息添加到聊天框的文本中
[self.totalAttributeString appendAttributedString:attributedString];
[self.totalAttributeString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@"\n"]];
e、將聊天框的文本放到聊天框中
self.allMessageContentTextView.attributedText = self.totalAttributeString;

5胃碾、即時(shí)通訊Demo運(yùn)行效果

2021-02-25 14:05:47.431511+0800 SocketDemo[61539:2661249] 鏈接成功
2021-02-25 14:06:10.202964+0800 SocketDemo[61539:2679731] 當(dāng)前線程為:<NSThread: 0x600003cd6980>{number = 8, name = (null)}涨享,接收到的信息為:Hello Boy
2021-02-25 14:06:19.910566+0800 SocketDemo[61539:2661249] 發(fā)送 10 字節(jié)
2021-02-25 14:06:19.912298+0800 SocketDemo[61539:2661249] 系統(tǒng)當(dāng)前時(shí)間:2021-02-25 06:06:19 +0000,記錄日期:2021-02-25 06:06:10 +0000仆百,時(shí)間差:9.910700
2021-02-25 14:06:36.369588+0800 SocketDemo[61539:2679731] 當(dāng)前線程為:<NSThread: 0x600003cd6980>{number = 8, name = (null)}厕隧,接收到的信息為:I want to know you
2021-02-25 14:06:36.369950+0800 SocketDemo[61539:2661249] 系統(tǒng)當(dāng)前時(shí)間:2021-02-25 06:06:36 +0000,記錄日期:2021-02-25 06:06:19 +0000俄周,時(shí)間差:17.369708
2021-02-25 14:06:55.629877+0800 SocketDemo[61539:2661249] 發(fā)送 17 字節(jié)
2021-02-25 14:06:55.630306+0800 SocketDemo[61539:2661249] 系統(tǒng)當(dāng)前時(shí)間:2021-02-25 06:06:55 +0000吁讨,記錄日期:2021-02-25 06:06:36 +0000,時(shí)間差:19.630005

二峦朗、使用GCDAsySocket建立即時(shí)通訊

1建丧、導(dǎo)入框架和創(chuàng)建屬性

#import <GCDAsyncSocket.h>

@interface ViewController ()<GCDAsyncSocketDelegate>

@property (weak, nonatomic) IBOutlet UITextField *contentTF;
@property (nonatomic, strong) GCDAsyncSocket *socket;

@end

2、點(diǎn)擊按鈕觸發(fā)的事件

a波势、連接socket
- (IBAction)didClickConnectSocket:(id)sender
{
    // 創(chuàng)建socket
    if (self.socket == nil)
    {
        self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    
    // 連接socket
    if (!self.socket.isConnected)
    {
        NSError *error;
        [self.socket connectToHost:@"127.0.0.1" onPort:8090 withTimeout:-1 error:&error];
        if (error) NSLog(@"錯(cuò)誤信息:%@",error);
    }
}
b翎朱、發(fā)送消息
- (IBAction)didClickSendAction:(id)sender
{
    NSData *data = [self.contentTF.text dataUsingEncoding:NSUTF8StringEncoding];
    [self.socket writeData:data withTimeout:-1 tag:10086];
}
c、關(guān)閉socket
- (IBAction)didClickCloseAction:(id)sender
{
    [self.socket disconnect];
    self.socket = nil;
}
d艰亮、重連socket
- (IBAction)didClickReconnectAction:(id)sender
{
    // 創(chuàng)建socket
    if (self.socket == nil)
    {
        self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
        
    // 連接socket
    if (!self.socket.isConnected)
    {
        NSError *error;
        [self.socket connectToHost:@"127.0.0.1" onPort:8090 withTimeout:-1 error:&error];
        if (error) NSLog(@"%@",error);
    }
}

3、GCDAsyncSocketDelegate

a挣郭、已經(jīng)連接到服務(wù)器
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(nonnull NSString *)host port:(uint16_t)port
{
    NSLog(@"連接成功迄埃,主機(jī):%@,端口:%d",host,port);
    [self.socket readDataWithTimeout:-1 tag:10086];// -1 表示長鏈接兑障,保持鏈接狀態(tài)
}
b侄非、斷開連接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    NSLog(@"斷開socket連接,錯(cuò)誤原因:%@",err);
}
c流译、已經(jīng)接收到服務(wù)器返回來的數(shù)據(jù)
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSLog(@"接收到tag = %ld逞怨,長度 = %ld 的數(shù)據(jù)",tag,data.length);
    [self.socket readDataWithTimeout:-1 tag:10086];
}
d、成功向服務(wù)器發(fā)送消息
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"%ld 成功向服務(wù)器發(fā)送消息",tag);
}

4福澡、演示運(yùn)行效果

a叠赦、通過終端開啟服務(wù)器建立鏈接
xiejiapei@xiejiapeis-iMac ~ % nc -lk 8090
2021-02-25 15:35:24.627974+0800 GCDAsySocketDemo[63622:2787577] 連接成功,主機(jī):127.0.0.1革砸,端口:8090
b除秀、向服務(wù)端發(fā)送消息
2021-02-25 15:38:04.996879+0800 GCDAsySocketDemo[63622:2788493] 10086 成功向服務(wù)器發(fā)送消息
c糯累、服務(wù)器向客戶端發(fā)送消息
2021-02-25 15:45:03.072456+0800 GCDAsySocketDemo[63622:2796716] 接收到tag = 10086,長度 = 19 的數(shù)據(jù)

三册踩、粘包拆包

1泳姐、點(diǎn)擊按鈕觸發(fā)的事件

a、發(fā)送文本
- (IBAction)didClickSendTextAction:(UIButton *)sender
{
    NSData *data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
    
    unsigned int command = kcVideoDataType;
    [self sendData:data dataType:command];
}
b暂吉、發(fā)送圖片
- (IBAction)didClickSendImageAction:(UIButton *)sender
{
    UIImage *image = [UIImage imageNamed:@"luckcoffee"];
    NSData  *imageData  = UIImagePNGRepresentation(image);

    unsigned int command = kcImageDataType;
    [self sendData:imageData dataType:command];
}
c胖秒、發(fā)送視頻
- (IBAction)didClickSendVideoAction:(UIButton *)sender
{
    NSData  *videoData  = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"girl.mp4" ofType:nil]];
    
    unsigned int command = kcVideoDataType;
    [self sendData:videoData dataType:command];
}

2、發(fā)送數(shù)據(jù)格式化

a慕的、拼接發(fā)送數(shù)據(jù)
- (void)sendData:(NSData *)data dataType:(unsigned int)dataType
{
    NSMutableData *mData = [NSMutableData data];
    
    // 拼接數(shù)據(jù)總長度
    unsigned int dataLength = 4+4+(int)data.length;
    NSData *lengthData = [NSData dataWithBytes:&dataLength length:4];
    [mData appendData:lengthData];
    
    // 拼接數(shù)據(jù)類型
    NSData *typeData = [NSData dataWithBytes:&dataType length:4];
    [mData appendData:typeData];
    
    // 最后拼接實(shí)際數(shù)據(jù)
    [mData appendData:data];
    
    NSLog(@"發(fā)送數(shù)據(jù)的總字節(jié)大小: %ld",mData.length);
    
    // 發(fā)送數(shù)據(jù)
    [self.socket writeData:mData withTimeout:-1 tag:10086];
}
b阎肝、解析服務(wù)器返回的數(shù)據(jù)
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSLog(@"接收到tag = %ld,長度 = %ld 的數(shù)據(jù)",tag,data.length);
    
    // 獲取總的數(shù)據(jù)包大小
    NSData *totalSizeData = [data subdataWithRange:NSMakeRange(0, 4)];
    unsigned int totalSize = 0;
    [totalSizeData getBytes:&totalSize length:4];
    NSLog(@"響應(yīng)總數(shù)據(jù)的大小 %u",totalSize);
    
    // 獲取指令類型
    NSData *commandIdData = [data subdataWithRange:NSMakeRange(4, 4)];
    unsigned int commandId = 0;
    [commandIdData getBytes:&commandId length:4];
    
    // 獲取數(shù)據(jù)上傳結(jié)果
    NSData *resultData = [data subdataWithRange:NSMakeRange(8, 4)];
    unsigned int result = 0;
    [resultData getBytes:&result length:4];
    
    NSMutableString *str = [NSMutableString string];
    if (commandId == kcImageDataType)
    {
        [str appendString:@"圖片 "];
    }
    
    if(result == 1)
    {
        [str appendString:@"上傳成功"];
    }
    else
    {
        [str appendString:@"上傳失敗"];
    }
    NSLog(@"已經(jīng)接收服務(wù)器返回來的數(shù)據(jù):%@",str);
    
    [self.socket readDataWithTimeout:-1 tag:10086];
}

3业稼、發(fā)送心跳

發(fā)送心跳保證客戶端與服務(wù)器在同一個(gè)鏈接上盗痒,避免數(shù)據(jù)丟包。因?yàn)?code>Socket與TCP的長連接不同低散,只管發(fā)送消息不管服務(wù)端是否有能力接受消息俯邓,比如青樓今天客滿了那么即使再來了客人也沒有姑娘陪你。

a熔号、設(shè)置心跳機(jī)制
- (void)setupHeartBeat
{
    dispatch_async(dispatch_get_main_queue(), ^{
        
        [self destoryHeartBeat];
        
        __weak typeof(self) weakSelf = self;
        self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:15 repeats:YES block:^(NSTimer * _Nonnull timer) {
            __weak typeof(self) strongSelf = weakSelf;

            NSData *heartData = [@"heartBeat" dataUsingEncoding:NSUTF8StringEncoding];
            [strongSelf.socket writeData:heartData withTimeout:-1 tag:10086];
            NSLog(@"heartBeat");
        }];
    });
}
b稽鞭、銷毀心跳機(jī)制
- (void)destoryHeartBeat
{
    dispatch_main_async_safe(^{
        if (self.heartTimer && [self.heartTimer respondsToSelector:@selector(isValid)] && [self.heartTimer isValid])
        {
            [self.heartTimer invalidate];
            self.heartTimer = nil;
        }
    });
}

4、重連機(jī)制

a引镊、重連Socket
- (void)reconnectSocket
{
    // 1朦蕴、關(guān)閉socket
    [self disconnectSocket];
    
    // 2.1 超時(shí)判斷
    if (self.reconnectTime > 64)
    {
        NSLog(@"網(wǎng)絡(luò)超時(shí),不再重連");
        return;
    }
    
    // 2.2 延時(shí)等待重連
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.reconnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self connectSocketOrCreate];
    });
    
    // 3弟头、超時(shí)時(shí)長處理
    if (self.reconnectTime == 0)
    {
        self.reconnectTime = 2;
    }
    else
    {
        // 2^5 = 64(重連次數(shù))
        self.reconnectTime *= 2;
    }
}
b吩抓、關(guān)閉socket
- (void)disconnectSocket
{
    if (self.socket)
    {
        [self.socket disconnect];
        self.socket.delegate = nil;
        self.socket = nil;
        [self destoryHeartBeat];
    }
}
c、斷開連接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    NSLog(@"斷開socket連接赴恨,錯(cuò)誤原因:%@",err);
    
    // 進(jìn)行重連
    [self reconnectSocket];
}

5疹娶、演示運(yùn)行效果

a、客戶端與服務(wù)器建立鏈接
開啟服務(wù)器
2021-02-26 10:00:03.218274+0800 003---粘包拆包[73735:3331386] 服務(wù)器socket開啟成功
2021-02-26 10:01:20.478809+0800 003---粘包拆包[73735:3331573] 當(dāng)前客戶端的IP:127.0.0.1 端口號(hào)57491
2021-02-26 10:01:20.478968+0800 003---粘包拆包[73735:3331573] 當(dāng)前有1個(gè)客戶端連接
客戶端請(qǐng)求鏈接
2021-02-26 10:01:20.478908+0800 粘包拆包[74007:3345722] 連接成功伦连,主機(jī):127.0.0.1雨饺,端口:8060

b、客戶端向服務(wù)器發(fā)送文本信息
客戶端發(fā)送文本信息
2021-02-26 10:12:37.490970+0800 粘包拆包[74161:3357535] 發(fā)送數(shù)據(jù)的總字節(jié)大小: 13
2021-02-26 10:12:37.491273+0800 粘包拆包[74161:3357683] 10086 成功向服務(wù)器發(fā)送消息
服務(wù)器接收到客戶端發(fā)來的文本
2021-02-26 10:12:37.492464+0800 粘包拆包服務(wù)器[74145:3357277] 服務(wù)器接收到客戶端發(fā)來的文本:hello

c惑淳、客戶端向服務(wù)器發(fā)送圖片信息
客戶端發(fā)送圖片信息
2021-02-26 10:14:39.701774+0800 粘包拆包[74161:3357535] 發(fā)送數(shù)據(jù)的總字節(jié)大小: 2019792
2021-02-26 10:14:39.803917+0800 粘包拆包[74161:3359425] 10086 成功向服務(wù)器發(fā)送消息
服務(wù)器接收到客戶端發(fā)來的圖片信息
2021-02-26 10:14:39.702292+0800 003---粘包拆包[74145:3357277] 接收到tag = 10010 : 244980 長度的數(shù)據(jù)
2021-02-26 10:14:39.702425+0800 003---粘包拆包[74145:3357277] 接收總數(shù)據(jù)的大小 2019792
2021-02-26 10:14:39.702735+0800 003---粘包拆包[74145:3357277] 此次接收的數(shù)據(jù)包大小 244980
...
2021-02-26 10:14:39.806013+0800 003---粘包拆包[74145:3359429] 此次接收的數(shù)據(jù)包大小 237316
2021-02-26 10:14:39.806093+0800 003---粘包拆包[74145:3359429] 數(shù)據(jù)已經(jīng)接收完成
2021-02-26 10:16:10.002145+0800 003---粘包拆包[74145:3357024] 保存圖片成功

d额港、客戶端向服務(wù)器發(fā)送視頻信息
客戶端發(fā)送視頻信息
2021-02-26 10:14:39.803917+0800 粘包拆包[74161:3359425] 10086 成功向服務(wù)器發(fā)送消息
2021-02-26 10:17:31.754655+0800 粘包拆包[74161:3357535] 發(fā)送數(shù)據(jù)的總字節(jié)大小: 3887273
服務(wù)器接收到客戶端發(fā)來的視頻信息
2021-02-26 10:19:03.438611+0800 003---粘包拆包[74145:3361181] 接收到tag = 10010 : 195984 長度的數(shù)據(jù)
2021-02-26 10:19:03.438726+0800 003---粘包拆包[74145:3361181] 接收總數(shù)據(jù)的大小 3887273
2021-02-26 10:19:03.438872+0800 003---粘包拆包[74145:3361181] 此次接收的數(shù)據(jù)包大小 195984
...
2021-02-26 10:19:03.447098+0800 003---粘包拆包[74145:3361180] 此次接收的數(shù)據(jù)包大小 944937
2021-02-26 10:19:03.447222+0800 003---粘包拆包[74145:3361180] 數(shù)據(jù)已經(jīng)接收完成
2021-02-26 10:19:03.457837+0800 003---粘包拆包[74145:3361180] 寫入視頻狀態(tài): 1 路徑: /Users/xiejiapei/Library/Developer/CoreSimulator/Devices/5BC32A40-EDB6-4954-A93D-DE1741EFFB53/data/Containers/Data/Application/5CD7D2AA-2241-4E41-884A-388E089CE077/Documents/netVideo.mp4

e、服務(wù)器向客戶端發(fā)送圖片信息
服務(wù)器發(fā)送圖片信息
2021-02-26 10:42:27.557118+0800 003---粘包拆包[74404:3375045] 發(fā)送數(shù)據(jù)的總字節(jié)大小:8379226
2021-02-26 10:42:27.701872+0800 003---粘包拆包[74404:3387849] 10010 發(fā)送數(shù)據(jù)成功
客戶端接受到的信息

不知道為什么好像不行誒~嘗試了幾下都沒找到問題原因歧焦,有人修復(fù)了可以給我說一聲我改一下哈移斩,謝謝。

2021-02-26 10:45:43.956786+0800 粘包拆包[74613:3392355] 接收到tag = 10086,長度 = 666078 的數(shù)據(jù)
2021-02-26 10:45:43.956872+0800 粘包拆包[74613:3392355] 響應(yīng)總數(shù)據(jù)的大小 1429423039
2021-02-26 10:45:43.956937+0800 粘包拆包[74613:3392355] 已經(jīng)接收服務(wù)器返回來的數(shù)據(jù):上傳失敗

f叹哭、超時(shí)重連
殺死服務(wù)器后客戶端輸出的信息
2021-02-26 10:27:45.107291+0800 粘包拆包[74161:3374632] 斷開socket連接忍宋,錯(cuò)誤原因:Error Domain=GCDAsyncSocketErrorDomain Code=7 "Socket closed by remote peer" UserInfo={NSLocalizedDescription=Socket closed by remote peer}
2021-02-26 10:27:45.107865+0800 粘包拆包[74161:3374632] 網(wǎng)絡(luò)超時(shí),不再重連

四风罩、UDP畫板功能應(yīng)用

無論是在客戶端還是服務(wù)器糠排,只要一方在進(jìn)行繪圖,另外一方也會(huì)立即同步進(jìn)行繪圖超升。

1入宦、點(diǎn)擊按鈕觸發(fā)事件

a、創(chuàng)建socket連接服務(wù)器
- (IBAction)didClickCreatSocketAction:(UIBarButtonItem *)sender
{
    // 1 創(chuàng)建socket
    if (!self.udpSocket)
    {
        self.udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    NSLog(@"創(chuàng)建socket 成功");
    
    // 2: 綁定socket
    NSError * error = nil;
    [self.udpSocket bindToPort:8060 error:&error];
    if (error)
    {
        // 監(jiān)聽錯(cuò)誤打印錯(cuò)誤信息
        NSLog(@"error:%@",error);
    }
    else
    {
        // 3: 監(jiān)聽成功則開始接收信息
        [self.udpSocket beginReceiving:&error];
    }
}
b室琢、斷開socket連接
- (IBAction)didClickDisconnectAction:(id)sender
{
    [self.drawView.pathArray removeAllObjects];
    [self.drawView.currentPath removeAllPoints];
    [self.drawView setNeedsDisplay];
}

2乾闰、接受數(shù)據(jù)的回調(diào)

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
    ...
}
a、獲取到點(diǎn)位信息并將這些點(diǎn)連接起來
NSDictionary *tempDic = [NSJSONSerialization JSONObjectWithData:data
                                                        options:kNilOptions
                                                          error:nil];
NSArray *pointArray = [tempDic objectForKey:@"points"];
UIBezierPath *tempPath = [UIBezierPath bezierPath];
for (int i = 0; i < pointArray.count; I++)
{
    NSDictionary *pointDict = pointArray[I];
    CGPoint point = CGPointMake([pointDict[@"x"] floatValue] , [pointDict[@"y"] floatValue]);
    if (i == 0)
    {
        // 起始點(diǎn)
        [tempPath moveToPoint:point];
    }
    else
    {
        [tempPath addLineToPoint:point];
    }
}
b盈滴、獲取到線條寬度和顏色涯肩,將其繪制到連接好的點(diǎn)位上
dispatch_async(dispatch_get_main_queue(), ^{
    
    UIBezierPath *path = tempPath;
    NSString *lineColor = [tempDic objectForKey:@"lineColor"];
    CGFloat lineWidth = [[tempDic objectForKey:@"lineWidth"] floatValue];
    
    [self.drawView dealwithData:path lineColor:lineColor lineWidth:lineWidth];
    [self.drawView setNeedsDisplay];
});

3、進(jìn)行繪圖將點(diǎn)位信息發(fā)送給服務(wù)器

- (void)initWithDrawView
{
    __weak typeof(self) weakSelf = self;
    self.drawView.onePathEndBlock = ^(NSMutableDictionary *dict) {
        ...
    };
}
a巢钓、獲取到繪制的圖形的點(diǎn)位信息病苗、線條寬度、線條顏色
UIBezierPath *path = dict[@"drawPath"];
NSString *lineColor = dict[@"lineColor"];
CGFloat lineWidth = [dict[@"lineWidth"] floatValue];

NSArray *points = [path points];
NSMutableArray *temp = [NSMutableArray arrayWithCapacity:1];

for (id value in points)
{
    CGPoint point = [value CGPointValue];
    NSDictionary *dict = @{@"x":@(point.x),@"y":@(point.y)};
    [temp addObject:dict];
}
b症汹、將點(diǎn)位信息硫朦、線條寬度、線條顏色包裹成字典
NSDictionary *passDic = @{@"points"    : temp,
                          @"lineWidth" : @(lineWidth),
                          @"lineColor" : lineColor
                          };
c背镇、將字典轉(zhuǎn)化為數(shù)據(jù)類型再發(fā)送給服務(wù)器
NSData *passData = [NSJSONSerialization dataWithJSONObject:passDic
                                                   options:NSJSONWritingPrettyPrinted
                                                     error:nil];

[weakSelf.udpSocket sendData:passData toHost:@"127.0.0.1" port:8070 withTimeout:-1 tag:10088];

Demo

Demo在我的Github上咬展,歡迎下載。
UseFrameworkDemo

參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載瞒斩,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者破婆。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市胸囱,隨后出現(xiàn)的幾起案子祷舀,更是在濱河造成了極大的恐慌,老刑警劉巖旺矾,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔑鹦,死亡現(xiàn)場離奇詭異夺克,居然都是意外死亡箕宙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門铺纽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柬帕,“玉大人,你說我怎么就攤上這事∠萸蓿” “怎么了锅很?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凤跑。 經(jīng)常有香客問我爆安,道長,這世上最難降的妖魔是什么仔引? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任扔仓,我火速辦了婚禮,結(jié)果婚禮上咖耘,老公的妹妹穿的比我還像新娘翘簇。我一直安慰自己,他們只是感情好儿倒,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布版保。 她就那樣靜靜地躺著,像睡著了一般夫否。 火紅的嫁衣襯著肌膚如雪彻犁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天慷吊,我揣著相機(jī)與錄音袖裕,去河邊找鬼。 笑死溉瓶,一個(gè)胖子當(dāng)著我的面吹牛急鳄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播堰酿,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼疾宏,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了触创?” 一聲冷哼從身側(cè)響起坎藐,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哼绑,沒想到半個(gè)月后岩馍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抖韩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年蛀恩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茂浮。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡双谆,死狀恐怖壳咕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情顽馋,我是刑警寧澤谓厘,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站寸谜,受9級(jí)特大地震影響竟稳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熊痴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一住练、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧愁拭,春花似錦讲逛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惜论,卻和暖如春许赃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背馆类。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工混聊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乾巧。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓句喜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沟于。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咳胃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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