socket

socket介绍

  • socket是一种IPC机制,但是它不仅可以实现本机进程间的通信还可以实现不同网络中的进程间通信。一般的unix操作系统以及大多数操作系统都是使用BSD版本的套接字
  • 使用int socket(int domain, int type, int protocol);系统调用可以创建一个socket,该调用返回一个指向socket的文件描述符。第一个参数domain表示通信的范围,常用取值有AF_INET、AF_INET6、AF_UNIX,取值的含义见下表;第二个参数type表示该套接字的类型,常见的取值有流套接字和数据报套接字(SOCK_STREAM和SOCK_DGRAM);第三个参数一般指定为0,但如果要使用原始套接字需要指定为SOCK_RAW
domain 通信方式 地址格式 地址结构
AF_INET 通过ipv4进行通信 32位ip地址加上16位端口号 sockaddr_in
AF_INET6 通过ipv6进行通信 128为ip地址加上16位端口号 sockaddr_in6
AF_UNIX 通过本地主机通信 路径名 sockaddr_un
  • 在创建完socket以后,需要将一个地址绑定到socket上,这时候需要调用int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);其中sockfd是相关套接字的文件描述符;addr是指向地址结构的指针,该结构的类型根据domain的参数而确定;addrlen表示该结构的大小。该函数调用成功返回0,失败返回-1
  • 流socket通常分为主动和被动两种,在默认情况下,使用socket调用创建的socket是主动的。一个主动的socket可以通过int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen)调用来连接一个被动的sokcet(监听套接字),其中sockfd是对应的主动套接字,addr是远端被动套接字绑定的地址,addrlen是结构体长度,这称为主动打开一个连接。而一个被动的socket需要调用int listen(int sockfd,int backlog)函数来获得,相当于开启监听。sockfd是对应的套接字文件描述符,backlog是指定内核中未决套接字队列的长度。当服务端没法处理一个连接时(可能正在执行IO或者其他),内核会将accept产生的套接字放入未决队列中,accept调用会立即返回成功;如果超出了队列的长度限制,accept调用会阻塞直到队列中有空闲的位置,出现这种情况意味着有大量的socket同时连接服务器,在这个时候对于在对等端调用connect连接函数的socket来说,可能就会连接失败
  • int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen)中的sockfd是一个被动套接字,如果调用成功,addr会指向对等端的地址结构体,addrlen是结构体长度;并且该函数返回一个新的文件描述符,指向一个新的套接字,这个新的套接字就是后续用来与对等端通信的socket
  • 由于socket相关函数返回的都是文件描述符,所以可以使用read、write等IO函数进行数据收发;并且可以调用close关闭socket。同时,socket的IO方式和管道的IO方式很相似,如果某对等端关闭了套接字,那另一端想从中读取数据会返回EOF;往里面写数据会收到SIGPIPE信号,中断write调用并返回EPIPE错误
  • 使用数据报套接字收发数据需要使用专门的函数ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen)以及ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen)
  • 对于unix domain来说,即本机进程间的通信,想要连接一个流socket需要在该socket文件上拥有写权限;想要通过数据报socket发送数据需要对socket文件拥有写权限。此外,都需要对该socket所在路径的所有目录有执行权限
  • udp是不可靠的协议,因为udp在ip协议之上只提供了两个额外的特性,端口号和数据校验。所以在使用udp传输数据时需要在应用层保证其可靠性(如果必要的话)

Internet Domain SOCKET

  • 由于不同的硬件架构用不同的字节顺序来存储数据,所以为了方便在网络上传输,需要规定一个标准的存储顺序,称之为网络字节序。这是一种大端的字节序,即数据的最高有效位存储在地址的低字节。所以经常需要将数据在网络字节序和主机字节序之间相互转换,常常使用下面这组函数进行转换
#include <arpa/inet.h>

