网络编程--套接字

套接字(Socket)的概念


套接字: 是系统内核中的一种数据结构,也是网络间进程之间一种通信机制,同时也是** I/O 文件描述符**。
在利用套接字进行网络通信时,套接字是如何唯一确定网络中的进程的呢?
在网络中,每一个主机可以通过 IP 地址唯一确定,主机内的进程可以用进程ID 唯一确定,但是套接字使用的不是 进程ID ,使用的是 端口号。在主机内,端口号进程ID 是一一对应的关系,每一个进程都被分配了一个唯一性的端口号


套接字有两种格式:

  • 半相关描述的套接字格式为:{协议,本地IP,本地端口}
  • 完全相关描述的套接字格式为:{协议、本地IP,本地端口,对方IP,对方端口}

其中协议主要分为三种:

  1. 流式Socket(SOCK_STREAM): 用于 TCP 通信。流式套接字提供面向连接的可靠的通信流;
  2. 数据报Socket(SOCK_DGRAM): 用于 UDP 通信。数据报套接字提供无连接不可靠的数据传输服务;
  3. 原始Socket(SOCK_RAW): 用于新网络协议的测试。

Socket 通信在实现过程中,也有一个类似于打开文件的函数,它能返回一个整型的 Socket 描述符,我们利用这个 Socket 描述符实现网络连接、数据传输等操作。


Socket数据结构


Socket 的信息数据结构有两种,如下所示:

  • 通用型Socket信息结构:
struct sockaddr {
    unsigned short sa_family; // 地址族
    char sa_data[14];    //14字节协议地址,保存 Socket 的 IP 地址和端口号
};
  • 具体型Socket信息结构:
struct sockaddr_in {
    short int sa_family;    // 地址族
    unsigned short int sin_port;    // 端口号
    struct in_addr sin_addr;    // IP地址
    unsigned char sin_zero[8];    // 填充0以保持和 struct sockaddr 一致
};
struct in_addr {
    unsigned long int s_addr;    // 32为 IPv4地址,网络字节序
};

在头文件 <netinet/in.h> 中,地址族可以取如下值:

  1. sa_family::AF_INET 表示 IPv4 协议;
  2. sa_family::AF_INET6 表示 IPv6 协议。

通用型Socket的数据结构 可以和 具体型Socket的数据结构 相互转化。


数据存储方式


计算机数据存储有两种方式:

  1. 大端模式: 高字节优先;内存的高地址存放数据的低字节,内存的低地址存放数据的高字节;
  2. 小端模式: 低字节优先:内存的高地址存放数据的高字节,内存的低地址存放数据的低字节;
    例如,对于数据 0 x 12345678 0x12345678 0x12345678 而言,如果采用 大端模式 存放,则真实的数为: 0 x 12345678 0x12345678 0x12345678;如果采用 小端模式 存放,则真实的数为:0x78563412

在这里插入图片描述

  • 系统中存放数据的字节序称为主机字节序主机字节序有可能采用小端模式,也有可能采用 大端模式。但是,
  • 网络中流动的数据称为:网络字节序,网络字节序采用的是大端模式

当数据从主机发送到网络时,需要进行 字节序转化,Linux 提供了四个函数用于转化 字节序

  1. htons()主机字节序网络字节序 的转化;用于端口转换。
  2. ntohs()网络字节序主机字节序 的转化;用于端口转换。
  3. htonl()主机字节序网络字节序 的转化;用于 IP 转换。
  4. ntohl()网络字节序主机字节序 的转化;用于 IP 转换。

