欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

iossocket開發(fā),iOSsocket

iOS應(yīng)用中使用AsyncSocket庫處理Socket通信的用法

用socket可以實(shí)現(xiàn)像QQ那樣發(fā)送即時(shí)消息的功能。客戶端和服務(wù)端需要建立長連接,在長連接的情況下,發(fā)送消息。客戶端可以發(fā)送心跳包來檢測長連接。

創(chuàng)新互聯(lián)是網(wǎng)站建設(shè)技術(shù)企業(yè),為成都企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、成都外貿(mào)網(wǎng)站建設(shè)公司,網(wǎng)站設(shè)計(jì),網(wǎng)站制作,網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制適合企業(yè)的網(wǎng)站。十多年品質(zhì),值得信賴!

在iOS開發(fā)中使用socket,一般都是用第三方庫AsyncSocket,不得不承認(rèn)這個(gè)庫確實(shí)很強(qiáng)大。下載地址CocoaAsyncSocket。

特性

AsyncSocket類是支持TCP的。

AsyncUdpSocket是支持UDP的。

AsyncSocket是封裝了CFSocket和CFSteam的TCP/IP socket網(wǎng)絡(luò)庫。它提供了異步操作,本地cocoa類的基于delegate的完整支持。主要有以下特性:

隊(duì)列的非阻塞的讀和寫,而且可選超時(shí)。你可以調(diào)用它讀取和寫入,它會(huì)當(dāng)完成后告知你。

自動(dòng)的socket接收。如果你調(diào)用它接收連接,它將為每個(gè)連接啟動(dòng)新的實(shí)例,當(dāng)然,也可以立即關(guān)閉這些連接。

委托(delegate)支持。錯(cuò)誤、連接、接收、完整的讀取、完整的寫入、進(jìn)度以及斷開連接,都可以通過委托模式調(diào)用。

基于run loop的,而不是線程的。雖然可以在主線程或者工作線程中使用它,但你不需要這樣做。它異步的調(diào)用委托方法,使用NSRunLoop。委托方法包括socket的參數(shù),可讓你在多個(gè)實(shí)例中區(qū)分。

自包含在一個(gè)類中。你無需操作流或者socket,這個(gè)類幫你做了全部。

支持基于IPV4和IPV6的TCP流。

AsyncUdpSocket是UDP/IP socket網(wǎng)絡(luò)庫,包裝自CFSocket。它的工作很像TCP版本,只不過是用于處理UDP的.。它包括基于非阻塞隊(duì)列的發(fā)送接收操作,完整的委托支持,基于runloop,自包含的類,以及支持IPV4和IPV6。

使用AsyncSocket的時(shí)候可以做一層封裝,根據(jù)需求提供幾個(gè)接口出來。比如:連接、斷開連接、發(fā)送消息等等。還有接受消息,接受到的消息可以通過通知、代理、block等傳出去。

下面簡單介紹一下對(duì)AsyncSocket使用.一般來說,一個(gè)用戶只需要建立一個(gè)socket長連接,所以可以用單例類方便使用。

定義單列類:LGSocketServe

LGSocketServe.h

復(fù)制代碼 代碼如下:

//

// LGSocketServe.h

// AsyncSocketDemo

//

#import

#import "AsyncSocket.h"

@interface LGSocketServe : NSObject

+ (LGSocketServe *)sharedSocketServe;

@end

LGSocketServe.m

復(fù)制代碼 代碼如下:

//

// LGSocketServe.m

// AsyncSocketDemo

//

#import "LGSocketServe.h"

@implementation LGSocketServe

static LGSocketServe *socketServe = nil;

#pragma mark public static methods

+ (LGSocketServe *)sharedSocketServe {

@synchronized(self) {

if(socketServe == nil) {

socketServe = [[[self class] alloc] init];

}

}

return socketServe;

}

+(id)allocWithZone:(NSZone *)zone

{

@synchronized(self)

{

if (socketServe == nil)

{

socketServe = [super allocWithZone:zone];

return socketServe;

}

}

return nil;

}

@end

建立socket長連接

LGSocketServe.h

復(fù)制代碼 代碼如下:

@property (nonatomic, strong) AsyncSocket *socket; // socket

// socket連接

- (void)startConnectSocket;

LGSocketServe.m

//自己設(shè)定

#define HOST @"192.168.0.1"

#define PORT 8080

//設(shè)置連接超時(shí)

#define TIME_OUT 20

- (void)startConnectSocket

{

self.socket = [[AsyncSocket alloc] initWithDelegate:self];

[self.socket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];

if ( ![self SocketOpen:HOST port:PORT] )

{

}

}

- (NSInteger)SocketOpen:(NSString*)addr port:(NSInteger)port

{

if (![self.socket isConnected])

{

NSError *error = nil;

[self.socket connectToHost:addr onPort:port withTimeout:TIME_OUT error:error];

}

return 0;

}

宏定義一下HOST、PORT、TIME_OUT,實(shí)現(xiàn)startConnectSocket方法。這個(gè)時(shí)候要設(shè)置一下AsyncSocket的代理AsyncSocketDelegate。當(dāng)長連接成功之后會(huì)調(diào)用:

