socket通信

寫在前面的話

關(guān)于socket的通信基本知識(shí)這里不多贅述概耻,有興趣自行百度封恰。本文重點(diǎn)講解socket中使用到的結(jié)構(gòu)體及其參數(shù)意義。
  本文也不講解具體使用教程举塔,網(wǎng)上一搜一堆,提供筆者參考的一篇實(shí)現(xiàn)iOS socket通信求泰。
  本人是一名iOS工程師央渣,參考的均為OC語(yǔ)言和C語(yǔ)言中及l(fā)inux系統(tǒng)中的相關(guān)定義,在其他語(yǔ)言或操作平臺(tái)中或有出入拜秧。

socket中用到的頭文件

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

??iOS審核要求必須支持ipv6,而ipv6的頭文件是<netinet6/in6.h>,在<netinet/in.h>的最后有相關(guān)定義如下

/* INET6 stuff */
#define __KAME_NETINET_IN_H_INCLUDED_
#include <netinet6/in6.h>
#undef __KAME_NETINET_IN_H_INCLUDED_

其中核心<sys/socket.h>提供了創(chuàng)建痹屹,綁定,連接枉氮,監(jiān)聽(tīng)志衍,斷開(kāi),發(fā)消息等常用函數(shù)及基本數(shù)據(jù)結(jié)構(gòu)聊替,之后將一一介紹楼肪。
  <netinet/in.h>提供socket地址數(shù)據(jù)結(jié)構(gòu)sockaddr_in的相關(guān)定義。
  <arpa/inet.h>提供IP地址轉(zhuǎn)換函數(shù)

常用數(shù)據(jù)結(jié)構(gòu)解析

1惹悄、sockaddr
/*
 * [XSI] Structure used by kernel to store most addresses.
 */
struct sockaddr {
    __uint8_t   sa_len;     /* total length */
    sa_family_t sa_family;  /* [XSI] address family */
    char        sa_data[14];    /* [XSI] addr value (actually larger) */
};

該結(jié)構(gòu)體用于存儲(chǔ)地址結(jié)構(gòu)春叫。來(lái)自<sys/socket.h>
  sa_len表示地址的長(zhǎng)度泣港。
  sa_family表示地址族常用的族有ipv4的AF_INET和ipv6的AF_INET6暂殖。
  sa_data[14]表示地址數(shù)據(jù)。

2当纱、sockaddr_in
/*
 * Socket address, internet style.
 */
struct sockaddr_in {
    __uint8_t   sin_len;
    sa_family_t sin_family;
    in_port_t   sin_port;
    struct  in_addr sin_addr;
    char        sin_zero[8];
};

該結(jié)構(gòu)體是對(duì)sockaddr的擴(kuò)展呛每。來(lái)自<netinet/in.h>
  sin_len表示長(zhǎng)度坡氯。
  sin_family表示地址族晨横,sin_port表示端口號(hào)洋腮,sin_addr表示ip地址。
  sin_zero[8]沒(méi)有實(shí)際意義,只是為了跟sockaddr結(jié)構(gòu)在內(nèi)存中對(duì)齊手形。

3啥供、in_addr
/*
 * Internet address (a structure for historical reasons)
 */
struct in_addr {
    in_addr_t s_addr;
};

該結(jié)構(gòu)體用來(lái)表示一個(gè)32位的IPv4地址。來(lái)自<netinet/in.h>库糠。

常用函數(shù)解析

1伙狐、初始化函數(shù)socket()
int socket( int af, int type, int protocol);

該函數(shù)來(lái)自<sys/socket.h>
  af:一個(gè)地址描述曼玩。支持AF_INET鳞骤、AF_INET6等格式。
  type:指定socket類型黍判。新套接口的類型描述類型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)篙梢。常用的socket類型有顷帖,SOCK_STREAMSOCK_DGRAM渤滞、SOCK_RAW贬墩、SOCK_PACKETSOCK_SEQPACKET等等妄呕。
  protocol:顧名思義陶舞,就是指定協(xié)議。套接口所用的協(xié)議绪励。如調(diào)用者不想指定肿孵,可用0。常用的協(xié)議有疏魏,IPPROTO_TCP停做、IPPROTO_UDPIPPROTO_STCP大莫、IPPROTO_TIPC等蛉腌,它們分別對(duì)應(yīng)TCP傳輸協(xié)議、UDP傳輸協(xié)議只厘、STCP傳輸協(xié)議烙丛、TIPC傳輸協(xié)議。
