Web-Server高性能服务器

一、LINUX网络编程基础API

1、socket地址API

1.主机字节序和网络字节序

即小端字节序和大端字节序,linux提供htonl、htons、ntohl、ntohs4个函数来实现主机字节序和网络字节序的转换。

2.通用socket地址

socket网络编程接口中表示socket地址的是sockaddr结构体:

struct socadd{
     sa_family_t sa_family;
     char sa_data[4];          
}

sa_family_t为地址族类型(地址族与协议族对应)

linux下的通用socket结构体:

3.专用socket地址

UNIX本地域协议族专用socket地址结构体:

sockaddr_un\sockaddr_in\sockaddr_in6

 

4.IP地址转换函数

in_addr_t inet_addr(const char * strptr):点分十进制的IPv4地址转换为用网络字节序整数表示的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地址转化为用点分十进制字符串表示的IPv4地址。

同时适用于IPv4和IPv6的函数:

指定cnt大小的宏定义:

2、创建socket

 domain参数:使用哪个底层协议,TCP/IP协议族:PF_INET,PF_INET6,UNIX本地域协议族:PF_UNIX;

type参数:指定服务类型,SOCK_STREAM(流服务,TCP)和SOCK_UGRAM(数据报服务,UDP);

protocal参数:在前两个参数构成的协议集合下,再选择一个具体的协议,默认为0(别动它)。

socket系统调用成功时返回一个socket文件描述符,失败返回-1并设置errno。

3、命名socket

 bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出该socket地址长度

bind成功时返回0,失败返回-1并设置errno

两种常见errno:

EACCES:被绑定的地址是受保护的,仅超级用户能访问。

EADDRINUSE:被绑定地址正在使用。

4、监听socket

创建监听队列:

 sockfd参数:指定被监听的socket;

backlog参数:提示内核监听队列的最大长度,长度超过将不受理;

listen成功时返回0,失败则返回-1并设置errno。

5、接受连接

从listen监听队列中接受一个连接:

sockfd参数:执行过listen系统调用的监听socket;

addr参数:获取被接受连接的远端socket地址;

addrlen参数:addr参数获取的socket地址的长度;

accept成功返回一个新的连接socket,该socket唯一地标识了被接受的这个连接,服务器可通过读写该socket来与被接受连接对应的客户端通信;

accpet失败返回-1并设置errno。

6、发起连接

 客户端主动与服务器建立连接的函数:

sockfd参数:由socket系统调用返回一个socket;

serv_addr参数:服务器监听的socket地址;

addr_len参数:指定这个地址的长度;

connect成功返回0,一旦成功建立连接,sockfd就唯一标识了这个连接,客户端就可以通过读写sockfd来与服务器通信,

失败则返回-1,并设置errno,常见errno:

ECONNREFUSED:目标端口不存在,连接被拒绝。

ETIMEDOUT:连接超时。

7、关闭连接

通过关闭普通文件描述符的系统调用完成:

 fd参数是待关闭的socket

close系统调用并非总是立即关闭一个连接,而是将fd的引用计数减1,只有当fd为0时才真正关闭连接,

系统调用默认将使父进程中打开的socket的引用计数加1,因此必须在父进程和子进程中都对该socket执行close调用才能将连接关闭。

强制关闭:

 sockfd参数:待关闭的socket;

howto参数:

shutdown成功返回0,失败返回-1并设置errno。

8、数据读写

1.TCP数据读写

TCP数据流读写的系统调用:

recv读socket上数据,buf指定读缓冲区的位置,len指定读缓冲区的大小,flags参数通常为0;

recv成功时返回实际读取到的数据的长度,可能会小于预期长度,这时可能会多次调用recv才能得到完整数据;

recv返回0就意味着通信对方已经关闭连接;

recv出错时返回-1并设置errno。

send写socket上数据,buf指定写缓冲区的位置,len指定写缓冲区的大小;

send成功时返回实际写入数据的长度;

send失败时返回-1并设置errno。

flags参数为数据的收发提供额外的控制:

 MSG_OOB选项给应用程序提供了发送和接收带外数据的方法。

2.UDP数据读写

recvfrom读取sockfd上的数据,buf指定读缓冲区的位置,len指定读缓冲区的大小,

因为UDP通信没有连接的概念,所以每次读取数据都需要获取发送端的socket地址,即参数src_addr所指的内容,

addrlen指定src_addr地址的长度。

sendto往sockfd上写数据,buf指定读缓冲区的位置,len指定读缓冲区的大小,

dest_addr参数指定接收端的socket地址,

addrlen指定src_addr地址的长度。

flags和send/recv系统调用的flags一致,返回值也相同。

注意,如果奖最后两个参数都设置为NULL,那么他们也可以用于面向连接的数据读写。

3.通用数据读写函数

TCP和UDP通用的数据读写系统调用:

msghdr结构体定义:

msg_name成员指向一个socket地址,它指定通信双方的socket地址,

对于TCP协议,该成员变量无意义,必须被设置为NULL,因为对数据流socket来说,对方的地址已经知道。

msg_namelen成员指定msg_name所指socket的长度。

msg_ios成员是iovec结构体类型指针:

iovec结构体封装了一块内存的起始位置和长度,

