网络编程基础API
用于Ctrl+F
Socket地址结构体
struct sockaddr_in
{
sa_family_t sin_famly; // 地址族 AF_INET
uint16_t sin_port; // 端口号 用网络字节序表示
struct in_addr sin_addr; // IPv4地址结构体
};
struct in_addr
{
uint32_t s_addr; // IPv4地址
}
socket生成函数
创建一个socket
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
- domain 使用的协议族
- TCP/IP使用PF_INET(Protocol Family of Internet 用于IPv4)
- PF_INET6(Protocol Family of Internet 用于IPv6)
- PF_UNIX (用于UNIX)
- type 指定服务类型
- SOCK_STREAM(流服务)
- SOCK_UGRAM(数据报服务)
- protocol 选择的具体协议,通常情况下为0,表示使用默认值
- 成功返回一个socket文件描述符 失败返回-1
bind绑定
创建socket时指定了地址族,但未指定使用哪个具体socket地址
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
- 将my_addr所指的socket地址分配给sockfd文件描述符
- addrlen为该socket地址的长度,使用sizeof获取
- 成功返回0 失败-1
listen监听
socket 被命名之后需要创建一个监听队列来存储客户连接
#include <sys/socket.h>
int listen(int sockfd, int backlog);
- back指定队列最大长度
- 成功返回0 失败-1
accept接受连接
服务器通过accept被动接受连接
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, const struct sockaddr* addr, socklen_t *addrlen);
- addr用来获取被接受连接的远端socket地址
- addrlen标识这个socket地址的长度
- 成功时返回新的连接socket,客户端可以读写这个socket来和远端服务器通信
connect发起连接
客户端通过connect主动与服务器建立连接
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen);
- sockfd标识返回的服务器连接
- 成功返回0 失败-1
close关闭连接
关闭连接
#include <unistd.h>
int close(int fd);
- fd为待关闭的连接
- close只会将fd的引用计数-1 只有其引用计数为0时才真正关闭连接
TCP数据读写
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
- recv返回读取到的数据的长度 0表示对方关闭了连接
- send返回写入的长度 失败返回-1
UDP数据读写
#include <sys/types.h>
#include <sys/socket.h>
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,
struct sockaddr* dest_addr, socklen_t* addrlen);
- 因为UDP没有连接的概念,所有每次recvfrom都需要获取发送端的socket地址
- 只要把最后两个参数设置为NULL 就可以用于面向连接的socket通信
通用数据读写
可用与TCP流数据和UDP数据报
#include <sys/socket.h>
ssize_t recvmsg(inti sockfd, struct msghdr* msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr* msg, int flags);
msghdr结构体定义
struct msghdr
{
void* msg_name; // socket地址
socklen_t msg_namelen; // socket地址的长度
struct iovec* msg_iov; // 分散的内存块
int msg_iovlen; // 分散内存块的数量
void* msg_control; // 指向辅助数据的起始位置
socklen_t msg_controllen; // 辅助数据的大小
int msg_flags; // 复制函数中的flags参数
}
sockatmark带外标记
#include <sys/socket.h>
int sockatmark(int sockfd);
用于判断sockfd是否处于带外标记,如果是则返回1,此时可以利用带MSG_OOB标记的recv来接受带外数据
地址信息
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr* address, socklen_t* address_len);
int getpeername(int sockfd, struct sockaddr* address, socklen_t* address_len);
- 前者返回本段socket地址 并存于address中
- 后者返回远端socket地址
- 成功返回0 失败返回-1
socket选项
用于读取和设置socket文件描述符属性
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int option_name,
void* option_value,socklen_t* restrict option_len);
int setsockopt(int sockfd, int level, int option_name,
const void* option_value, socklen_t option_len);
Linux函数学习:setsockopt、getsockopt和getsockname函数。
gethostbyname和gethostbyaddr
根据主机名/IP地址获取主机的完整信息
#include <netdb.h>
struct hostent* gethostbyname(const char* name);
struct hostent* gethostbyaddr(const void* addr, size_t len, int type);
- name指定目标主机的主机名
- addr指定目标主机的IP地址
- len指定addr所指IP地址的长度
- type指定IP地址的类型 AF_INET/AF_INET6
hostent结构体定义
#include <netdb.h>
struct hostent
{
char* h_name; // 主机名
char** h_aliases; // 主机别名列表
int h_addrtype; // 地址类型
int h_length; // 地址长度
char** h_addr_list; // IP地址列表
}
getservbyname和getservbyport
根据名称/端口号获取某个服务的完整信息
#include <netdb.h>
struct servent* getservbyname(const char* name, const char* proto);
struct servent* getservbyport(int port, const char* proto);
- name指定目标服务的名字
- port指定目标服务对应的端口号
- proto指定服务类型 tcp-获取流服务 udp-获取数据报服务 NULL-获取所有类型服务
servent结构体定义
#include <netdb.h>
struct servent
{
char* s_name; // 服务名
char** s_aliases; // 服务别名列表
int s_port; // 端口号
char* s_proto; // 服务类型
}
getaddrinfo
技能通过主机名获得IP地址(内部使用gethostbyname)
也能通过服务名获得端口号(内部使用getservbyname)
#include <netdb.h>
int getaddrinfo(const char* hostname, const char* service,
const struct addrinfo* hints, struct addrinfo** result);
- hostname 可以接受主机名/字符串表示的IP地址
- service 可以接受服务名/字符串表示的十进制端口号
- hints 应用程序给getaddrinfo的提示
- result 指向存储getaddrinfo反馈的结果链表
- getaddrinfo反馈每一条结果都是addrinfo结构体的对象
addrinfo结构体定义
struct addrinfo
{
int ai_flags; // 见表
int ai_family; // 地址族
int ai_socktype; // 服务类型 SOCK_STREAM/SOCK_DGRAM
int ai_protocal; // 具体的网络协议 通常为0
socklen_t ai_addrlen; // ai_addr的长度
char* ai_canonname; // 主机的别名
struct sockaddr* ai_addr; // 指向socket地址
struct addrinfo* ai_next; // 指向下一个sockinfo对象
}
选项 | 含义 |
---|---|
AI_PASSIVE | 在hints参数中设置,表示调用者是否会将取得的socket地址用于被动打开。服务器通常需要设置它,表示接受任何本地socket地址上的服务请求,客户端程序不能设置它。 |
AI_CANONNAME | 在hints参数中设置,告诉getaddrinfo函数返回主机的别名 |
AI_NUMERICHOST | 在hints参数中设置,表示hostname必须是用字符串表示的IP地址,从而避免了DNS查询 |
AI_NUMERICSERV | 在hints参数中设置,强制service参数使用十进制端口号的字符串形式,而不能是服务名 |
AI_V4MAPPED | 在hints参数中设置,如果ai_family被设置为AF_INET6,那么当没有满足条件的IPv6地址被找到时,将IPv4地址映射为IPv6地址 |
AI_ALL | 必须和AI_V4MAPPED同时使用,否则将被忽略。表示同时返回符合条件的IPv6地址以及由IPv4地址映射得到的IPv6地址 |
AI_ADDRCONFIG | 仅当至少配置由一个IPv4(除了回路地址)时,才返回IPv4地址信息;同样,仅当至少配置有一个IPv6地址(除了回路地址)时,才返回IPv6地址信息,它和AI_V4MAPPED是互斥的 |
getnameinfo
通过socket地址同时获得以字符串表示的主机名(gethostbyaddr)和服务名(getservbyport)
#include <netdb.h>
int getnameinfo(const struct sockaddr* sockaddr, socklen_t addrlen, char* host,
socklen_t hostlen, char* serv, socklen_t servlen, int flags);
- 将主机名存储在host指向的缓存中
- 将服务名存储在serv指向的缓存中
- hostlen和servlen分别指定这两块缓存的长度
- flags参数控制函数的行为 如下表
选项 | 含义 |
---|---|
NI_NAMEREQD | 如果通过socket地址不能获得主机名,则返回一个错误 |
NI_DGRAM | 返回数据报服务。大部分同时支持流和数据报的服务使用相同的端口号来提供这两种服务,但端口512-514是例外。比如TCP的514端口提供的shell登录服务,而UDP的514提供的是syslog服务 |
NI_NUMERICHOST | 返回字符串表示的IP地址,而不是主机名 |
NI_NUMERICSERV | 返回字符串表示的十进制端口号,而不是服务名 |
NI_NOFQDN | 仅返回主机域名的第一部分 |