復(fù)制代碼 代碼如下:

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port

{

//這是異步返回的連接成功,

NSLog(@"didConnectToHost");

}

心跳

LGSocketServe.h

復(fù)制代碼 代碼如下:

@property (nonatomic, retain) NSTimer *heartTimer; // 心跳計(jì)時(shí)器

LGSocketServe.m

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port

{

//這是異步返回的連接成功,

NSLog(@"didConnectToHost");

//通過定時(shí)器不斷發(fā)送消息,來檢測長連接

self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES];

[self.heartTimer fire];

}

// 心跳連接

-(void)checkLongConnectByServe{

// 向服務(wù)器發(fā)送固定可是的消息,來檢測長連接

NSString *longConnect = @"connect is here";

NSData *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding];

[self.socket writeData:data withTimeout:1 tag:1];

}

在連接成功的回調(diào)方法里,啟動(dòng)定時(shí)器,每隔2秒向服務(wù)器發(fā)送固定的消息來檢測長連接。(這個(gè)根據(jù)服務(wù)器的需要就可以了)

斷開連接

1,用戶手動(dòng)斷開連接

LGSocketServe.h

復(fù)制代碼 代碼如下:

// 斷開socket連接

-(void)cutOffSocket;

LGSocketServe.m

-(void)cutOffSocket

{

self.socket.userData = SocketOfflineByUser;

[self.socket disconnect];

}

cutOffSocket是用戶斷開連接之后,不在嘗試重新連接。

2,wifi斷開,socket斷開連接

LGSocketServe.m

復(fù)制代碼 代碼如下:

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err

{

NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]);

if (err.code == 57) {

self.socket.userData = SocketOfflineByWifiCut;

}

}

wifi斷開之后,會(huì)回調(diào)onSocket:willDisconnectWithError:方法,err.code == 57,這個(gè)時(shí)候設(shè)置self.socket.userData = SocketOfflineByWifiCut。

重新連接

socket斷開之后會(huì)回調(diào):

LGSocketServe.m

復(fù)制代碼 代碼如下:

- (void)onSocketDidDisconnect:(AsyncSocket *)sock

{

NSLog(@"7878 sorry the connect is failure %ld",sock.userData);

if (sock.userData == SocketOfflineByServer) {

// 服務(wù)器掉線,重連

[self startConnectSocket];

}

else if (sock.userData == SocketOfflineByUser) {

// 如果由用戶斷開,不進(jìn)行重連

return;

}else if (sock.userData == SocketOfflineByWifiCut) {

// wifi斷開,不進(jìn)行重連

return;

}

}

在onSocketDidDisconnect回調(diào)方法里面,會(huì)根據(jù)self.socket.userData來判斷是否需要重新連接。

發(fā)送消息

LGSocketServe.h

復(fù)制代碼 代碼如下:

// 發(fā)送消息

- (void)sendMessage:(id)message;

LGSocketServe.m

//設(shè)置寫入超時(shí) -1 表示不會(huì)使用超時(shí)

#define WRITE_TIME_OUT -1

- (void)sendMessage:(id)message

{

//像服務(wù)器發(fā)送數(shù)據(jù)

NSData *cmdData = [message dataUsingEncoding:NSUTF8StringEncoding];

[self.socket writeData:cmdData withTimeout:WRITE_TIME_OUT tag:1];

}

//發(fā)送消息成功之后回調(diào)

- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag

{

}

發(fā)送消息成功之后會(huì)調(diào)用onSocket:didWriteDataWithTag:,在這個(gè)方法里可以進(jìn)行讀取消息。

接受消息

LGSocketServe.m

復(fù)制代碼 代碼如下:

//設(shè)置讀取超時(shí) -1 表示不會(huì)使用超時(shí)

#define READ_TIME_OUT -1

#define MAX_BUFFER 1024

//發(fā)送消息成功之后回調(diào)

- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag

{

//讀取消息

[self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];

}

//接受消息成功之后回調(diào)

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

{

//服務(wù)端返回消息數(shù)據(jù)量比較大時(shí),可能分多次返回。所以在讀取消息的時(shí)候,設(shè)置MAX_BUFFER表示每次最多讀取多少,當(dāng)data.length MAX_BUFFER我們認(rèn)為有可能是接受完一個(gè)完整的消息,然后才解析

if( data.length MAX_BUFFER )

{

//收到結(jié)果解析...

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];

NSLog(@"%@",dic);

//解析出來的消息,可以通過通知、代理、block等傳出去

}

[self.socket readDataWithTimeout:READ_TIME_OUT buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];

接受消息后去解析,然后可以通過通知、代理、block等傳出去。在onSocket:didReadData:withTag:回調(diào)方法里面需要不斷讀取消息,因?yàn)閿?shù)據(jù)量比較大的話,服務(wù)器會(huì)分多次返回。所以我們需要定義一個(gè)MAX_BUFFER的宏,表示每次最多讀取多少。當(dāng)data.length MAX_BUFFER我們認(rèn)為有可能是接受完一個(gè)完整的消息,然后才解析 。

