linux网络编程技术API
包括socket地址API、socket基础API,网络信息API
网络字节序和主机字节序
也叫大端和小端。
- 大端字节序:一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。
- 小端字节序:一个整数的高位字节存储在内存的高地址处,低位字节存储在内存的低地址处。
判断字节序的代码
bool IsBigEndian()
{
union
{
unsigned short a ;//2个字节
char b ;//高地址
} c;
c.a =0x0102 ;
if(c.b ==1)//如果高地址存放低字节
return true ;
else
return false ;
}
linux提供的字节序转换函数
#include<netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
通用socket地址
#include<socket.h>
struct sockaddr{
sa_family_t sa_family;
char sa_data[14];
}
- sa_family 是地址类型的变量,地址族类型通常与协议族类型对应
- sa_data 用于存放socket地址值
专用socket地址
struct sockaddr_in{
sa_family_t sin_family;//地址族
u_int16_t sin_port;//端口号
struct in_addr sin_addr;//ipv4地址结构体
};
struct in_addr{//ipv4 地址结构体
u_int32_t s_addr;
};
IP地址转换函数
#include<arpa/inet.h>
in_addr_t inet_addr(const char* strptr);
//点分十进制字符串转ipv4地址,失败返回INADDR_NONE
int inet_aton(const char* cp,struct in_addr* inp);
//同inet_addr,结果存在inp中,成功1失败0
char* inet_ntoa(struct in_addr in);
//ipv4地址转点分十进制字符串,结果具有不可重入性
#include<arpa/inet.h>
int inet_pton(int af,const const char* src,void* dst);
//字符串转地址结构体,可选ipv4/6
//af指定地址族,AF_INET或AF_INET6,
//src是要转换的字符串
//dst是转换结果
const char* inet_ntop(int af,const void * src,char dst,socklen_t cnt);
//地址结构体转字符串,可选ipv4/6
//af指定地址族
//src是要转换的地址结构体
//dst存储转换结果
//cnt指定目标存储单元的大小
创建socket
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,.int type,int protocol);
参数:
domain:协议族PF_INET 或PF_INET6
type:指定服务类型,TCP是SOCK_STREAM,UDP是SOCK_DGRAM
protocol:一般设置为0
返回:
成功返回一个sock文件描述符,失败返回-1并设置errno
命名socket
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen);
bind将所指的socket地址分配给未命名的sockfd文件描述符,addrken指定了socket地址的长度。
成功返回0,失败返回-1,并设置errno。
监听
#include<sys/socket.h>
int listen(int sockfd,int backlog);
给指定的sockfd创建监听队列,队列长度为backlog
成功返回0,失败返回-1,并设置errno。
接受连接
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr* addr,socklen_t * addrlen);
sockfd是执行过listen的socket文件描述符
addr参数用啦获取被连接的远端socket地址,addrlen是他的长度的指针。
返回一个新的连接的socket,该socket唯一标识了被接受的这个连接。
失败返回-1并设置errno
发起连接
#include<sys/types.h>
#include<sys/socket.h>
int connect (int sockfd,const struct sockaddr* serv_addr,socklen_t addrlen);
sockfd是要用来连接的socket文件描述符
serv_addr是要连接的服务器地址,紧跟着他的长度
成功返回0,失败返回-1并设置errno。
关闭连接
#include<unistd.h>
int close(int fd);
可以用关闭普通文件描述符的方式关闭socket文件描述符
值得注意的是,close实际上是将该文件描述符的引用计数-1,当引用计数为0时才真正关闭,因此在多进程中,父子进程中都要执行close。
对于socket可以用专用的方法,立即终止连接
#include<sys/socket.h>
int shutdown(int sockfd,int howto);
sockfd是要关闭的socket文件描述符,howto决定了shutdown的行为。
howto可选值:
SHUT_RD :关闭sockfd的读端,不再执行读操作
SHUT_WR:关闭socket的写端,不再执行写操作
SHUT_RDWR:同时关闭读写端
数据读写
TCP的读写
#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd,void * buf ,size_t len,int flag);
ssize_t send(int sockfd,const void* buf,size_t len, int flag);
recv用于在指定的socket文件描述符上读取数据,数据读取后保存在长度为len的buf里,flag通常设置为0,recv成功返回读取的长度,可能小于len,因此可能要多次调用recv才能独到完整的数据。失败返回-1并设置errno。
send用于发送数据到指定的socket文件描述符上,要发送的数据保存在长度为len的buf里,flag通常设置为0。成功返回实际写入的长度,失败返回-1并设置errno。
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,void* buf,,size_t len,int flags,struct sockaddr* dest_addr,socklen_t addrlen);
recvfrom读取sockfd上的数据,buf和len分别指定读缓冲区位置和大小,还要指定src_addr发送端的地址和addrlen发送端地址长度的指针。
sendto往sockfd上写入数据,buf和len参数分别指定写缓冲区位置和大小,还要指定dest接收端的地址和addrlen接收端地址长度。
通用数据读写
socket编程接口提供了一对通用的数据读写,可用于TCP和UDP
#include<sys/socket.h>
ssize_t recvmsg(int sockfd,struct msghdr* msg,int flags);
ssize_t sendmsg(int sockfd,struct msghdr* msg,int flags);
struct msghdr{
void * msg_name; //socket地址,对于TCP设置为NULL
socklen_t msg_namelen;//socket地址长度
struct iovec* msg_iov;//分散的内存块
int msg_iovlen;//分散内存块数量
void* msg_control;//指向辅助数据
socklen_t msg_controllen;//辅助数据大小
int msg_flags;//复制函数中的flags参数,并在调用过程中更新
}
-
其中,msg_iov是 iovec结构体类型的一个数组指针
-
iovec里保存了一段内存的起始地址和长度。
-
对于recvmsg,数据将被读取,存放在msg_iovlen块分散的内存中,由msg_iov这个数组记录这些分散的内存,叫做分散读。
-
对于sendmsg,这些分散的内存将被一并发送,叫做集中写。
struct iovec{
void *iov_base;
size_t iov_len;
};
带外标记
Linux内核检测到TCP紧急标志时,通知应用程序有带外数据要接收。通知的方式一般是IO复用产生的异常事件和SIGURG信号。
应用程序还需要知道带外数据在数据流中的具体位置。
#include<sys/socket.h>
int sockatmark(int sockfd);
这个系统调用用于判断sockfd是否有带外标记,如果是则返回1,否则返回0.
地址信息函数
#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地址和获得对端的socket地址。
成功返回0,失败-1,并设置errno。
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)
sockfd是要设置或者获取选项的socket文件描述符
level指定了要操作哪个协议的选项,如IPV4、IPV6、TCP
option_name指定了选项的名字
option_value 选项的值
option_len 选项的长度
成功返回0,失败-1,并设置errno。
使用举例:
1、设置地址重用
setsockopt(lisfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse);
2、设置缓冲区大小
/* 设置TCP发送缓冲区大小 */
int sendbuf = atoi(argv[3]);
int len = sizeof sendbuf;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuf, len);
/* 读取socket选项 */
getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuf, (socklen_t*)&len);
printf("发送缓冲区大小是:%d\n", sendbuf);
网络信息API
根据主机名或地址获取主机信息
#include<netdb.h>
struct hostent* gethostbyneme(const char*name);
struct hostent* gethostbyaddr(const void* addr,size_t len,int type);
name指定目标主机的主机名
addr指定目标主机的IP地址,
len指定该地址长度
type指定地址类型:AF_INET/AF_INET6
返回hostent结构体
struct hostent{
char * h_naem;//主机名
char** h_aliases;//主机别名列表
int h_addr_type;//地址类型
int h_length;//地址长度
char** h_addr_list;//按网络字节序列出的IP地址列表
根据端口号或服务名获取某个服务的完整信息
getservbyport
/* 根据端口号获取某个服务的完整信息 */
struct servent* getservbyport(int port, const char *proto);
/* 参数说明 */
// port:目标服务对应的端口号
// proto:指定服务类型,给它传递“tcp”表示获取流服务,
“udp”表示数据报服务,NULL表示所有类型的服务
getservbyname
/* 根据服务名称获取某个服务的完整信息 */
struct servent* getservbyname(const char *name, const char *proto);
/* 参数说明 */
// name:目标服务的名字
// proto:指定服务类型,给它传递“tcp”表示获取流服务,
“udp”表示数据报服务,NULL表示所有类型的服务
靠主机名获得IP、靠服务名获得端口
getaddrinfo
/* 既能通过主机名获得IP地址,也能通过服务名获得端口号 */
int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result);
/* 参数说明 */
// hostname:可以接收主机名,也可以接收字符串表示的IP地址
// service:可以接收服务名称,也可以就收字符串表示的十进制端口号
// hints:应用程序给getaddrinfo的一个提示,以对getaddrinfo的输出进行更精确的控制
// result:指向一个链表,该链表用于存储getaddrinfo反馈的结果
靠地址获得主机名和服务名
getnameinfo
/* 能通过socket地址同时获得以字符串表示的主机名和服务名 */
int getnameinfo(const struct sockaddr *sockaddr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);
/* 参数说明 */
// host:返回的主机名
// serv:返回的服务名
// flags:控制getnameinfo的行为
后记:linux网络编程技术API复杂多样,不可死记硬背