當(dāng)返回結(jié)果為-1時(shí)表示創(chuàng)建失敗羔味。

2河咽、htons()
#define htons(x)    __DARWIN_OSSwapInt16(x)
#define __DARWIN_OSSwapInt16(x) _OSSwapInt16(x)
/* Generic byte swapping functions. */
OS_INLINE
uint16_t
_OSSwapInt16(
    uint16_t        data
)
{
  /* Reduces to 'rev16' with clang */
  return (uint16_t)(data << 8 | data >> 8);
}

該函數(shù)來(lái)自<sys/_endian.h>
  該函數(shù)將一個(gè)16位數(shù)從主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序介评。將高低位互換位置库北。參數(shù)為端口號(hào)爬舰。

3、inet_addr()
in_addr_t    inet_addr(const char *);

該函數(shù)來(lái)自<arpa/inet.h>寒瓦。
  inet_addr()的功能是將一個(gè)點(diǎn)分十進(jìn)制的IP轉(zhuǎn)換成一個(gè)長(zhǎng)整數(shù)型數(shù)情屹。參數(shù)為IP地址。

4杂腰、連接函數(shù)connect()
int connect(int s, const struct sockaddr * name, int namelen);

該函數(shù)來(lái)自<sys/socket.h>垃你。
  s:標(biāo)識(shí)一個(gè)未連接socket。
  name:指向要連接套接字的sockaddr結(jié)構(gòu)體的指針喂很。
  namelen:sockaddr結(jié)構(gòu)體的字節(jié)長(zhǎng)度惜颇。
  返回值為-1表示連接失敗。

5少辣、接收函數(shù)recv()
ssize_t recv( int s, void *buf, _size_t len, int flags);

該函數(shù)來(lái)自<sys/socket.h>凌摄。
  s:標(biāo)識(shí)一個(gè)已連接socket。
  buf:接收到的消息的存儲(chǔ)位置漓帅。注意大小锨亏。
  len:接收消息的長(zhǎng)度。
  當(dāng)返回值小于0時(shí)表示錯(cuò)誤忙干,當(dāng)等于0時(shí)表示對(duì)端的socket已正常關(guān)閉器予。

6、發(fā)送函數(shù)send()
ssize_t send(int s, const void * buf, size_t len, int √)

該函數(shù)來(lái)自<sys/socket.h>捐迫。
  s:標(biāo)識(shí)一個(gè)已連接socket乾翔。
  buf:發(fā)送的消息的存儲(chǔ)位置。注意大小施戴。
  len:發(fā)送消息的長(zhǎng)度反浓。[1]

當(dāng)返回值為-1時(shí)表示錯(cuò)誤。

7暇韧、綁定函數(shù)bind()
int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen)

該函數(shù)來(lái)自<sys/socket.h>勾习。
  sockfd:標(biāo)識(shí)一未捆綁套接口的描述符。
  my_addr:賦予套接口的地址懈玻。
  addrlen:my_addr結(jié)構(gòu)的長(zhǎng)度巧婶。
  當(dāng)返回值為-1時(shí)表示錯(cuò)誤。

8涂乌、監(jiān)聽(tīng)函數(shù)listen()
int listen( int sockfd, int backlog)

該函數(shù)來(lái)自<sys/socket.h>艺栈。
  sockfd:用于標(biāo)識(shí)一個(gè)已捆綁未連接套接口的描述符。
  backlog:等待連接隊(duì)列的最大長(zhǎng)度湾盒。

9湿右、接受連接accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

該函數(shù)來(lái)自<sys/socket.h>
  sockfd:套接字描述符罚勾,該套接口在listen()后監(jiān)聽(tīng)連接毅人。
  addr:(可選)指針吭狡,指向一緩沖區(qū),其中接收為通訊層所知的連接實(shí)體的地址丈莺。Addr參數(shù)的實(shí)際格式由套接口創(chuàng)建時(shí)所產(chǎn)生的地址族確定划煮。
  addrlen:(可選)指針,輸入?yún)?shù)缔俄,配合addr一起使用弛秋,指向存有addr地址長(zhǎng)度的整型數(shù)。

10俐载、關(guān)閉連接close()
int  close(int sockfd)

該函數(shù)來(lái)自unistd.h
  sockfd:套接字描述符蟹略。

ipv4與ipv6