出錯(cuò)處理

LGSocketServe.m

復(fù)制代碼 代碼如下:

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err

{

NSData * unreadData = [sock unreadData]; // ** This gets the current buffer

if(unreadData.length 0) {

[self onSocket:sock didReadData:unreadData withTag:0]; // ** Return as much data that could be collected

} else {

NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]);

if (err.code == 57) {

self.socket.userData = SocketOfflineByWifiCut;

}

}

}

socket出錯(cuò)會(huì)回調(diào)onSocket:willDisconnectWithError:方法,可以通過unreadData來讀取未來得及讀取的buffer。

使用

導(dǎo)入#import “LGSocketServe.h”

復(fù)制代碼 代碼如下:

LGSocketServe *socketServe = [LGSocketServe sharedSocketServe];

//socket連接前先斷開連接以免之前socket連接沒有斷開導(dǎo)致閃退

[socketServe cutOffSocket];

socketServe.socket.userData = SocketOfflineByServer;

[socketServe startConnectSocket];

//發(fā)送消息 @"hello world"只是舉個(gè)列子,具體根據(jù)服務(wù)端的消息格式

[socketServe sendMessage:@"hello world"];

iOS使用socket實(shí)現(xiàn)聊天功能

關(guān)于GCDAsyncSocket實(shí)現(xiàn)即時(shí)通訊功能

客戶端需要做的:

1、連接服務(wù)器,連接socket,發(fā)送心跳

2、編碼數(shù)據(jù)包,發(fā)送消息給服務(wù)器

3、接收處理服務(wù)器返回的消息

4、提供手動(dòng)斷聯(lián)socket方法,在需要的地方調(diào)用

5、socket非手動(dòng)斷聯(lián),重新請(qǐng)求連接

服務(wù)端需要做的:

1、用戶心跳的維持和刷新

2、用戶的調(diào)用

3、數(shù)據(jù)包的拆解和分發(fā)等

新建socket管理類BLSocketManager

.h文件

.m文件

iOS開發(fā)之藍(lán)牙/Socket鏈接小票打印機(jī)(二)

上一篇 主要介紹了部分ESC/POS指令集,包括一些常用的排版指令,打印位圖指令等。另外,還介紹了將圖片轉(zhuǎn)換成點(diǎn)陣圖的方法。在這篇文章中,將主要介紹通過藍(lán)牙和Socket連接打印機(jī),發(fā)送打印指令相關(guān)知識(shí)。這里將用到 CoreBluetooth.framework 和 CocoaAsyncSocket 。

藍(lán)牙是一種支持設(shè)備間短距離通訊的無線電技術(shù)。iOS系統(tǒng)中,有四個(gè)框架支持藍(lán)牙鏈接:

CoreBluetooth框架有兩個(gè)核心概念,central(中心)和 peripheral(外設(shè)),它們分別有自己對(duì)應(yīng)的API;這里顯然是手機(jī)作為central,藍(lán)牙打印機(jī)作為peripheral;

設(shè)置代理后,會(huì)回調(diào)此方法,確認(rèn)藍(lán)牙狀態(tài),當(dāng)狀態(tài)為 CBCentralManagerStatePoweredOn 才能去掃描設(shè)備,藍(lán)牙狀態(tài)變化時(shí),也會(huì)回調(diào)此方法

調(diào)用此方法開始掃描外設(shè)

注意:第一個(gè)參數(shù)指定一個(gè) CBUUID 對(duì)象數(shù)組,每個(gè)對(duì)象表示外圍設(shè)備正在通告的服務(wù)的通用唯一標(biāo)識(shí)符(UUID)。此時(shí),僅返回公布這些服務(wù)的外設(shè)。當(dāng)參數(shù)為 nil ,則返回所有已發(fā)現(xiàn)的外設(shè),而不管其支持的服務(wù)是什么。

當(dāng)掃描到4.0外設(shè)后會(huì)回調(diào)此方法,這里包含設(shè)備的相關(guān)信息,如名稱、UUID、信號(hào)強(qiáng)度等;

調(diào)用此方法連接外設(shè)

[self.centralManager connectPeripheral:peripheral options:nil];

注意:第一個(gè)參數(shù)是要連接的外設(shè)。第二個(gè)參數(shù) options 是可選的 NSDictionary ,系統(tǒng)定義了一下三個(gè)鍵,它們的值都是NSNumber (Boolean);默認(rèn)為NO。當(dāng)設(shè)置為YES,則應(yīng)用進(jìn)入后臺(tái)或者被掛起后,系統(tǒng)會(huì)用Alert通知藍(lán)牙外設(shè)的狀態(tài)變化,效果是這樣

連接成功或失敗,都有對(duì)應(yīng)的回調(diào)方法

