Linux系统下Socket套接字API介绍

0 简介

Socket套接字是一种进程间通信的方法,允许位于同一主机(计算机)或使用网络连接起来的不同主机上的应用程序之间交换数据。这组API已经被移植到了所有Unix实现以及其他大多数操作系统上了。下面我们介绍几个关键的套接字API:socket、bind、listen、accept、connect。

1 创建一个socket:socket()

socket()系统调用创建一个新socket。

#include <sys/socket.h>
int socket(int domain,int type,int protocol)//若返回-1说明出错

domain参数指定socket的通信domain,一般定义为AF_INET,表示在使用IPv4网络连接起来的主机上通信。
type参数指定Socket类型,一般定义为SOCK_STREAM,表示这是一个流式socket,提供可靠的双向字节流动通信信道。也可指定为SOCK_DGRAM,表示这是一个数据报socket。自Linux内核2.6.27开始,type参数可以设置为SOCK_NONBLOCK,表示这个一个非阻塞socket;也可设置为SOCK_CLOEXEC,实现close-on-exec。
protocol一般指定为0。

2 将socket绑定到一个地址:bind()

bind()实现将socket绑定到一个地址,参数sockfd就是socket()返回的文件描述符,sockaddr是地址结构,包含协议、ip地址、端口号三个信息,后面详细介绍。

#include <sys/socket.h>
int bind(int sockfd,struct sockaddr* addr,
socklen_t addrlen_t addrlen) //返回-1表示出错。

bind涉及的知识比较多,下面我们详细说一下。

2-1 网络字节序

首先回顾一下字节序的知识。IP地址和端口号均是整数,这些值在网络中传递时有一个问题便是不同硬件结构存储多字节整数的顺序不同,分为大端存储和小端存储。
所谓大端存储,是将一个数字的最高有效位存储在较低的内存地址处,这种存储方式比较符合我们的直觉,比方有一个二进制数1000,最高位的1会存在较低内存,类似我们从左往右读数。而小端存储反过来,是将一个数字的最高有效位存储在较高的内存地址处,x86架构的硬件使用的是小端存储。
由于端口号和地址必须要在网络中所有主机间传递,所以必须使用一个标准字节序,它是大端的,称为网络字节序。而我们为bind的地址结构sockaddr的信息赋值时,这些值是按找主机的规则来表示的,所以需要转换为网络字节序,通常会使用到这些函数。

#include <arpa/inet.h>
uint16_t htons(uint16_t host_uint16)//主机字节序转网络字节序,short
uint32_t htonl(uint32_t host_uint32)//主机字节序转网络字节序,long
uint16_t ntohs(uint16_t net_uint16)//网络字节序转主机字节序,short
uint32_t ntohl(uint32_t net_uint16)//网络字节序转主机字节序,long

函数的功能如注释所示,另外补充一组Linux独有的字节序转换函数,名称上将n(net)换为了be(big end)。

#include <endian.h>
uint64_t htobe64(uint64_t data)//(无符号)64字节主机转网络:
uint64_t be64toh(uint64_t data)//(无符号)64字节网络转主机:
uint32_t htobe32(uint32_t data)//(无符号)32字节主机转网络:
uint32_t be32toh(uint32_t data)//(无符号)32字节网络转主机:
uint16_t htobe16(uint16_t data)//(无符号)16字节主机转网络:
uint16_t be16toh(uint16_t data)//无符号)16字节网络转主机:

2.2 Internet Socket地址 struct sockaddr_in

Internet domain socket地址有IPv4与IPv6两种,这里只介绍IPv4。IPv4的地址结构存储在sockaddr_in中,具体如下

#include <netinet.h>
struct in_addr
{
    
    
	in_addr_r s_addr;//无符号32位整数
}
struct sockaddr_in
{
    
    
	sa_family_t sin_family;//地址协议族,定义为整数,一般是AF_INET
	in_port_t sin_port;//short型整数 端口号,网络字节序。
	struct in_addr sin_addr;//IP地址,网络字节序
	unsigned char _pad[X];//
}

注意bind的第二个参数是struct sockaddr,这是一个通用的地址结构。如前所述Internet domain socket地址有IPv4与IPv6两种,而内核中通信又有一种Socket地址sockaddr_un,为了让三者统一,转换成单个类型,定义了sockaddr。它的结构如下

struct sockaddr
{
    
    
	sa_family_t sa_family;
	char sa_data[14];
}

当使用bind时需要将sockaddr_in强转。

2.3 inet_pton()和inet_ntop函数

这两个函数用于在IPv4和IPv6地址的二进制形式和点分十进制或十六进制字符串表示法之间转换。

#include <arpa/inet>
int inet_pton(int domain,const char *src_str,void *addrptr)
const char* inet_ntop(int domain,const void *addrptr,char *dst_str,size_t len);

2-4 小结

通过上面三节的知识,bind函数的用法就很明晰了。在IPv4通信中,首先我们需要一个由socket()创建的sockfd,然后定义一个struct sockaddr_in addr,然后为结构体赋值。
赋值时,addr.sin_family 在IPv4下赋值为AF_INET,addr.sin_port需要将一个端口号主机转网络字节序,addr.sin_addr.s_addr也需要转为网络字节序,也可以使用inet_pton使用点分十进制字符串赋值。

bind(sockfd,(sockaddr*)&addr,sizeof addr);//示例

3 监听接入连接:listen()

listen()将sockfd引用的流socket标记为被动,使这个socket可以接受其他主动socket连接。

#include <sys/socket.h>
int listen(int sockfd,int backlog);

socket第二个参数backlog的用途是这样的,当一个主动socket来连接sockfd时,必须要在accept函数(后面讲)处于阻塞等待状态时连接申请才能被处理。如果出现accept正在处理其他主动socket,还没有返回阻塞等待状态,这时若来了一个连接,就将这样的连接称为未决的连接。
内核会记录下这样未决连接的信息,使accept后续进行处理。backlog参数用于限制这种未决连接的数目。在backlog数目内的未决连接可以被立即处理。Linux下的软上限是128,内核2.4.45后可以在/proc/sys/net/core/somaxconn下修改。

4 接受连接:accpet

如前所述,listen()将sockfd引用的流socket标记为被动,使这个socket可以接受其他主动socket连接。这里的接受就是由accept完成的。如果调用accept不存在未决连接,它将阻塞。

#include <sys/socket.h>
//若返回-1表示出错
int accept(int sockfd,strcut sockaddr *addr,socklen_t *addrlen);

accept的返回值是一个新的socket文件描述符。这个socket会与发起连接的socket进行通信。
另外值得注意的是内核2.6.8之后Linux支持一个新的系统调用accept4,它的前三个参数与accept一样,多了一个参数flags。通过这个参数我们可以设置新的socket为非阻塞的(SOCK_NONBLOCK)和close-on-exec的(SOCK_CLOEXEC)的。

5 连接到对等的socket:connect()

connect()用于将文件描述符引用的socket连接到一个监听的socket上。第二个参数代表一个地址信息。

#include <sys/socket.h>
//若返回-1表示出错
int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen)

6小结

上面的内容总结了Linux下常用的套接字API。使用这些套接字便可以进行TCP服务端-客户端网络通信了。具体的流程如下图所示,这个过程类似打电话。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/MoonWisher_liang/article/details/107548129