【Linux网络编程】socket套接字的基础API接口
目录
前言
1. 网络字节序
2. socket编程接口
2.1 socket
2.2 bind
2.3 地址转换函数
2.4 recvfrom
2.5 sendto
2.6 TCP socket API
2.6.1 listen()
2.6.2 accept()
2.6.3 close()
2.6.4 connect()
总结
前言
了解了计算机网络的基础知识,想要快速上手网络编程,那就很有必要了解一下网络套接字相关的接口;本文主要介绍一些socket编程必须的一些接口介绍;
1. 网络字节序
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络 字节序和主机字节序的转换。
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
比如:htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
- 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
- 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。
2. socket编程接口
套接字也分很多种:
- Unix socket:域间socket;不使用ip,使用的是同一台机器上的文件路径,很像命名管道,主要用于本主机内部进行通信
- 网络 socket:ip + port 用于网络通信,接口和上述使用的接口一样;
- 原始socket:编写一些网络工具
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同;
想要把他们绑定在一起,所以都统一使用struct socketaddr类型;在判断时只需根据前两个字节进行判断是网络通信,还是域间 socket 识别后在接口内部,将struct socketaddr类型强转成对应的类型也就是C风格的多态;
struct sockaddr {
sa_family_t sa_family; /* Address family */
char sa_data[]; /* Socket address */
};
struct sockaddr_in {
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* Port number */
struct in_addr sin_addr; /* IPv4 address */
};
struct sockaddr_un {
sa_family_t sun_family; /* Address family */
char sun_path[]; /* Socket pathname */
};
struct in_addr {
in_addr_t s_addr;
};
typedef uint32_t in_addr_t;
2.1 socket
函数原型:
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
返回值:
参数:
-
domain(地址域):
- 这个参数指定了使用的地址族,常见的值有:
AF_INET
:表示 IPv4 协议。AF_INET6
:表示 IPv6 协议。AF_UNIX
:表示 Unix 域套接字,用于同一主机上的进程间通信。
- 选取合适的地址域以符合所需的网络协议。
- 这个参数指定了使用的地址族,常见的值有:
-
type(套接字类型):
- 这个参数指定套接字的类型,常见的值有:
SOCK_STREAM
:表示流式套接字,基于 TCP,提供可靠的、面向连接的通信。SOCK_DGRAM
:表示数据报套接字,基于 UDP,提供无连接的、不可靠的数据传输。SOCK_RAW
:表示原始套接字,允许直接访问传输层协议,通常需要超级用户权限。
- 选择合适的套接字类型来匹配通信协议的需求。
- 这个参数指定套接字的类型,常见的值有:
-
protocol(协议):
- 这个参数通常指定协议类型。在许多情况下,可以设置为 0,让系统自动选择合适的协议(例如,选择 TCP 对于流套接字)。
- 对于原始套接字,可以指定特定的协议(如
IPPROTO_TCP
)。
使用示例:创建udp套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
2.2 bind
函数原型:
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
为了在通信中,能够让其他主机找到socket就需要绑定网络信息;
返回值:
参数:
-
sockfd:这是要绑定的套接字的文件描述符。它必须先通过
socket()
函数创建。 -
addr:这是指向
struct sockaddr
(或更具体的类型,比如用于 IPv4 地址的struct sockaddr_in
)的指针,指明套接字应绑定到哪个地址。该结构包含地址族、端口号和 IP 地址。 -
addrlen:该参数指定
addr
指向的地址结构的大小。通常可以使用sizeof(struct sockaddr_in)
或者你所使用的地址结构的适当大小。
使用示例:
//绑定指定网络信息
struct sockaddr_in local;
bzero(&local, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port); //不能直接传,要本地主机转网络字节序
local.sin_addr.s_addr = inet_addr(_ip.c_str());// ip地址要转换成4字节整形
int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
sockaddr_in 结构体对象是在函数内定义的,只存在栈上,并没有设置到内核中,所以要使用bind把数据加载到内核中 ;
对结构体对象进行初始化,可以调用这个接口,作用是将数据置为0,s开始的n个字节;
#include <strings.h>
void bzero(void s[.n], size_t n);
2.3 地址转换函数
我们使用的是ip都是字符串风格,系统中使用的是32位整形,所以我们需要进行转换,并且需要将ip转换为网络字节序我们自己转换就有点麻烦了,库里给我们提供了一系列的接口:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
除此之外还有许多其他的接口比如:
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
感兴趣可以去了解一下;
2.4 recvfrom
recvfrom() 用于接收网络数据报套接字(UDP)的系统调用;
函数原型:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
-
sockfd: 套接字描述符,代表一个已创建并绑定到特定地址和端口的套接字。通常通过
socket()
函数创建。 -
buf: 指向用来存储接收到的数据的缓冲区的指针。接收到的数据将被写入这个缓冲区。
-
len: 接收缓冲区的大小(以字节为单位)。这是最大可以接收的数据量。
-
flags: 控制接收操作的选项。可以使用以下标志:
MSG_CONFIRM
: 确认接收的消息,通常在处理同一套接字中的重复消息时使用。MSG_DONTWAIT
: 非阻塞模式下接收数据。- 0 阻塞等待。
-
src_addr: 一个指向
sockaddr
结构的指针,用于存储发送方的地址信息。如果不需要地址信息,可以将其设为NULL
。 -
addrlen: 指向
socklen_t
类型的变量的指针,表示src_addr
的长度。在调用recvfrom()
之前应该初始化为sizeof(struct sockaddr)
,接收到数据后,该值将会被更新为实际接收的地址长度。
返回值:
- 返回值为接收到的字节数。如果返回值为
-1
,表示发生错误,可以通过errno
获取错误信息。
除此之外可以获取接收到的消息接口还有:
- recv(通常用于TCP)
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
- read(通用的系统调用,可以从文件描述符中读取数据,也反映出套接字本质就是文件描述符)
ssize_t read(int fd, void *buf, size_t count);
2.5 sendto
用于在网络编程中发送数据的系统调用,适用于不同的协议和场景。通常用于 UDP 套接字,适合于无连接的通信场景,可以发送数据至任何指定的地址。它适用于实时应用、广播或多播等。
函数原型:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
-
flags: 控制发送操作的选项。常见的标志包括:
MSG_OOB
: 发送紧急数据。MSG_DONTWAIT
: 非阻塞模式。
-
dest_addr: 指向
sockaddr
结构的指针,表示目标地址(对于 UDP 等无连接协议时必需)。
其余参数和 recvfrom 相同;
返回值:
成功时返回已发送的字节数,失败时返回 -1
,并设置 errno;
除此之外可以用于发送消息的接口还有:
- send()
send()
主要用于 TCP 套接字,适用于已经建立连接的场景,保证了数据的顺序和完整性
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
flag以及返回值和sendto一样;
- write()
write()
是一个通用的写入系统调用,可以用于多种类型的文件描述符,也可以用于发送数据的系统调用,对于套接字,特别是 TCP 套接字,可以使用 write()
来发送数据。
ssize_t write(int fd, const void *buf, size_t count);
返回值:
成功时返回实际写入的字节数。如果返回值是 0
,则表示没有数据写入。如果返回 -1
,则表示发生错误,并且可以使用 errno
来获取错误信息
2.6 TCP socket API
socket、bind、以及发送(send)和接收(recv)相关的接口都可以参考上述内容;接下来这些接口属于TCP专属的网络接口;
TCP和UDP不同,是面向连接的,UDP是无连接的;这些后续会专门出一期来详细介绍UDP和TCP;本文主要介绍他们的socket API;
2.6.1 listen()
在指定的套接字上开始监听传入连接;
函数原型:
int listen(int sockfd, int backlog);
参数:
sockfd
: 套接字描述符。backlog
: 指定等待连接的队列长度。
返回值: 成功返回 0,失败返回 -1。
2.6.2 accept()
接受来自客户端的连接请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd
: 套接字描述符。addr
: 可选,指向一个用于存储客户端地址的结构体。addrlen
: 指向地址长度变量的指针。
返回值: 成功返回新的套接字描述符,失败返回 -1。
2.6.3 close()
关闭套接字
函数原型:
int close(int fd);
参数:fd
: 套接字描述符。
返回值: 成功返回 0,失败返回 -1
2.6.4 connect()
该接口主要是客户端使用,将客户端套接字连接到服务器;
函数原型:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
- sockfd: 客户端的套接字描述符,这个描述符应当是通过
socket()
创建的。 - addr: 指向包含服务器地址信息的
struct sockaddr
结构的指针。通常,对于 IPv4,使用struct sockaddr_in
。 - addrlen: 地址结构的长度,通常用
sizeof(struct sockaddr_in)
来获取
返回值:功时返回 0
,失败则返回 -1
,并设置 errno
以指明错误类型。
总结
以上便是本文的全部内容,希望对你有所帮助,感谢阅读!