連接成功后設(shè)置代理 peripheral.delegate = self ,調(diào)用 [peripheral discoverServices:nil]; 尋找外設(shè)內(nèi)的服務(wù)。這里的參數(shù)是一個(gè)存放 CBUUID 對(duì)象的數(shù)組,用于發(fā)現(xiàn)特定的服務(wù)。當(dāng)傳nil時(shí),表示發(fā)現(xiàn)外設(shè)內(nèi)所有的服務(wù)。發(fā)現(xiàn)服務(wù)后系統(tǒng)會(huì)回調(diào)下面的方法:

發(fā)現(xiàn)服務(wù)后,調(diào)用 [peripheral discoverCharacteristics:nil forService:service]; 去發(fā)現(xiàn)服務(wù)中包含的特征。和上面幾個(gè)方法一樣,第一個(gè)參數(shù)用于發(fā)現(xiàn)指定的特征。為nil時(shí),表示發(fā)現(xiàn)服務(wù)的所有特征。

當(dāng)掃描到寫入特征時(shí),保存,用于寫入數(shù)據(jù)。

寫入數(shù)據(jù),我們只需要調(diào)用方法

這里的 self.peripheral 就是連接的外設(shè), self.characteristicInfo 就是之前保存的寫入特征;這里最好使用 CBCharacteristicPropertyWrite 特征,并且 type 選擇 CBCharacteristicWriteWithResponse 。當(dāng)寫入數(shù)據(jù)成功后,系統(tǒng)會(huì)通過下面這個(gè)方法通知我們:

由于藍(lán)牙設(shè)備每次可寫入的數(shù)據(jù)量是有限制的,因此,我們需要將之前拼接的打印數(shù)據(jù)進(jìn)行拆分,分批發(fā)送給打印機(jī)

這里的 MAX_CHARACTERISTIC_VALUE_SIZE 是個(gè)宏定義,表示每次發(fā)送的數(shù)據(jù)長度,經(jīng)筆者測試,當(dāng) MAX_CHARACTERISTIC_VALUE_SIZE = 20 時(shí),打印文字是正常速度。但打印圖片的速度非常慢, 應(yīng)該在硬件允許的范圍內(nèi),每次發(fā)盡量多的數(shù)據(jù)。 不同品牌型號(hào)的打印機(jī),這個(gè)參數(shù)是不同的,筆者的藍(lán)牙打印機(jī)該值最多到140。超出后會(huì)出現(xiàn)無法打印問題。 最后筆者將該值定為 MAX_CHARACTERISTIC_VALUE_SIZE = 120 ,測試了公司幾臺(tái)打印機(jī)都沒有問題。

另外iOS9以后增加了方法 maximumWriteValueLengthForType: 可以獲取寫入特診的最大寫入數(shù)據(jù)量,但經(jīng)筆者測試,對(duì)于部分打印機(jī)(比如我們公司的)是不準(zhǔn)確的,因此,不要太依賴此方法,最好還是自己取一個(gè)合適的值。

注意:每個(gè)打印機(jī)都有一個(gè)緩沖區(qū),緩沖區(qū)的大小視品牌型號(hào)有所不同。打印機(jī)的打印速度有限,如果我們瞬間發(fā)送大量的數(shù)據(jù)給打印機(jī),會(huì)造成打印機(jī)緩沖區(qū)滿。緩沖區(qū)滿后,如繼續(xù)寫入,可能會(huì)出現(xiàn)數(shù)據(jù)丟失,打印亂碼。

這里使用 CocoaAsyncSocket 開源框架,與打印機(jī)進(jìn)行 Socket 連接。 CocoaAsyncSocket 中主要包含兩個(gè)類:

這里我們只用到 GCDAsyncSocket ,因此只需要將 GCDAsyncSocket.h 和 GCDAsyncSocket.m 兩個(gè)文件導(dǎo)入項(xiàng)目。

注意:手機(jī)和打印機(jī)必須在同一局域網(wǎng)下,設(shè)置到打印機(jī)的host和port。

連接成功后會(huì)通過代理回調(diào)

Timeout為負(fù),表示不設(shè)置超時(shí)時(shí)間。這里的data就是 上一篇 中拼接的打印數(shù)據(jù)。

寫入完成后回調(diào)

斷開連接有以下幾種方法

連接斷開后回調(diào)

讀取到數(shù)據(jù)會(huì)回調(diào)

網(wǎng)口打印機(jī)一般都支持狀態(tài)查詢,查詢指令如下:

可以通過 上一篇 介紹指令拼接方法,查詢打印機(jī)的狀態(tài)。

本篇只是簡單介紹了,通過藍(lán)牙和Socket連接打印機(jī)的方法。雖然可以初步完成連接和打印,但是,在真正的項(xiàng)目中使用還是遠(yuǎn)遠(yuǎn)不夠的。這里還有很多情況需要考慮,比如連接斷開、打印機(jī)異常、打印機(jī)緩沖區(qū)滿、打印機(jī)缺紙等。我們可以針對(duì)自身的業(yè)務(wù)情況,進(jìn)行相應(yīng)的處理。

Core Bluetooth Programming Guide

Getting the pixel data from a CGImage object

Core Bluetooth Programming Guide

iOS開發(fā)網(wǎng)絡(luò)篇—Socket編程