由于iOS在審核時(shí)必須通過(guò)ipv6環(huán)境的測(cè)試,而通常我們使用的都是ipv4的地址遏佣,同時(shí)sockaddr_in只能表示ipv4環(huán)境的結(jié)構(gòu)挖炬。因此引入了sockaddr_in6

struct sockaddr_in6 {
    __uint8_t   sin6_len;   /* length of this struct(sa_family_t) */
    sa_family_t sin6_family;    /* AF_INET6 (sa_family_t) */
    in_port_t   sin6_port;  /* Transport layer port # (in_port_t) */
    __uint32_t  sin6_flowinfo;  /* IP6 flow information */
    struct in6_addr sin6_addr;  /* IP6 address */
    __uint32_t  sin6_scope_id;  /* scope zone index */
};

基本結(jié)構(gòu)和sockaddr_in類似,只是參數(shù)名上多了個(gè)6状婶。
  這里存在一個(gè)地址轉(zhuǎn)換茅茂,研究了GCDAsyncSocket的轉(zhuǎn)換方法。這里也建議想省事的同學(xué)直接使用該第三方庫(kù)太抓。

NSMutableArray *addresses = nil;
NSError *error = nil;

NSString *portStr = [NSString stringWithFormat:@"%hu", port];

struct addrinfo hints, *res, *res0;
        
memset(&hints, 0, sizeof(hints));
hints.ai_family   = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
        
int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
        
if (gai_error) {
    error = [self gaiError:gai_error];
} else {
    NSUInteger capacity = 0;
    for (res = res0; res; res = res->ai_next) {
        if (res->ai_family == AF_INET || res->ai_family == AF_INET6) {
            capacity++;
        }
    }
            
    addresses = [NSMutableArray arrayWithCapacity:capacity];
            
    for (res = res0; res; res = res->ai_next) {
        if (res->ai_family == AF_INET) {
            // Found IPv4 address.
            // Wrap the native address structure, and add to results.
                    
            NSData *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
            [addresses addObject:address4];
        } else if (res->ai_family == AF_INET6) {
            // Fixes connection issues with IPv6
            // https://github.com/robbiehanson/CocoaAsyncSocket/issues/429#issuecomment-222477158
                    
            // Found IPv6 address.
            // Wrap the native address structure, and add to results.
                    
            struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)res->ai_addr;
            in_port_t *portPtr = &sockaddr->sin6_port;
            if ((portPtr != NULL) && (*portPtr == 0)) {
                    *portPtr = htons(port);
            }

            NSData *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
            [addresses addObject:address6];
        }
    }
    freeaddrinfo(res0);
            
    if ([addresses count] == 0) {
        error = [self gaiError:EAI_FAIL];
    }
}

這里面主要是addrinfo結(jié)構(gòu)和getaddrinfo()函數(shù)。

addrinfo
struct addrinfo {
    int ai_flags;   /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
    int ai_family;  /* PF_xxx */
    int ai_socktype;    /* SOCK_xxx */
    int ai_protocol;    /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
    socklen_t ai_addrlen;   /* length of ai_addr */
    char    *ai_canonname;  /* canonical name for hostname */
    struct  sockaddr *ai_addr;  /* binary address */
    struct  addrinfo *ai_next;  /* next structure in linked list */
};

ai_family:指定了地址族令杈,可取值如下:

名稱 意義
AF_INET 2 ipv4
AF_INET6 23 ipv6
AF_UNSPEC 0 協(xié)議無(wú)關(guān)

ai_socktype:指定我套接字的類型

名稱 意義
SOCK_STREAM 1
SOCK_DGRAM 2 數(shù)據(jù)報(bào)

在AF_INET通信域中套接字類型SOCK_STREAM的默認(rèn)協(xié)議是TCP(傳輸控制協(xié)議)
  在AF_INET通信域中套接字類型SOCK_DGRAM的默認(rèn)協(xié)議是UDP(用戶數(shù)據(jù)報(bào)協(xié)議)

ai_protocol:指定協(xié)議類型走敌。可取的值取決于ai_address和ai_socktype的值
  ai_flags指定了如何來(lái)處理地址和名字逗噩。

getaddrinfo()
int  getaddrinfo(const char * __restrict, const char * __restrict, const struct addrinfo * __restrict, struct addrinfo ** __restrict);

