1.相關理論
socket:顧名思義接口,套接字,他不是協議是而是一個抽象層,位置在服務層下與傳輸層之上的中間。socket主要解決計算機系統多個程序佔用同一個端口問題。當多個程序公用一個80端口時,當有數據傳輸是不知道具體哪個程序接受數據的難題。如果說tcp/udp定義了數據怎麼傳輸,那麼socket就是具體執行者。socekt定義了點到點雙向傳輸機制。他是成對出現 點a一個socket 點b一個socekt.
2.使用socket前提
2.1)協議 兩端定義 比如TCP/UDP
2.2)點A端口號
2.3. 點A的IP地址
2.4) 點B端口號
2.5) 點B的IP地址
3.ios端socket類型
iOS端socket框架中有以下幾種類型的socekt 層次有高向低
1.CFNetWork
CFNetWrok是ios中比較底層的網絡框架,c語言編寫,可以控制更底層的東西,如常用的網絡協議和socket通訊等,CFNetWork已經近於UNIX的socket通信。是CoreFoundation框架對BDSsocket的封裝。但他本質上還是應用層上的封裝。
2.CFSocket
CFSocket是在系統CFNetWork.framework框架中,他是基於伯克利進行抽象和封裝,CFSocket中包含了少數開銷,幾乎可以提供伯克利的一切功能,並把socket集成進一個少數循環當中。CFsocket不僅限於流的socket.他可以處理任意類型的socket.
3.BSDsocket
伯克利套接字,純c編寫進程間通信庫,可以跨平臺。他允許不同主機或同一計算機上不同進程之間的通信,支持多種I/O設備和驅動。UNIX最底層的socket就是基於BSDSocekt的,蘋果系統底層便是基於UNIX.
- 三種類型socekt相關函數
需要導入框架
#import <sys/socket.h> 核心函數庫
#import <netinet/in.h> //AF_INET和AF_INET6地址家族和他們對應的協議家族PF_INET和PF_INET6.在互聯網變成中廣發使用,包含ip地址以及tcp/udp端口號。//<sys/un.h>PF_UNIX/PF_LOCAL地址家族,用於運行在一臺計算機上的程序間的本地通信,不用在網絡中
#import <arpa/inet.h> //和ip地址相關的一些函數
<netdb.h>//把協議名和主機名轉化爲數字的一些函數
#import <ifaddrs.h>
#import <CoreFoundation/CoreFoundation.h>
1.CFNetWork
CFNetwork結構:CFFTP,CFHTSTPAuthentication,CFHTTP,CFSocketStream
(CFStream,CFSocket)Core Foundation
CFFTP:對用FTP協議通信的封裝,能下載,上傳文件和目錄到FTP服務器,
CFFHTTP:是對HTTP協議的抽象,主要對象是CFHTTPMessageRef(類似於NSURLRequest)我們構建CFHTTPMessageRef同樣包含幾個元素
必須元素:請求方法(類型CFStringRef):POST,GET,DELETE等
請求URL地址(類型CFURLRef):http://www.baidu.com
請求的HTTP版本(類型CFStringRef):通常使用kCFHTTPVersion1_1
kCFAllocatorDefault用於創建消息引用的指定默認系統內存分配起
可選參數
body體(類型CFDataRef)
消息頭部,如User-Agent等
demo
-(void)CFNetwok{
NSURL *url = [NSURL URLWithString:@"url"];
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(loadDataFormServerWithURL:) object:url];
[thread start];
NSString *host = url.host;
long long port = [[url port] longLongValue];
//流上下文
CFStreamClientContext ctx = {0,(__bridge void *)(self),NULL,NULL,NULL};
CFOptionFlags registeredEvents = (kCFStreamEventHasBytesAvailable|kCFStreamEventEndEncountered|kCFStreamEventErrorOccurred);
CFReadStreamRef readStream;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)host, (UInt32)port, &readStream,NULL);
if (CFReadStreamSetClient(readStream, registeredEvents,socketCallback,&ctx)) {
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
}else{
// [self netwokFailedWithErrorMessage:@"fffffff"];
return;
}
if (CFReadStreamOpen(readStream) == NO) {
return;
}
CFErrorRef error =CFReadStreamCopyError(readStream);
if (CFErrorGetCode(error) != 0) {
NSString *error = [NSString stringWithFormat:@"errorcode %@",error];
}
CFRelease(error);
return;
CFRunLoopRun();
}
回調函數
void socketCallback(CFReadStreamRef stream,CFStreamEventType event,void *myPtr){
ViewController *controller = (__bridge ViewController *)myPtr;
switch (event) {
case kCFStreamEventHasBytesAvailable:{
while (CFReadStreamHasBytesAvailable(stream)) {
CFIndex bufferSize = 0;
UInt8 buffer[bufferSize];
//int numBytesRead = CFReadStreamRead(stream, buffer, kBuffsrSize);
long long numBytesRead = CFReadStreamRead(stream, buffer, bufferSize);
// [controller didReceiveData:[NSData dataWithBytes:buffer length:numBytesRead]];
NSLog(@"%@",[NSData dataWithBytes:buffer length:numBytesRead]);
}
break;
}
default:
break;
}
}
2.CFSocket
-(void)CFsocket{
//創建socket 參數一:版本號必須0,參數二:info 一個指向任意程序定義數據的指針,
CFSocketContext socketContext = {0,(__bridge void *)(self),NULL,NULL,NULL};
CFSocketRef socketRef = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCF, kCFSocketConnectCallBack, ServerConnectCallBack, &socketContext);
struct sockaddr_in Socketaddr;
//memst:將addr中所有字節用0替代並返回addr,作用是一段內存塊中填充某個給定的值,他是對較大結構題或數組進行清零操作的一種最快方式
memset(&Socketaddr, 0, sizeof(Socketaddr));
//長度
Socketaddr.sin_len = sizeof(Socketaddr);
//協議族 用AF_INET-互聯網,tcp/udp等
Socketaddr.sin_family = AF_INET;
//d端口號(使用網絡字節順序)
Socketaddr.sin_port = htons(111);//@"這個端口隨便寫"
//存儲ip地址。 inet_addr()的功能是將一點分十進制ipd地址轉換成一個長整型數
Socketaddr.sin_addr.s_addr = inet_addr("192.168.103.244");//這個時ip地址,不能隨便亂寫
//將地址轉化爲CFDataRef
CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&Socketaddr, sizeof(Socketaddr));
//連接
//參數一:連接的socket,參數二:連接的socket包含的地址參數,參數三:連接超時時間,如果負,則不嘗試連接而是連接放在後臺,如果socket消息類型爲kCFSocketConnectCallBack,將會在連接成功或失敗的時候在後臺觸發回調函數
CFSocketConnectToAddress(socketRef, dataRef, -1);
//加入循環中 獲取當前線程的runloop
CFRunLoopRef runloopRef = CFRunLoopGetCurrent();
//把socket包裝成CFRunloopSource 最後一個參數是指有多個runloopsource通過一個unloop把順勢順序,
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socketRef, 0);
//加入循環 參數1:運行循環管,參數2:增加的運行循環源,他會被retain一次,參數三:用什麼模式把source加入到runloop裏面
CFRunLoopAddSource(runloopRef, sourceRef, kCFRunLoopCommonModes);
//之前倍retain一次,這裏釋放掉
CFRelease(sourceRef);
//發送數據
const char *data = [@"abcdefg" UTF8String];
//成功則返回實際傳送出去的字符數,失敗返回-1
long sendData = send(CFSocketGetNative(socketRef), data, strlen(data) + 1, 0);
NSLog(@"%ld",sendData);
}
回調函數
void ServerConnectCallBack(CFSocketRef s, CFSocketCallBackType callbackType,CFDataRef address,const void *data,void *info){
ViewController *vc = (__bridge ViewController *)(info);
//判斷是不是NULL
if (data !=NULL) {
printf("連接失敗");
[vc performSelector:@selector(_releseSocket) withObject:nil];
}else{
printf("連接成功");
[vc performSelector:@selector(_readStreamData) withObject:nil];
}
}
//清空socket
-(void)_releseSocket{
CFSocketRef socektRef = NULL;//這句假的要把上面的socketRef設置成全局變量
if (socektRef) {
CFRelease(socektRef);
}
socektRef = NULL;
}
//連接成功 讀取數據
-(void)_readStreamData{
CFSocketRef socektRef = NULL;//這句假的要把上面的socketRef設置成全局變量
//接受數據
char buffer[512];
long readData;
while ((readData = recv(CFSocketGetNative(socektRef), buffer, sizeof(buffer), 0))) {
NSString *conet = [[NSString alloc]initWithBytes:buffer length:readData encoding:NSUTF8StringEncoding];
NSLog(@"%@",conet);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"aaaa");
});
}
}
3.BSDSocket伯克利套接字
相關函數
socket(PF_INET,SOCK_STREAM,0);函數原型
函數講解
socket(確定協議族如PF_INET是ipv4 PF_INET6 是ipv6 PF_UNIX是本地,SOCK_STREAM(可靠面向連接的服務流)/SOCK_DGRAM(數據包服務)/SOCK_SEQPACKET(可靠的有序的分組服務)/SOCK_RAW(網絡層原始協議),IPPROTO_TCP/SCTP/UDP/DCCP確定使用的運輸層,用0可以默認一個協議有系統選擇) 創造某種類型的套接字,分配一些系統資源,用返回的整數識別
connect() 用在客戶端,給套接字分配一個空閒的端口號,比如一個tcp套接字
send() and recv(),or write() and read(), or sendto() and recvfrom() 用來接送和發送數據
close()關閉連接,系統釋放資源
gethostbyname() and gethostbyaddr()用來解析主機名和地址
gethostbyname(指定主機名如www.baidu.com)
gethostbyaddr(主機地址,len給出addr的長度以字節爲單位,指定地址類型比如AF_INET)
bind() 一般永在服務器,和一個套接字地址結構相連,比如說一個特定的本地端口號和一個ip地址
liosten()永在服務器,導致一個綁定的TCP套接字進入監聽狀態
accept() 永在服務器,從客戶端那接受請求試圖創造一個新的tcp連接
demo
-(void)BSDSocket{
//創建
//參數一:ip協議類型AF_INET指IPV4,參數二:socket的類型,一半字節流stream(SOCK_STREAM)h或數據報文(SOCK_DGRAM,參數三:協議 一般填0 系統自動選取最佳協議)
//tcp
int clientSocket = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in stSockAddr;
int Res;
if (clientSocket>0) {
NSLog(@"socke創建成功 %d",clientSocket);
}else{
NSLog(@"創建失敗");
}
memset(&stSockAddr, 0, sizeof(struct sockaddr_in));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(1100);
Res = inet_pton(AF_INET, "192.168.1.7", &stSockAddr.sin_addr);
if (0>Res) {
NSLog(@"連接失敗");
//關閉
close(clientSocket);
exit(EXIT_FAILURE);
}else if (0==Res){
close(clientSocket);
exit(EXIT_FAILURE);
}
if (-1 == connect(clientSocket, (const struct sockaddr *)&stSockAddr, sizeof(struct sockaddr_in))) {
close(clientSocket);
exit(EXIT_FAILURE);
}
//udp
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in sa;
char buffer[1024];
ssize_t recsize;
socklen_t fromlen;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(7654);
if (-1 == bind(sock,(struct sockaddr *)&sa, sizeof(struct sockaddr)))
{
perror("error bind failed");
close(sock);
exit(EXIT_FAILURE);
}
for (;;)
{
printf ("recv test....\n");
recsize = recvfrom(sock, (void *)buffer, 1024, 0, (struct sockaddr *)&sa, &fromlen);
if (recsize < 0)
fprintf(stderr, "%s\n", strerror(errno));
printf("recsize: %zd\n ",recsize);
sleep(1);
printf("datagram: %s\n",buffer);
}
}
5.socket相關第三方庫
cocoAsyncSocket(兩個類GCDAsyncSocekt/GCDAsyncUdpSocket)
WebSocket(Socket.IO,SocketRocket)三端可以雙全工socket.主要用於web端。也可以用於ios端。