一、網(wǎng)絡(luò)各個(gè)協(xié)議:TCP/IP、SOCKET、HTTP等

網(wǎng)絡(luò)七層由下往上分別為物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、會(huì)話層、表示層和應(yīng)用層。

其中物理層、數(shù)據(jù)鏈路層和網(wǎng)絡(luò)層通常被稱作媒體層,是網(wǎng)絡(luò)工程師所研究的對(duì)象;

傳輸層、會(huì)話層、表示層和應(yīng)用層則被稱作主機(jī)層,是用戶所面向和關(guān)心的內(nèi)容。

http協(xié)議對(duì)應(yīng)于應(yīng)用層

tcp協(xié)議對(duì)應(yīng)于傳輸層

ip協(xié)議對(duì)應(yīng)于網(wǎng)絡(luò)層

三者本質(zhì)上沒有可比性。 何況HTTP協(xié)議是基于TCP連接的。

TCP/IP是傳輸層協(xié)議,主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸;而HTTP是應(yīng)用層協(xié)議,主要解決如何包裝數(shù)據(jù)。

我 們?cè)趥鬏敂?shù)據(jù)時(shí),可以只使用傳輸層(TCP/IP),但是那樣的話,由于沒有應(yīng)用層,便無法識(shí)別數(shù)據(jù)內(nèi)容,如果想要使傳輸?shù)臄?shù)據(jù)有意義,則必須使用應(yīng)用層 協(xié)議,應(yīng)用層協(xié)議很多,有HTTP、FTP、TELNET等等,也可以自己定義應(yīng)用層協(xié)議。WEB使用HTTP作傳輸層協(xié)議,以封裝HTTP文本信息,然 后使用TCP/IP做傳輸層協(xié)議將它發(fā)送到網(wǎng)絡(luò)上。Socket是對(duì)TCP/IP協(xié)議的封裝,Socket本身并不是協(xié)議,而是一個(gè)調(diào)用接口(API),通過Socket,我們才能使用TCP/IP協(xié)議。

二、Http和Socket連接區(qū)別

相信不少初學(xué)手機(jī)聯(lián)網(wǎng)開發(fā)的朋友都想知道Http與Socket連接究竟有什么區(qū)別,希望通過自己的淺顯理解能對(duì)初學(xué)者有所幫助。

2.1、TCP連接

要想明白Socket連接,先要明白TCP連接。手機(jī)能夠使用聯(lián)網(wǎng)功能是因?yàn)槭謾C(jī)底層實(shí)現(xiàn)了TCP/IP協(xié)議,可以使手機(jī)終端通過無線網(wǎng)絡(luò)建立TCP連接。TCP協(xié)議可以對(duì)上層網(wǎng)絡(luò)提供接口,使上層網(wǎng)絡(luò)數(shù)據(jù)的傳輸建立在“無差別”的網(wǎng)絡(luò)之上。

建立起一個(gè)TCP連接需要經(jīng)過“三次握手”:

第一次握手:客戶端發(fā)送syn包(syn=j)到服務(wù)器,并進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器確認(rèn);

第二次握手:服務(wù)器收到syn包,必須確認(rèn)客戶的SYN(ack=j+1),同時(shí)自己也發(fā)送一個(gè)SYN包(syn=k),即SYN+ACK包,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài);

第三次握手:客戶端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認(rèn)包ACK(ack=k+1),此包發(fā)送完畢,客戶端和服務(wù)器進(jìn)入ESTABLISHED狀態(tài),完成三次握手。

手過程中傳送的包里不包含數(shù)據(jù),三次握手完畢后,客戶端與服務(wù)器才正式開始傳送數(shù)據(jù)。理想狀態(tài)下,TCP連接一旦建立,在通信雙方中的任何一方主動(dòng)關(guān)閉連

接之前,TCP

連接都將被一直保持下去。斷開連接時(shí)服務(wù)器和客戶端均可以主動(dòng)發(fā)起斷開TCP連接的請(qǐng)求,斷開過程需要經(jīng)過“四次握手”(過程就不細(xì)寫了,就是服務(wù)器和客

戶端交互,最終確定斷開)

2.2、HTTP連接

HTTP協(xié)議即超文本傳送協(xié)議(HypertextTransfer Protocol ),是Web聯(lián)網(wǎng)的基礎(chǔ),也是手機(jī)聯(lián)網(wǎng)常用的協(xié)議之一,HTTP協(xié)議是建立在TCP協(xié)議之上的一種應(yīng)用。

HTTP連接最顯著的特點(diǎn)是客戶端發(fā)送的每次請(qǐng)求都需要服務(wù)器回送響應(yīng),在請(qǐng)求結(jié)束后,會(huì)主動(dòng)釋放連接。從建立連接到關(guān)閉連接的過程稱為“一次連接”。

1)在HTTP 1.0中,客戶端的每次請(qǐng)求都要求建立一次單獨(dú)的連接,在處理完本次請(qǐng)求后,就自動(dòng)釋放連接。

