一、创建socket
?????socket就是一个可读、可写、可控制、可关闭的文件描述符(整型数字)
int socket(int domain, int type, int protocol)
domain:指定系统使用的协议。可以是TCP/IP协议族,也可以是UNIX协议族。分别对应的值为
PF_INET(IPv4)/PF_INET6(IPv6) 和 PF_UNIX。
type: 指明了服务的类型,有流服务和数据报服务,分别对应的参数为SOCK_STREAM、SOCK_UGRAM。
对于TCP/IP协议族来说,TCP是基于字节流的,因此传输层使用TCP,那么type要设置为.
SOCK_STREAM。UDP是基于数据报的协议,因此type的值对应为SOCK_UGRAM。
protocol:表示在前面两个参数的筛选下可以选择的协议,一般值为唯一的迷糊所有情况下,都将此值设置为0,表示默认协议。
return:socket调用成功返回一个socket文件描述符。失败返回-1,并设置errno。
????
二、命名socket
???????创建socket时,指定了使用哪个协议,就说明指定了地址族。但是没有指定该地址族中哪个具体的socket地址。这里我们可以理解,在我们的内核中,分为TCP/IP协议族、UNIX协议族等几大协议族,每个协议族有很多的socket,每个socket由一个具体的地址唯一的标识,当然,每个socket包含了相应(TCP/IP、UNIX协议族下的所有协议服务)。
将一个socket和socket地址绑定,称为给socket命名。在一般的服务器程序中要命名socket。但是客户端一般不需要命名socket,客户端一般采用匿名方式,使用os自动的分配socket地址。
命名socket需要调用函数bind()。
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen)
将my_addr指针指向的socket赋值给sockfd文件描述符,addrlen指的是socket地址的长度。
return: 地址绑定成功返回0,失败返回-1,并设置errno。
errno的值可以为EACCES、EADDRINUSE,EACCES表示指定的地址是受保护的地址,只有超
级用户可以访问,EADDRINUSE表示此socket地址正在被使用。
注意上述命名过程中最主要的一点是socket的地址,下面详细介绍socket地址的创建。
?
2、1通用的socket地址结构体
struc sockaddr
{
sa_family_t sa_family;
char sa_data[14];
}
协议族 | 地址族 |
---|
PF_UNIX | AF_UNIX | PF_INET | AF_INET | PF_INET6 | AF_INET6 |
但是由于像 PF_UNIX的地址长度为108字节, PF_INET的长度为6字节, PF_UNIT6地址的长度为26字节,sa _data无法满足协议地址族的地址值。因此,Linux下创建了新的socket地址结构体。
struc sockaddr_storage
{
sa_family_t sa_family;
unsigned long int __ss_align;
char __ss_padding[128-sizeof(__ss_align)];
}
?
2、2专用socket地址
上述的socket结构中只有地址选项,要获取相应的IP地址,要进行相应的位操作。因此Linux建立了专用socket地址,以方便获取IP地址和端口号。 比如IPv4的socket地址结构如下:
struct socketaddr_in
{
sa_family_t sa_family;
u_int6_t sin_port;
struct in_addr sin_addr;
};
struct in_addr
{
u_int32_t s_addr;
};
另外还有对应的IPv6socket地址专用结构,和IPv4的类似,都显示的标识了端口号和IP地址,注意此处的端口号和IP地址都是网络字节码标识,即大端模式。
?
三、监听socket
由于服务器要处理多个客户端的请求,因此不能始终的即使处理,因此要建立一个队列,将连接存储在队列中依次处理。因此就需要始终监听客户端的请求连接,然后合理的处理客户端的请求连接。
int listen(int sockfd,int backlog)
sockfd指定被监听的socket,backlog参数提示内核监听队列的最大长度。标识socket完全连接状态的socket数量,典型值为5.
?
四、接受连接
从监听队列中接收一个连接
int accept(int sockfd,struct sockaddr* addr, socklen_t * addrlen)
sockfd指定被监听过的的socket,addr是获取被接收连接的远端socket地址,addrlen指定了远端socket地址的长度。
?
五、总结
服务端socket编程的一个完整流程: -> 创建socket(指明要使用的协议,TCP/IP或者UNIX协议,接下来细化TCP、UDP) ->创建一个socket地址(IPv4、IPv6,注意端口号和IP地址要转为网络字节序整数) ->命名socket(将socket和socket地址进行绑定,用到了bind函数) ->socket的监听(对客户端请求的监听,用到了listen函数) ->accept函数接收连接,得到一个新的连接socket,服务器端就可以和远端的客户端进行读写操作。
|