我们知道具体的一个文件可以用一个路径来表示,比如/home/zzy/src_code/client.c,那么具体的socket实例我们该如何表示呢,其实就是使用上面提到的so_pcb的那几个关键属性,也就是使用so_type+ip地址+端口号。如果我们使用so_type+ip地址+端口号实例一个socket,那么互联网上的其他主机就可以与该socket实例进行通信了。所以下面我们看一下socket如何进行实例化,看看socket给我们提供了哪些接口,而我们又该如何组织这些接口
3、socket编程接口 3.1、socket接口int socket(int protofamily, int so_type, int protocol);
protofamily 指协议族,常见的值有:
AF_INET,指定so_pcb中的地址要采用ipv4地址类型
AF_INET6,指定so_pcb中的地址要采用ipv6的地址类型
AF_LOCAL/AF_UNIX,指定so_pcb中的地址要使用绝对路径名
当然也还有其他的协议族,用到再学习了
so_type 指定socket的类型,也就是上面讲到的so_type字段,比较常用的类型有:
SOCK_STREAM
SOCK_DGRAM
SOCK_RAW
protocol 指定具体的协议,也就是指定本次通信能接受的数据包的类型和发送数据包的类型,常见的值有:
IPPROTO_TCP,TCP协议
IPPROTO_UDP,UPD协议
0,如果指定为0,表示由内核根据so_type指定默认的通信协议
这里解释一下图三,图三其实是使用AF_INET,SOCK_DGRAM,IPPRTO_UDP实例化之后的一个具体的socket。
那为什么要通过这三个参数来生成一个socket描述符?
答案就是通过这三个参数来确定一组固定的操作。我们说过抽象的socket对外提供了一个统一、方便的接口来进行网络通信,但对内核来说,每一个接口背后都是及其复杂的,同一个接口对应了不同协议,而内核有不同的实现,幸运的是,如果确定了这三个参数,那么相应的接口的映射也就确定了。在实现上,BSD就把socket分类描述,每一个类别都有进行通信的详细操作,分类见下图。而对socket的分类,就好比对unix设备的分类,我们对设备write和read时,底层的驱动是有各个设备自己提供的,而socket也一样,当我们指定不同的so_type时,底层提供的通信细节也由相应的类别提供。
图4 socket层次图
更详细的socket()函数参数描述请移步:
3.2、bind接口
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind函数就是给图三种so_pcb结构中的地址赋值的接口
sockfd 是调用socket()函数创建的socket描述符
addr 是具体的地址
addrlen 表示addr的长度
struct sockaddr其实是void的typedef,其常见的结构如下图(图片来源传智播客邢文鹏linux系统编程的笔记),这也是为什么需要addrlen参数的原因,不同的地址类型,其地址长度不一样:
图5 地址结构图
AF_INET:
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; struct in_addr { uint32_t s_addr; /* address in network byte order */ };AF_INET6:
struct sockaddr_in6 { sa_family_t sin6_family; /* AF_INET6 */ in_port_t sin6_port; /* port number */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */ }; struct in6_addr { unsigned char s6_addr[16]; /* IPv6 address */ };AF_UNIX:
#define UNIX_PATH_MAX 108 struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[UNIX_PATH_MAX]; /* pathname */ }; 3.3、connect接口int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);