uint16_t htons(uint16_t host_uint16);
uint32_t htonl(uint32_t host_uint32);
uint16_t ntohs(uint16_t net_uint16);
uint32_t ntohl(uint32_t net_uint32);
  • 对于Internet Domain类型的socket来说,地址类型分为ipv4和ipv6两种,这里只介绍ipv4。即对于domain为AF_INET的socket来说,使用的地址被存储于一个sockaddr_in的结构体中。其中sin_family需要指定为AF_INET;sin_port表示该地址绑定的端口号,端口号的类型为in_port_t(16位无符号整型),一般使用htons函数转换字节序;sin_addr是一个存放具体ipv4地址的结构体in_addr,该结构体中只有一个in_addr_t类型的成员变量,表示一个32位无符号整型;最后一个_pad字段是用来填充字节的,一般不设置。相关结构体如下
#include <netinet/in.h>

struct in_addr{
    in_addr_t s_addr;
};

struct sockaddr_in{
    sa_family_t sin_family;
    in_port_t sin_port;
    struct in_addr sin_addr;
    unsigned char _pad[X];
};
  • int inet_pton(int domain,const char* src_str,void* addrptr)函数允许将字符串表示的地址转换为对应的二进制格式的地址。其中domain指定AF_INET或者AF_INET6;src_str表示字符串表示的v4或者v6地址;转换得到的二进制地址会存放于addrptr中(该指针指向一个in_addr或者in_addr6结构)。相反的,const char* inet_ntop(int domain,const void* addrptr,char* dst_str,size_t len)函数将一个二进制表示的地址转换为相对应的字符串形式。其中domain参数的意义和上面一样;addrptr应该指向一个in_addr或者in_addr6结构体;dst_str指向存放字符串的缓冲区;len表示缓冲区长度
  • 如果只是单纯的想要转换ipv4地址,可以使用char* inet_ntoa(struct in_addr in)int inet_aton(const char* string,struct in_addr* addr)代替上面较复杂的函数
  • 函数int getaddrinfo(const char *node, const char *service,const struct addrinfo *hints,struct addrinfo **res)可以通过查询DNS服务器获得所指定域名的所有相关地址以及端口号(服务名)。该函数调用成功返回0,否则返回错误码,可以调用const char* gai_strerror(int errcode)函数来查看出错信息。其中node参数包含一个主机名或者字符串形式的地址;service参数包含一个十进制的端口号或者服务名;hints参数指向一个addrinfo结构体,用来规定res列表中返回的地址的标准,hints参数只能设置结构体中的ai_flags,ai_family,ai_socktype和ai_protocol这四个字段,其余字段应该设置为0,ai_family字段指定了返回的socket的地址结构(ipv4或者ipv6或者其他),ai_socktype指定了返回的socket类型,可以是SOCK_DGRAM或者SOCK_STREAM等;res参数返回一个addrinfo结构体列表,保存了所有与指定域名以及端口号相关的地址信息。addrinfo结构体如下
struct addrinfo {
     int ai_flags;
     int ai_family;
     int ai_socktype;
     int ai_protocol;
     socklen_t ai_addrlen;
     struct sockaddr* ai_addr;
     char* ai_canonname;
     struct addrinfo* ai_next;
 };
  • 由于getaddrinfo函数如果调用成功,会动态分配内存,保存结构体链表。所以若不在需要该链表,需要主动调用void freeaddrinfo(struct addrinfo *res)释放内存
  • 对于一个socket来说,可以调用int getsockname(int sockfd, struct sockaddr*addr, socklen_t *addrlen)来获得相关的本地地址信息;也可以调用int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)来获得对等端相对应的客户端地址信息
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容

  • 套接字是网络通信的基石,是网络通信的基本构建,最初是由加利福利亚大学Berkeley分校为UNIX开发的网络通信编...
    Super超人阅读 3,876评论 0 26
  • 下面为Daytime这个服务的源代码例子,同时兼容IPV6和IPV4的地址,最后部分有更多说明。 单播模式下的Se...
    天楚锐齿阅读 5,542评论 0 2
  • 网络模型 物理层 物理层表示的是比特流传输,通常包括串口/COM口、并行/LPT口、USB、网线接口、电话线接口;...
    秋风弄影阅读 695评论 0 2
  • socket的基本概念 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。...
    小叶大孟阅读 675评论 0 0
  • 大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相...
    VD2012阅读 2,247评论 0 5