2)在HTTP 1.1中則可以在一次連接中處理多個(gè)請(qǐng)求,并且多個(gè)請(qǐng)求可以重疊進(jìn)行,不需要等待一個(gè)請(qǐng)求結(jié)束后再發(fā)送下一個(gè)請(qǐng)求。

于HTTP在每次請(qǐng)求結(jié)束后都會(huì)主動(dòng)釋放連接,因此HTTP連接是一種“短連接”,要保持客戶端程序的在線狀態(tài),需要不斷地向服務(wù)器發(fā)起連接請(qǐng)求。通常的

做法是即時(shí)不需要獲得任何數(shù)據(jù),客戶端也保持每隔一段固定的時(shí)間向服務(wù)器發(fā)送一次“保持連接”的請(qǐng)求,服務(wù)器在收到該請(qǐng)求后對(duì)客戶端進(jìn)行回復(fù),表明知道客

戶端“在線”。若服務(wù)器長時(shí)間無法收到客戶端的請(qǐng)求,則認(rèn)為客戶端“下線”,若客戶端長時(shí)間無法收到服務(wù)器的回復(fù),則認(rèn)為網(wǎng)絡(luò)已經(jīng)斷開。

三、SOCKET原理

3.1、套接字(socket)概念

套接字(socket)是通信的基石,是支持TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元。它是網(wǎng)絡(luò)通信過程中端點(diǎn)的抽象表示,包含進(jìn)行網(wǎng)絡(luò)通信必須的五種信息:連接使用的協(xié)議,本地主機(jī)的IP地址,本地進(jìn)程的協(xié)議端口,遠(yuǎn)地主機(jī)的IP地址,遠(yuǎn)地進(jìn)程的協(xié)議端口。

應(yīng)

用層通過傳輸層進(jìn)行數(shù)據(jù)通信時(shí),TCP會(huì)遇到同時(shí)為多個(gè)應(yīng)用程序進(jìn)程提供并發(fā)服務(wù)的問題。多個(gè)TCP連接或多個(gè)應(yīng)用程序進(jìn)程可能需要通過同一個(gè)

TCP協(xié)議端口傳輸數(shù)據(jù)。為了區(qū)別不同的應(yīng)用程序進(jìn)程和連接,許多計(jì)算機(jī)操作系統(tǒng)為應(yīng)用程序與TCP/IP協(xié)議交互提供了套接字(Socket)接口。應(yīng)

用層可以和傳輸層通過Socket接口,區(qū)分來自不同應(yīng)用程序進(jìn)程或網(wǎng)絡(luò)連接的通信,實(shí)現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。

3.2 、建立socket連接

建立Socket連接至少需要一對(duì)套接字,其中一個(gè)運(yùn)行于客戶端,稱為ClientSocket,另一個(gè)運(yùn)行于服務(wù)器端,稱為ServerSocket。

套接字之間的連接過程分為三個(gè)步驟:服務(wù)器監(jiān)聽,客戶端請(qǐng)求,連接確認(rèn)。

服務(wù)器監(jiān)聽:服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài),實(shí)時(shí)監(jiān)控網(wǎng)絡(luò)狀態(tài),等待客戶端的連接請(qǐng)求。

客戶端請(qǐng)求:指客戶端的套接字提出連接請(qǐng)求,要連接的目標(biāo)是服務(wù)器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務(wù)器的套接字,指出服務(wù)器端套接字的地址和端口號(hào),然后就向服務(wù)器端套接字提出連接請(qǐng)求。

接確認(rèn):當(dāng)服務(wù)器端套接字監(jiān)聽到或者說接收到客戶端套接字的連接請(qǐng)求時(shí),就響應(yīng)客戶端套接字的請(qǐng)求,建立一個(gè)新的線程,把服務(wù)器端套接字的描述發(fā)給客戶

端,一旦客戶端確認(rèn)了此描述,雙方就正式建立連接。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽狀態(tài),繼續(xù)接收其他客戶端套接字的連接請(qǐng)求。

3.3、SOCKET連接與TCP連接

創(chuàng)建Socket連接時(shí),可以指定使用的傳輸層協(xié)議,Socket可以支持不同的傳輸層協(xié)議(TCP或UDP),當(dāng)使用TCP協(xié)議進(jìn)行連接時(shí),該Socket連接就是一個(gè)TCP連接。

3.4、Socket連接與HTTP連接

于通常情況下Socket連接就是TCP連接,因此Socket連接一旦建立,通信雙方即可開始相互發(fā)送數(shù)據(jù)內(nèi)容,直到雙方連接斷開。但在實(shí)際網(wǎng)絡(luò)應(yīng)用

中,客戶端到服務(wù)器之間的通信往往需要穿越多個(gè)中間節(jié)點(diǎn),例如路由器、網(wǎng)關(guān)、防火墻等,大部分防火墻默認(rèn)會(huì)關(guān)閉長時(shí)間處于非活躍狀態(tài)的連接而導(dǎo)致

Socket 連接斷連,因此需要通過輪詢告訴網(wǎng)絡(luò),該連接處于活躍狀態(tài)。

