25、socket(套接字)

概念
  • sockt主要用于 网络中进程间数据的传递,可以看成是应用在通信时的逻辑端点

  • socket会在背后调用TCP/IP协议,帮我们完成一系列复杂的操作;而使用时 只需要传递 IP地址和端口号 即可。也就是说,socket衔接了 应用层 和 下面的传网数 层

  • socket本质上也是一个文件,有自己的fd。我们就是通过 fd 向一端的socket中写入数据(写缓冲区),然后在另一端的socket中读取数据(读缓冲区)

  • 套接字通信分两部分:

    • 服务器端:被动接受连接,一般不会主动发起连接
    • 客户端:主动向服务器发起连接
字节序
  • 字节序就是 字节存储的顺序,最小单元是字节。1字节的数据只有一个字节序

  • 类型

    • 小端字节序:存储的字节序和实际的字节序相反,应用较多
    • 大端字节序:存储的字节序和实际的字节序相同
    • 即 大端的存储时,字节间的顺序没有发生改变;小端存储时,字节间的顺序发生了改变

    (官方:大端字节序是指 整数的 高位字节 存储在 内存的低地址处,低位字节 存储在 内存的高地址处;小端字节序则相反)

  • 发送方总是 把要发送的数据转换 大端字节序,如果接收方也是大端字节序,那么就可以直接读取了;但如果接收方是 小端字节序,则还需要把收到的数据 转换成小端字节序后 再读取

  • 网路字节序 都是大端,而主机字节序有大端有小端;通信过程中 字节序的变化:主机A字节序 ->网络字节序(必为大端) ->主机B字节序

字节序转换函数
h  - host 主机,主机字节序
to - 转换成什么
n  - network  网络字节序
s  - short (2字节,用于转换端口)
l  - long (4字节,用于转换IP)
#include <arpa/inet.h>
// 转换端口
uint16_t htons(uint16_t hostshort); // 主机字节序 -> 网络字节序
uint16_t ntohs(uint16_t netshort); // 网络字节序 -> 主机字节序
// 转IP
uint32_t htonl(uint32_t hostlong); // 主机字节序 -> 网络字节序
uint32_t ntohl(uint32_t netlong); // 网络字节序 -> 主机字节序

实例:

#include <stdio.h>
#include <arpa/inet.h>
int main() {

    // htons 转换端口
    unsigned short a = 0x0102;
    printf("a : %x\n", a);
    unsigned short b = htons(a);
    printf("b : %x\n", b);

    // htonl  转换IP
    char buf[4] = {192, 168, 1, 100};
    int num = *(int *)buf;
    int sum = htonl(num);
    unsigned char *p = (char *)&sum;
    printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3));

    // ntohl
    unsigned char buf1[4] = {1, 1, 168, 192};
    int num1 = *(int *)buf1;
    int sum1 = ntohl(num1);
    unsigned char *p1 = (unsigned char *)&sum1;
    printf("%d %d %d %d\n", *p1, *(p1+1), *(p1+2), *(p1+3));

    return 0;
}
socket地址
  • socket地址 其实是一个结构体,是端口号、IP地址等信息的封装。

  • 分类:

    • 通用 socket 地址(旧)
#include <bits/socket.h>
struct sockaddr {
 sa_family_t sa_family;//表示 地址族的类型
 char        sa_data[14];//最大只有14字节,存不下PF_INET6
};

typedef unsigned short int sa_family_t;
  • 地址族类型通常与协议族类型对应:

    在这里插入图片描述

    ​ (宏 PF_* 和 AF_* 都定义在 bits/socket.h 头文件中,两者完全一致,可混用)

  • sa_data[14] 用于存放 socket 地址值,不同的协议族 具有不同的地址值含义和长度
    在这里插入图片描述

  • 通用 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) ];//空间更大,能够存下PF_INET6
};

typedef unsigned short int sa_family_t;
  • 专用 socket 地址

    • sockaddr_in 就是 sockaddr 的专用地址,更加的方便。开发的时候基本用的就是专用 socket 地址(sockaddr_in)。在作为参数时,只需将类型转换为 原来的通用类型(sockaddr)即可

    在这里插入图片描述

    • sockaddr_in 和 sockaddr_in6 分别用于 IPv4 和 IPv6:
  #include <netinet/in.h>
  struct sockaddr_in
  {
      sa_family_t sin_family; //值就是PF_INET
      in_port_t sin_port;     //表示 端口号(2字节)
      struct in_addr sin_addr;   //结构体类型,表示 IP地址(4字节)
      
      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;//s_addr其实是int类型,占4个字节
  };
  
  struct sockaddr_in6
  {
      sa_family_t sin6_family;//值就是PF_INET6
      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))
IP地址转换函数
  • 作用:

    1. 将IP地址从 字符串类型 转换为 整数类型
    2. 将 主机字节序 转换为 网络字节序
  • 像192.168.1.1这样的IP地址 都是字符串类型的,我们需要把它们转换成 整数类型,才能代入socket地址;而日志则需要记录字符串的IP地址,所以需要将 整数型IP 转换成 字符串型IP

  #include <arpa/inet.h>
  
  // 字符串 -> 整数
  int inet_pton(int af, const char *src, void *dst);
   	af: 表示地址族,如 AF_INET  AF_INET6
      src: 表示字符串型IP
      dst: 传出参数,转换后的结果保存在这个里面 (可带入int型地址)
          
  // 整数 -> 字符串
  const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
   	af: 表示地址族,如 AF_INET  AF_INET6
      src: 表示整数型IP的地址
      dst: 传出参数,转换后的结果保存在这个里面
      size: dst的大小(即数组的大小)
   	返回值:和dst的内容完全相同
  • 实例:
  #include <stdio.h>
  #include <arpa/inet.h>
  int main() {
      
  	// 将点分十进制的IP字符串转换成网络字节序的整数
      char buf[] = "192.168.1.4";
      unsigned int num = 0;
      inet_pton(AF_INET, buf, &num);
      unsigned char * p = (unsigned char *)&num;
      printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3));
  
  
      // 将网络字节序的IP整数转换成点分十进制的IP字符串
      char ip[16] = "";
      const char * str =  inet_ntop(AF_INET, &num, ip, 16);
      printf("str : %s\n", str);
      printf("ip : %s\n", str);
      printf("%d\n", ip == str);
  
      return 0;
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值