getaddrinfo函數(shù)能夠處理名字到地址以及服務(wù)到端口這兩種轉(zhuǎn)換掉丽,返回的是一個(gè)sockaddr 結(jié)構(gòu)的鏈而 不是一個(gè)地址清單。它具有協(xié)議無(wú)關(guān)性异雁。
  hostname:一個(gè)主機(jī)名或者地址串(IPv4的點(diǎn)分十進(jìn)制串或者IPv6的16進(jìn)制串)
  service:一個(gè)服務(wù)名或者10進(jìn)制端口號(hào)數(shù)串捶障。
  hints:可以是一個(gè)空指針,也可以是一個(gè)指向某個(gè)addrinfo結(jié)構(gòu)的指針纲刀,調(diào)用者在這個(gè)結(jié)構(gòu)中填入關(guān)于期望返回的信息類型的暗示项炼。舉例來(lái)說(shuō):如果指定的服務(wù)既支持TCP也支持UDP,那么調(diào)用者可以把hints結(jié)構(gòu)中的ai_socktype成員設(shè)置成SOCK_DGRAM使得返回的僅僅是適用于數(shù)據(jù)報(bào)套接口的信息示绊。
  返回0: 成功锭部,返回非0: 出錯(cuò)。


  1. 這里的長(zhǎng)度不能多也不能少面褐,筆者在這里預(yù)留多余長(zhǎng)度后在后臺(tái)解析錯(cuò)誤拌禾,特此批注。 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末展哭,一起剝皮案震驚了整個(gè)濱河市湃窍,隨后出現(xiàn)的幾起案子闻蛀,更是在濱河造成了極大的恐慌,老刑警劉巖您市,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件觉痛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡墨坚,警方通過(guò)查閱死者的電腦和手機(jī)秧饮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)泽篮,“玉大人盗尸,你說(shuō)我怎么就攤上這事∶背牛” “怎么了泼各?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)亏拉。 經(jīng)常有香客問(wèn)我扣蜻,道長(zhǎng),這世上最難降的妖魔是什么及塘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任莽使,我火速辦了婚禮,結(jié)果婚禮上笙僚,老公的妹妹穿的比我還像新娘芳肌。我一直安慰自己,他們只是感情好肋层,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布亿笤。 她就那樣靜靜地躺著,像睡著了一般栋猖。 火紅的嫁衣襯著肌膚如雪净薛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天蒲拉,我揣著相機(jī)與錄音肃拜,去河邊找鬼。 笑死全陨,一個(gè)胖子當(dāng)著我的面吹牛爆班,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辱姨,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼柿菩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了雨涛?” 一聲冷哼從身側(cè)響起枢舶,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤懦胞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后凉泄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體躏尉,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年后众,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胀糜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒂誉,死狀恐怖教藻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情右锨,我是刑警寧澤括堤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站绍移,受9級(jí)特大地震影響悄窃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蹂窖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一轧抗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞬测,春花似錦鸦致、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)抗碰。三九已至狮斗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間弧蝇,已是汗流浹背碳褒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留看疗,地道東北人沙峻。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像两芳,于是被迫代替她去往敵國(guó)和親摔寨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • socket通信原理 socket又被叫做套接字,它就像連接到兩端的插座孔一樣,通過(guò)建立管道怖辆,將兩個(gè)不同的進(jìn)程之間...
    jiodg45閱讀 1,140評(píng)論 0 1
  • 前言 我們深諳信息交流的價(jià)值是复,那網(wǎng)絡(luò)中進(jìn)程之間如何通信删顶,如我們每天打開(kāi)瀏覽器瀏覽網(wǎng)頁(yè)時(shí),瀏覽器的進(jìn)程怎么與web服...
    Chars閱讀 2,983評(píng)論 2 117
  • 近期在做的項(xiàng)目中淑廊,涉及到了進(jìn)程間數(shù)據(jù)傳輸逗余,系統(tǒng)的原本實(shí)現(xiàn)是通過(guò)管道,但是原有的實(shí)現(xiàn)中兩個(gè)進(jìn)程是在同一臺(tái)機(jī)器季惩,而且兩...
    Jensen95閱讀 3,141評(píng)論 0 8
  • 研究IPv6 socket編程原因: Supporting IPv6 in iOS 9 WWDC2015蘋果宣布在...
    li大鵬閱讀 7,326評(píng)論 7 15
  • 1三個(gè)相關(guān)數(shù)據(jù)結(jié)構(gòu). 關(guān)于socket的創(chuàng)建录粱,首先需要分析socket這個(gè)結(jié)構(gòu)體,這是整個(gè)的核心画拾。 104 str...
    ice_camel閱讀 2,829評(píng)論 1 8