网络编程-socket相关概念

字节序

了解网络编程需要先了解计算机通信中字节的存储顺序

字节序概念

考虑32位机,CPU累加器一次能装载4字节。这4字节在内存的排列顺序影响被累加器装载成的整数值。
字节序分为大端字节序(Big-Endian)和小端字节序(Little-Endian)。发送端总是要把数据转换位大端字节序发送

  • 大端字节序:指整数的高位字节存储在内存的低地址处,也称网络字节序
  • 小端字节序:指整数的高位字节存储在内存的高地址处,现代PC大多采用的方式

假设内存增加方向从左往右,12345678在内存中的存储格式如下

  • 在大端字节序中的存储是 0x12 0x34 0x56 0x78,12是数的高位,存储在内存地址的低位
  • 在小端字节序中的存储是 0x78 0x56 0x34 0x12,12是数的高位,存储在内存地址的高位

字节序转换函数

字节序转换函数封装在BSD Socket中,头文件是#include <arpa/inet.h>

  1. 转换端口
  • uint16_t htons(uint16_t hostshort); // 主机字节序 - 网络字节序
  • uint16_t ntohs(uint16_t netshort); // 网络字节序 -主机字节序
  1. 转换IP
  • uint32_t htonl(uint32_t hostlong); // 主机字节序 - 网络字节序
  • uint32_t ntohk(uint32_t netlong); // 网络字节序 -主机字节序

其中

  • h - host 主机,主机字节序
  • to - 转换成什么
  • n - network 网络字节序
  • s - short unsigned short
  • l - long unsigned int

socket介绍

socket用于不同主机间进程的通信
socket包含IP地址和端口
socket通信分为服务器端和客户端

socket套接字,是对网络上不同主机上的应用进程之间进行双向通信的断点的抽象。套接字就是网络上进程通信的一端,提供应用层进程利用网络协议交换数据的机制

socket 可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的 API,也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。

在 Linux 环境下,socket用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。可以用文件描述符引用。

socket地址

socket地址其实是一个结构体,封装端口号和IP等信息

socket地址包括通用socket地址和专用socket地址

通用socket地址

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

#include <bits/socket.h>
struct sockaddr {
    
    
    sa_family_t sa_family;
    char sa_data[14];
};
typedef unsigned short int sa_family_t;

sa_family 成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。常见的协议
族(protocol family,也称 domain)和对应的地址族入下所示:
在这里插入图片描述
宏 PF_* 和 AF_* 都定义在 bits/socket.h 头文件中,且后者与前者有完全相同的值,所以二者通常混
用。
sa_data 成员用于存放 socket 地址值。但是,不同的协议族的地址值具有不同的含义和长度,如下所
示:
在这里插入图片描述
14 字节的 sa_data 根本无法容纳多数协议族的地址值。因此,Linux 定义了下面这个新的
通用的 socket 地址结构体,这个结构体不仅提供了足够大的空间用于存放地址值,而且是内存对齐的。

#include <bits/socket.h>
struct sockaddr_storage
{
    
    
    sa_family_t sa_family;
    unsigned long int __ss_align;
    char __ss_padding[ 128 - sizeof(__ss_align) ];
};
typedef unsigned short int sa_family_t;

专用socket地址

很多网络编程函数诞生早于 IPv4 协议,那时候都使用的是 struct sockaddr 结构体,为了向前兼容,现在sockaddr 退化成了(void *)的作用,传递一个地址给函数,至于这个函数是 sockaddr_in 还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型。

在这里插入图片描述
通用socket结构体并不好用。在设置和获取IP地址和端口号的时候都需要执行繁琐的位操作。因此Linux为各个协议族提供了专用的socket结构体。

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

#include <sys/un.h>
struct sockaddr_un
{
    
    
    sa_family_t sin_family;
    char sun_path[108];
};

TCP/IP 协议族有 sockaddr_in 和 sockaddr_in6 两个专用的 socket 地址结构体,它们分别用于 IPv4 和IPv6:

#include <netinet/in.h>
struct sockaddr_in
{
    
    
    sa_family_t sin_family; /* __SOCKADDR_COMMON(sin_) */
    in_port_t sin_port; /* Port number. */
    struct in_addr sin_addr; /* Internet address. */
    /* Pad to size of `struct sockaddr'. */
    unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE -
    sizeof (in_port_t) - sizeof (struct in_addr)];
};
struct in_addr
{
    
    
    in_addr_t s_addr;
};
struct sockaddr_in6
{
    
    
    sa_family_t sin6_family;
    in_port_t sin6_port; /* Transport layer port # */
    uint32_t sin6_flowinfo; /* IPv6 flow information */
    struct in6_addr sin6_addr; /* IPv6 address */
    uint32_t sin6_scope_id; /* IPv6 scope-id */
};
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))

值得注意的是:

  1. 这些专用socket结构体里的端口号都是用网络字节序表示的。
  2. 所有专用socket地址(以及sockaddr_storage)类型的变量都需要在实际使用时转化为通用socket地址类型sockaddr(直接强制转换),因为所有socket编程API用的地址参数的类型都是sockaddr。

猜你喜欢

转载自blog.csdn.net/MinutkiBegut/article/details/114265869
今日推荐