而HTTP連接使用的是“請(qǐng)求—響應(yīng)”的方式,不僅在請(qǐng)求時(shí)需要先建立連接,而且需要客戶端向服務(wù)器發(fā)出請(qǐng)求后,服務(wù)器端才能回復(fù)數(shù)據(jù)。

多情況下,需要服務(wù)器端主動(dòng)向客戶端推送數(shù)據(jù),保持客戶端與服務(wù)器數(shù)據(jù)的實(shí)時(shí)與同步。此時(shí)若雙方建立的是Socket連接,服務(wù)器就可以直接將數(shù)據(jù)傳送給

客戶端;若雙方建立的是HTTP連接,則服務(wù)器需要等到客戶端發(fā)送一次請(qǐng)求后才能將數(shù)據(jù)傳回給客戶端,因此,客戶端定時(shí)向服務(wù)器端發(fā)送連接請(qǐng)求,不僅可以

保持在線,同時(shí)也是在“詢問”服務(wù)器是否有新的數(shù)據(jù),如果有就將數(shù)據(jù)傳給客戶端。

這里我們使用Socket實(shí)現(xiàn)一個(gè)聊天室的功能,關(guān)于服務(wù)器這里的就不介紹了

@interfaceViewController (){

NSInputStream *_inputStream;//對(duì)應(yīng)輸入流

NSOutputStream *_outputStream;//對(duì)應(yīng)輸出流

}

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputViewConstraint;

@property (weak, nonatomic) IBOutlet UITableView *tableView;

@property (nonatomic, strong) NSMutableArray *chatMsgs;//聊天消息數(shù)組

@end

懶加載這個(gè)消息數(shù)組

//從主運(yùn)行循環(huán)移除

//1.建立連接

//定義C語言輸入輸出流

//把C語言的輸入輸出流轉(zhuǎn)化成OC對(duì)象

//設(shè)置代理

//把輸入輸入流添加到主運(yùn)行循環(huán)

//不添加主運(yùn)行循環(huán) 代理有可能不工作

//打開輸入輸出流

//登錄

//發(fā)送用戶名和密碼

//在這里做的時(shí)候,只發(fā)用戶名,密碼就不用發(fā)送

//如果要登錄,發(fā)送的數(shù)據(jù)格式為 "iam:zhangsan";

//如果要發(fā)送聊天消息,數(shù)據(jù)格式為 "msg:did you have dinner";

//登錄的指令11NSString *loginStr =@"iam:zhangsan";

//把Str轉(zhuǎn)成NSData

//建立一個(gè)緩沖區(qū) 可以放1024個(gè)字節(jié)

//返回實(shí)際裝的字節(jié)數(shù)

//把字節(jié)數(shù)組轉(zhuǎn)化成字符串

//從服務(wù)器接收到的數(shù)據(jù)

//聊天信息

//刷新表格

//發(fā)送數(shù)據(jù)

//發(fā)送完數(shù)據(jù),清空textField

//數(shù)據(jù)多,應(yīng)該往上滾動(dòng)

}

//監(jiān)聽鍵盤

//獲取窗口的高度

//鍵盤結(jié)束的Frm

//獲取鍵盤結(jié)束的y值

IOS 基于TCP的socket通信詳解

最近在整理通信層相關(guān)知識(shí),這篇文章是邊整理邊寫的,有些地方可能不夠準(zhǔn)確,還請(qǐng)各位路過的大牛專家指出來。這次整理的socket通信是基于TCP的,實(shí)現(xiàn)方式是GCD形式,以下記錄的都是些理論知識(shí),方便自己回憶。

1、socket通信原理:現(xiàn)網(wǎng)絡(luò)上有很多socket開園框架文件,基本上能滿足簡單網(wǎng)絡(luò)通信,但如過你的項(xiàng)目需要成熟的網(wǎng)絡(luò)通信,還需要自 己對(duì)socket好好研究完善下。socket通信分為server端和client端,開發(fā)過程中分別對(duì)應(yīng)著服務(wù)器和客戶端。當(dāng)連接上服務(wù)器 后,socket就會(huì)啟動(dòng)一個(gè)while或for無限循環(huán),不斷的異步監(jiān)聽socket動(dòng)靜,看是否有read或者write動(dòng)作,直到出錯(cuò)或者主動(dòng)結(jié) 束。

socket通信傳輸?shù)亩际亲止?jié)流,傳輸時(shí)沒有包的概念的。至于我們經(jīng)常說的包的概念,是我們制定了一定的規(guī)則形成的。常用規(guī)則有兩種,一 種是制定特定的分隔符(delimiter)來分割每個(gè)包,此時(shí)必須保證每條信息體中不包含該分隔符。第二種是指定每條消息體的長度,比如,在每條消息體 之前插入一個(gè)4字節(jié)長度的無符號(hào)整形來表明后面的消息體長度(一般包體長度限制在8k大小內(nèi),遇到消息體過大的信息時(shí),一般采用分包發(fā)送和拼包解析)。其 中第二種方式更常見一些。

基于TCP鏈接的socket通信中,經(jīng)常會(huì)涉及粘包、分包、解包的問題,一下久這問題簡單說一下。

