iOS開發(fā)中可以使用開源庫CocoaAsyncSocket
簡化socket
開發(fā)
**1.連接socket **
//創(chuàng)建一個(gè)TCP服務(wù) 連接到服務(wù)器
- (void)createTcpSocket {
if (self.asyncSocket==nil) {
self.asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}
if (self.asyncSocket.isConnected) {
} else {
NSError *error;
[self.asyncSocket connectToHost:_socketHost onPort:_socketPort withTimeout:-1 error:&error];
if (error) {
NSLog(@"%@",error);
}
}
}
2.實(shí)現(xiàn)socket代理方法
//已經(jīng)連接到服務(wù)器
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
NSLog(@"連接成功");
}
// 連接斷開
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
NSLog(@"連接斷開");
}
//消息發(fā)送成功 代理函數(shù) 向服務(wù)器 發(fā)送消息
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
NSLog(@"消息發(fā)送成功");
}
//已經(jīng)接收服務(wù)器返回來的數(shù)據(jù)
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSLog(@"接收到服務(wù)器返回的數(shù)據(jù)");
}
3.向服務(wù)器發(fā)送消息
[self.asyncSocket writeData:self.message withTimeout:-1 tag:0];
以上只是簡單的介紹CocoaAsyncSocket
的使用,下面結(jié)合實(shí)際開發(fā)中的需求來講解開發(fā)中的關(guān)鍵點(diǎn),比如 tag
用來干嘛???
在開發(fā)中前端和后端會(huì)約定一個(gè)固定的數(shù)據(jù)(消息)格式,按照這個(gè)格式來讀取數(shù)據(jù),就能把每組數(shù)據(jù)劃分出來,也就較好的解決了
粘包
掉包
的問題,數(shù)據(jù)不完整時(shí)也能獲知數(shù)據(jù)的缺失。
舉個(gè)栗子,如下表格是規(guī)定好的消息格式
起始符 | 目標(biāo)地址 | 原地址 | 數(shù)據(jù)長度 | 發(fā)送/接收 的數(shù)據(jù) | 檢驗(yàn)符 |
---|---|---|---|---|---|
0x02(1Byte) | dest(2Byte) | src(2Byte) | dataLength(2Byte) | data (數(shù)據(jù)) | CRC校驗(yàn) (1Byte) |
注:以上的消息格式分成3塊,數(shù)據(jù)和校驗(yàn)符在這里稱為身體部分
1.消息頭部:起始符 + 目標(biāo)地址 + 原地址 + 數(shù)據(jù)長度 = 7Byte,(頭部包含了那么多信息,并且長度是固定的,所以我們接收消息的時(shí)候,要先從頭部開始入手)
2.消息體:要發(fā)送或者接收到的數(shù)據(jù),長度為dataLength
3.校驗(yàn)符:用來檢驗(yàn)接收的數(shù)據(jù)是否完整一致(開發(fā)中可能沒有)
** 為了能夠在收到消息時(shí),先獲取到頭部信息 ,我們需要用到 tag
**
//固定的頭部長度
//起始符(1Byte) + 目標(biāo)地址(2byte) + 源地址(2byte) + 應(yīng)用層數(shù)據(jù)長度(2byte) = 7Byte
#define KPacketHeaderLength 7
typedef NS_ENUM(NSInteger ,KReadDataType){
TAG_FIXED_LENGTH_HEADER = 10,//消息頭部tag
TAG_RESPONSE_BODY = 11//消息體tag
};
** 所以在socket連接成功之后應(yīng)該這樣寫 **
//已經(jīng)連接到服務(wù)器
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
NSLog(@"連接成功");
// -1不超時(shí)一直讀取 等待數(shù)據(jù),先讀取頭部信息長度為 KPacketHeaderLength
// tag為頭部消息tag,這個(gè)在接收到數(shù)據(jù)時(shí),用來區(qū)分此次讀取的是頭部數(shù)據(jù)還是消息體數(shù)據(jù)
[self.asyncSocket readDataToLength:KPacketHeaderLength withTimeout:-1 tag:TAG_FIXED_LENGTH_HEADER];
}
** 在接受到服務(wù)器的數(shù)據(jù)時(shí) **
//已經(jīng)接收服務(wù)器返回來的數(shù)據(jù)
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSLog(@"接收到服務(wù)器返回的數(shù)據(jù)");
// 根據(jù)tag來做不同的操作
switch (tag) {
case TAG_FIXED_LENGTH_HEADER:
{
self.headData = data;
Byte *bytes = (Byte *)[data bytes];
//身體長度 = 消息體 + 1Byte的校驗(yàn)碼
self.bodyLength = [self readShort:bytes location:5] + 1;
// 從數(shù)據(jù)緩沖區(qū)讀取完整的身體部分?jǐn)?shù)據(jù),此時(shí)tag變成了TAG_RESPONSE_BODY
[self.asyncSocket readDataToLength:self.bodyLength withTimeout:-1 tag:TAG_RESPONSE_BODY];
}
break;
case TAG_RESPONSE_BODY:{
//如果當(dāng)前讀取出來的數(shù)據(jù)長度沒有達(dá)到完整包身體的長度,則包不完整(則根據(jù)當(dāng)前接收的數(shù)據(jù)長度,和身體長度比較,繼續(xù)讀取兩者相差的數(shù)據(jù)長度)
//讀取完身體數(shù)據(jù),開始校驗(yàn),校驗(yàn)成功,則展示數(shù)據(jù)并且,開始等待下一次讀取數(shù)據(jù),tag變成TAG_FIXED_LENGTH_HEADER
[self.asyncSocket readDataToLength:KPacketHeaderLength withTimeout:-1 tag:TAG_FIXED_LENGTH_HEADER];
}
break;
default:
break;
}
}
- (short)readShort:(Byte *)bytes location:(int)location {
return OSReadLittleInt16(bytes, location);
}
這些只是開發(fā)中可能會(huì)遇到的點(diǎn),實(shí)際中還有很多細(xì)節(jié)要處理