其中,各个简写的含义如下:

  • h 表示 host
  • n 表示 network
  • s 表示 short,表示 16 16 16 位的端口号;
  • l表示long`,表示 32 32 32 位的 IP 地址。

在这里插入图片描述


IP 点分十进制与二进制的相互转化


把 IP 点分十进制二进制 进行相互转换。

  • 点分十进制 的 IP 转化为 网络字节序二进制
  • inet_addr(const char *cp)
  • inet_aton(const char *cp, struct in_addr *inp)
    两个函数已经包含了 主机字节序网络字节序 的转化


在这里插入图片描述

  • 二进制 IP 转化为 点分十进制 的 IP:
    • inet_ntoa(struct in_addr in)
      这个函数包含了 主机字节序网络字节序 的转化。

在这里插入图片描述

其中,IPv4 和 IPv6 兼容的函数有 inet_pton()inet_ntop()


例子:网络地址与二进制地址的转化

// ip_addr.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
     char ip[] = "192.168.0.101"; // 点分十进制的 IP
     struct in_addr myaddr; // 存储 IP 的二进制
     /*inet_aton*/
     int iRet = inet_aton(ip, &myaddr); // 把 IP 的点分十进制转化为 二进制
     printf("%x\n", myaddr.s_addr);
	
     /*inet_addr*/
     printf("%x\n", inet_addr(ip)); // 把 IP 的点分十进制转化为 二进制
	
     /*inet_pton*/
     iRet = inet_pton(AF_INET, ip, &myaddr); // 把 IP 的点分十进制转化为 二进制
     printf("%x\n", myaddr.s_addr);
     myaddr.s_addr = 0xac100ac4;
	
     /*inet_ntoa*/
     printf("%s\n", inet_ntoa(myaddr)); // 把 IP 的二进制转化为 点分十进制格式
	
     /*inet_ntop*/
     inet_ntop(AF_INET, &myaddr, ip, 16); // 把 IP 的二进制转化为 点分十进制格式
     puts(ip);
     return 0;
}

域名与IP地址相互转化


一般使用 主机名域名 来代替 IP 地址的表达。
主机名域名 的区别为:

  • 主机名 通常在局域网里面使用,通过 /etc/hosts 文件可以将主机名解析到对应的 IP;
  • 域名 通常在 Internet 上使用,例如:www.baidu.com

在 Linux 一般使用 gethostbyname() 将主机名转化为 IP 地址;使用 gethostbyaddr() 将 IP 地址转化为主机名。这两个函数在 IPv4 和 IPv5 中都适用。函数原型如下:

  • gethostbyname 函数:用于将域名( www. baidu. com)或主机名转换为 IP 地址,参数 hostname 指向存放域名或主机名的字符串;
  • gethostbyaddr 函数:用于将 IP 地址转换为域名或主机名,
    • 参数 addr 是 一个 IP 地址, 此时这个 IP 地址不是普通的字符串, 而是要通过函数 inet_ aton 转换 的;
    • len 为 IP 地址 的 长度, AF_ INET 为 4;
    • family 可用 AF_ INET: IPv4AF_ INET6: IPv6

在这里插入图片描述

结构体为:

在这里插入图片描述

例子:由域名获取主机的相关信息

// name_to_ip.c
// transform www.baidu.com to ip
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    char *ptr = NULL, **pptr = NULL;
    struct hostent *hptr = NULL;
    char str[32] = {'\0'};

    ptr = argv[1]; // 域名: www.baidu.com
    if((hptr = gethostbyname(ptr)) == NULL) {
        printf("gethostbyname error for host:%s\n", ptr);
        return 0;
    }
    printf("official hostname: %s\n", hptr->h_name); // 打印主机名
    /*主机可能有多个别名,将所有别名分别打印出来*/  
    for(pptr = hptr->h_aliases; *pptr != NULL; pptr++)
        printf("alias:%s\n", *pptr);
    /*根据地址类型,将地址打印出来*/
    switch (hptr->h_addrtype)
    {
    case AF_INET:
    case AF_INET6:
        pptr = hptr->h_addr_list; // 指向ip地址列表
        /*将刚才得到的所有地址都打印出来, 其中调用了inet_ntop函数*/
        for(; *pptr != NULL; pptr++) {
            inet_ntop(hptr->h_addrtype, (void *)*pptr, str, sizeof(str)); // 把二进制ip转化为点分十进制
            printf("address: %s\n", str);
        }
        break;
    
    default:
        printf("unknown address type \n");
    }
    return 0;
}

运行结果如下:

$ gcc name_to_ip.c -o name_to_ip
$ ./name_to_ip www.baidu.com
official hostname: www.a.shifen.com
alias:www.baidu.com
address: 14.215.177.38
address: 14.215.177.39

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值