msg_iovlen指定这样的iovec结构对象有多少个。

recvmsg的分散读(scatter read):数据被读取并存在msg_iovlen块分散的内存中,这些内存的位置和长度由msg_iov指向的数组指定。

sendmsg的集中读(gather write):msg_iovlen块分散内存中的数据将被一并发送。

msg_control和msg_controllen成员用于辅助数据的传送。

msg_flags成员无须设定,它会赋值recvmsg/sendmsg的flags参数的内容以影响程序读写过程,

recvmsg调用结束前会将某些更新后的标志设置到msg_flags中。

注,recvmsg和sendmsg的flags参数含义和recv、send一致。

 9、带外标记

内核通知应用程序带外数据到达的两种方式:I/O复用和SIGURG信号。

应用程序得到带外数据需接收的通知后,用sockatmark判断sockfd是否处于带外标记:

 即下一个被读取到的数据是否是带外数据

socktamark返回1,此时就可以用带MSG_OOB标志的recv调用来接收带外数据,

如果不是,则返回0。

10、地址信息函数

获取一个连接socket的本端socket地址,以及远端的socket地址:

getsockname获取sockfd对应的本端socket地址,并将其存储在address参数指定的内存,该socket地址的长度存储在address_len参数指向变量中,

如果实际长度大于address所指内存区的大小,则该socket地址将被截断,

getsockname成功返回0,失败返回-1并设置errno。

getpeername获取sockfd对应的远端socket地址,其他参数含义和 getsockname一致。

11、socket选项

fcntl系统调用是控制文件描述符熟悉的通用POSIX方法

两个专门用来读取和设置的两个系统调用:

sockfd参数:指定被操作的socket;

level参数:指定要操作哪个协议的选项(属性,如IPv4、IPv6、TCP等);

option_name参数:指定选项的名字(见下表);

option_value参数:被操作选项的值;

option_len参数:被操作选项的长度。

getsockopt和setsockopt成功返回0,失败返回-1并设置errno。

部分重要的socket选项实现: (后续补充2022.04.25记)

1.SO_REUSEADDR选项

2.SO_RCVBUF和SO_SNDBUF选项

3.SO_RCVLOWAT好SO_SNDLOWAT选项

4.SO_LINGER选项


12、网络信息API

1.gethostbyname和gethostbyaddr

gethostbyname函数根据主机名称获取主机的完整信息,gethostbyaddr函数根据IP地址获取主机的完整信息。

gethostbyname获取主机顺序:本地/etc/hosts配置文件中查找主机->DNS服务器。

两个函数的定义:

name参数:指定主机的主机名

addr参数:指定目标主机的IP地址

len参数:指定addr所指IP地址的长度

type参数:指定addr所指IP地址的类型(AF_INET和AF_INET6)

这两个函数的返回值都是hostent结构体类型的指针:

2.getservbyname和getservbyport

getservbyname函数根据名称获取某个服务的完整信息,getservbyport函数根据端口号获取某个服务的完整信息,

它们都是通过读取/etc/service文件来获取服务信息的,其结构体如下:

 注:

上面的4个函数都是不可重入的,即非线程安全的。

在netdb.h头文件给出了它们的可重入版本,即在它们的尾部加入 _r(re-entrant)

3.getaddrinfo

getaddrinfo函数即能通过主机名获取IP地址,也能通过服务名获取端口号(内部分别使用了gethostbyname和getservbyname)

getaddinfo定义:

 

 hostname参数:可以接收主机名,也可以接收字符表示的IP地址。

service参数:可以接收服务名,也可以接收字符串表示的十进制端口号。

hints参数:应用程序给getaddrinfo的一个提示,以对getaddrinfo的输出进行更精确的控制。hists的参数为NULL时,表示允许getaddrinfo反馈任何可用的结果。

result参数:指向一个链表,该链表用于存储getaddrinfo反馈的结果。

getaddrinfo反馈的每一条结果都是addrinfo结构体类型的对象,其定义如下:

 使用hints参数的时候,可设置ai_flags、ai_family、ai_socktype、ai_protocol四个字段,其他字段必须设置为NULL

由于getaddrinfo会隐式地分配内存,所以必须使用配套的一个函数来释放其分配的内存:

4.getnameinfo

getnameinfo函数能通过socket地址同时获取以字符串表示的主机名和服务名(分别使用了gethostbyaddr和getservport)

getnameinfo函数定义如下:

 getnameinfo将返回的主机名存储在艾host参数指向的缓存中,

将服务名存储在serv参数指向的缓存中,

hostlen和servlen参数分别指定这两块缓存的长度,

flags参数表:

 getaddrinfo和genameinfo函数成功时返回0,失败时返回错误码:

 Linux下的strerror函数能将数值错误码errno转换成易读的字符串,下面的函数也可将上表中的错误码转换成其字符串形式:

 

 二、高级I/O函数

1、pipe函数

2、dup函数和dup2函数

3、readv函数和writev函数

4、sendfile函数

5、mmap函数和munmap函数

6、splice函数

7、tee函数

8、fcntl函数

****参考说明****

《Linux高性能服务器编程》--游双

 

posted @ 2022-04-25 16:35  NK-cat  阅读(215)  评论(0编辑  收藏  举报