粘包:使用TCP長連接就會(huì)引入粘包的問題,粘包是指發(fā)送方發(fā)送的若干包數(shù)據(jù)到接收方接收時(shí)粘成一包,從接收緩沖區(qū)看,后一包數(shù)據(jù)的頭緊接 著前一包數(shù)據(jù) 的尾。粘包可能由發(fā)送方造成,也可能由接收方造成。TCP為提高傳輸效率,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一包數(shù)據(jù),造成多個(gè)數(shù)據(jù)包的粘連。如果 接收進(jìn)程不及時(shí)接收數(shù)據(jù),已收到的數(shù)據(jù)就放在系統(tǒng)接收緩沖區(qū),用戶進(jìn)程讀取數(shù)據(jù)時(shí)就可能同時(shí)讀到多個(gè)數(shù)據(jù)包。

粘包一般的解決辦法是制定通訊協(xié)議,由協(xié)議來規(guī)定如何分包解包。

分包:常用分包的邏輯是先發(fā)一個(gè)長度,然后緊接著是數(shù)據(jù)包內(nèi)容,這樣就可以把每個(gè)包分開。

解包:由于我們應(yīng)用層數(shù)據(jù)包既可以傳命令也可以傳數(shù)據(jù),因而針對(duì)每個(gè)包我們進(jìn)行解包,分出命令和數(shù)據(jù)分別處理,因而每個(gè)Socket服務(wù)對(duì)象都需要解包。解包過程中,才會(huì)出現(xiàn)我們常見的包頭、信息體長度、信息體。

粘包、分包、解包解決方案:東家DZH:采用的是用一個(gè)足夠大(500*1024)的接受緩存pRecvBuf去讀區(qū)socket(recv()),然后使用君子協(xié)定讓數(shù)據(jù)報(bào)的開頭都是一個(gè)標(biāo)準(zhǔn)的包頭信息,包頭信息中包含著本包的包體長度,利用這個(gè)包體長度曲讀取本包數(shù)據(jù)包體。讀完后,如果該數(shù)據(jù)包還有未讀數(shù)據(jù),則重復(fù)上訴解包操作。直到完成。

東家EM:每次收到數(shù)據(jù)報(bào)時(shí),先讀取前4個(gè)字節(jié)轉(zhuǎn)為無符號(hào)整形作為本消息包的長度length,然后一直重socket中read,直到讀取length長度為之。這樣可以解決分包發(fā)送問題,也可以解決粘包問題。

iOS開發(fā)之Socket實(shí)現(xiàn)HTTPS GET請(qǐng)求通過Body傳參

Cocoa框架里,無論是用OS層基于 C 的BSD socket還是用對(duì)BSD socket進(jìn)行了輕量級(jí)的封裝的CFNetwork,對(duì)于我這種C語言不及格的同學(xué),那都是極其痛苦的體驗(yàn),因此我們就用CocoaAsyncSocket來進(jìn)行socket連接,完全OC風(fēng)格,非常愉快。

.h文件里沒有什么內(nèi)容只是暴露了一個(gè)供外界調(diào)用的請(qǐng)求接口,后面介紹,主要是.m文件里的擴(kuò)展屬性:

GCDAsyncSocketDelegate代理的實(shí)現(xiàn):

這里重點(diǎn)說一下sendData這個(gè)屬性的拼接(很重要,這里的格式?jīng)Q定了你發(fā)送的請(qǐng)求數(shù)據(jù)是否被服務(wù)器認(rèn)可,并給你返回信息,相當(dāng)于NSURLRequest的作用,其實(shí)就是拼接一個(gè)http協(xié)議):

以上就是建立HTTP連接收發(fā)數(shù)據(jù)的全部內(nèi)容,如果不需要支持https的話,這個(gè)GET請(qǐng)求已經(jīng)可以完成,下面介紹進(jìn)行https連接需要進(jìn)行的設(shè)置(在.m文件里實(shí)現(xiàn)):(上面提到的[self doTLSConnect:sock]這個(gè)方法)

至此發(fā)送HTTPS GET請(qǐng)求并且用body傳遞參數(shù)就實(shí)現(xiàn)了,是不是很神奇。下面封裝一個(gè)對(duì)外調(diào)用的接口(在.h文件中把這個(gè)接口暴露出去就行了):

** 其中的DCNetCache類用來暫存網(wǎng)絡(luò)請(qǐng)求的參數(shù),它是這樣子滴:**

這樣子就大功告成了,注意把上面的host換成自己的,這里還有許多不完善的地方,我只是實(shí)現(xiàn)了簡單的GET請(qǐng)求并暫存請(qǐng)求參數(shù),至于你需要其他的功能自己加上就是了。

附一篇講GCDAsyncSocket的干貨文章,非常值得一讀

當(dāng)前文章:iossocket開發(fā),iOSsocket
標(biāo)題網(wǎng)址:http://chinadenli.net/article9/dsihdih.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT微信公眾號(hào)標(biāo)簽優(yōu)化企業(yè)網(wǎng)站制作網(wǎng)站策劃商城網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)