linux高性能服务器编程(跳过TCP/IP协议详解) ---------更新中

linux高性能服务器编程

深入解析高性能服务器编程

(首更,2021年11.28)

一.linux网络编程基础API

1.什么是字节序列:

现代CPU的累加器一次都能装载8字节的数据,这八字节在内存中排列的顺序将影响到它被累加器装载成长整型/浮点型/异质数据结构的值,这就是字节序列,字节序分为大端字节序和小端字节序,大端字节序就是指一个浮点/长整型的高位字节存储在机器内存中的低地址处,而小端字节序的高位字节存储在机器内存中的高地址处。(低位字节相反)

2.什么是主机字节序?什么是网络字节序?

现代PC大多采用小端法,所以小端字节序又叫主机字节序。

ps:问题引入:

当两个都是小端字节序的主机互相直接传递数据(流式传输),必然会错误的解释流向自己的数据。怎样才能避免都是小端字节序带来的错误解释 ?

解决方法:
发送端将数据转换成大端字节序数据之后发送,而接收端知道对端传送过来的数据总是采用大端字节序,所以接收端可以根据自身采用的字节序决定是否对接收到的数据进行转换,(大端机器转换,小端机器不转换)由此可以正确的在网际直接传输信息。

所以大端法又叫网络字节序。

3.LINUX os提供了如下四个函数来完成主机字节序和网络字节序之间的转换:

#include<netinet/in.h> //库文件名

unsigned long int htonl(unsigned long int hostlong) //host to net long 主机字节序转换网络字节序,比如长整型的IP 地址

unsigned long int ntohl(unsigned long int netlong) //net to host long 网络字节序转换主机字节序,比如长整型的IP地址

usigned short int htons(unsigned short int hostlong) //host to net short 主机字节序转换网络字节序,比如短整形的端口号

usigned short int ntohs(unsigned short int nettlong) //net to host short 网络字节序转换主机字节序,比如短整形的端口号

4.通用套接字地址

socket网络编程接口中表示socket地址的是结构体sockaddr,其定义如下:

#include<bits/socket.h>

 struct sockaddr

{

sa_family_t sa_family;      //sa_family是地址族类型

char sa_data[14];         //存放socket地址值

 }
4.1 sa_family成员:

地址族类型和协议族对应,常见协议族(protocol family也称为domain)和对应的地址族如下

协议族地址族描述
PF_UNIXAF_UNIXUNIX本地域协议族
PF_INETAD_INETTCP/IPV4协议族
PF_INET6AF_INET6TCP/IPV6协议族

PF与AF都存在bits/socket.h头文件中,两者有完全相同的值,故可混用。

4.2 sa_data[14]成员:

用于存放socket地址,但是不同协议族的地址长度和含义不同:

协议族地址值含义和长度
PF_UNIX文件的路径名,长度可达到108字节
PF_INET16bit的端口号和32BIT的IPV4地址,共6字节
PF_INET616BIT端口号,32bit流标识,128bit IPV6地址,32bit范围ID 共26字节

可以看到sa_data[14]成员只有14个字节,在UNIX本地协议和IPV6协议族中不能达到存储的条件,所以linux os 定义了如下通用的socket地址结构体:

#include<bits/socket.h>

 struct sockaddr_storage

{


     sa_family_t sa_family;      //sa_family是地址族类型

     unsigned long int __ss_align //内存对齐
     
     char __ss_padding[128_sizeof(__ss_slign)]//存储socket地址


 }
5.专用socket地址

如上这两个通用socket结构体显然不好用,比如在设置与获取IP地址和端口号的时候需要执行繁琐的位操作,所以linux专门为各个协议族提供了专门的socket地址结构体.

unix本地域协议族使用如下专用socket地址结构体

#include<sys/un.h>

 struct sockaddr_un

{

sa_family_t sa_family;      //sa_family是地址族类型

char sun_path[108];         //文件路径名

 }

ipv4使用入下结构体:

struct sockaddr_in
{
    sa_family_t sin_family;  //地址族
    u_int16_t sin_port;   //端口号 用网络字节序表示
    struct in_addr sin_addr;//ipv4地址结构体
}
struct in_addr
{
    u_int32_t s_addr;//ipv4地址,用网络字节序表示
}

ipv6使用如下结构体

struct sockaddr_in16
{
    sa_family sin6_family;//地址族
    u_int16_t sin6_port;//端口号,用网络字节序表示
    u_int32_t sin6_flowwinfo;//流信息,设置为0
    struct in6_addr sin6_addr;//ipv6地址结构体,
    u_int32_t sin6_scope_id;//scope ID 现在处于实验阶段
}
struct in6_addr {
    unsigned char sa_addr[16];//ipv6地址,用网络字节序表示
}

所有专用socket地址与sockaddr_storage类型的变量在使用中都必须转换为通用socket地址类型sockaddr(强转即可),因为所有socket 编程接口使用的地址参数的类型都是sockaddr。

6.IP地址转换函数

通常人们使用点分十进制字符串或者16进制字符串来表示ipv4地址,但是在编程中我们需要先把它们转化为整数(二进制数)方能使用,而记录日志却相反,我们需要把整数表示的IP转化为可读的字符串,下面的三个函数课用于点分十进制字符串表示的IPv4地址和用网络字节序整数表示的ipv4地址之间的转换:

#include<arpa/inet.h>
 in_addr_t inet_addr(const char* strptr);   //点分十进制ip---->网络字节序整数ip 失败返回INADDR_NONE

 int inet_aton(const char *cp,struct in_addr* inp);//点分十进制ip----->网络字节序整数IP,将得到的IP存贮于inp指向的地址,失败返回0 ,成功返回1

char* inet_ntoa(struct in_addr in);

inet_ntoa函数用网络字节序整数表示的IPV4地址转换成点分十进制字符串IP地址。该函数内部用一个静态变量存储转化结果,函数的返回值指向该静态内存,因此inet_ntoa是不可重入的。

char *szValue1=inet_ntoa("1.2.3.4");
char *szValue2=inet_ntoa("10.194.71.60");
printf("address1:%s\n",szValue1);
printf("address2: %s\n",szValue2);

运行结果:

address1:1.2.3.4
address2:10.194.71.60

下面这对函数能完成以上三个函数的所有功能,且同样适用IPV4与ipv6地址

#include<arpa/inet.h>
int inet_pton(int af,const char* src,void *dst)//字符串转网络字节序整数
const char* inet_ntop(int af,const void src,char *dst,socklen_t cnt)//网络字节序整数转字符串

inet_pton函数中af参数表示协议族,src表示传入的字符串ip地址,转化后的字节序整数存储在dst指针指向的内存中。(成功返回1,失败返回0,设置errno)

inet_ntop函数中源操作,目标操作与pton相反,cnt表示制定目标存储单元的大小用此区分ipv4和ipv6(成功返回目标存储单元的地址,失败返回NULL并设置ERRNO)

7.创建socket

linux/unix关于系统中任何部分都可以抽象为文件,文件都是可读写,可打开关闭,可控制的文件,socket也不例外。下面用一个socket系统调用创建一个socket

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);

socket函数成功创建套接字后返回1,失败返回0并且设置ERRNO,

domain参数说明套接字使用什么协议族,若是TCP/IP协议族,则可设置为PF_INET(IPV4)或者PF_INET6(IPV6),若是unix本地域协议族,则设置为PF_UNIX协议。

type参数表明服务类型,在TCP/IP中有两种传输协议:TCP流式传输,设置为SOCK_STREAM, UDP数据报文传输,设置为SOCK_DGRAM.

protocol参数是在前两个参数构成的协议集合下,再选择一个具体的协议,不过这个值通常唯一,在前面两个参数已经确定的情况下,应该把本参数设置为0,表示使用默认前两个参数的协议。

8.命名socket(待更新)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅得不谈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值