套接字和地址

socket

socket是什么

在网络编程中,到底应该怎么理解 socket 呢?我在这里先呈上这么一张图,你可以先看看。

 先从右边服务端看,因为在客户端发起连接请求之前,服务器端必须初始化好。

上边的图显示的是服务器端初始化的过程;

1.首先初始化socket

2.服务器端需要执行bind函数,将自己的服务能力绑定在一个众所周知的端口上

3.服务器端执行listen操作,将原先的socket 转化为服务端的 socket

4.服务端最后阻塞在 accept上 等待客户端请求的到来。

客户端过程:

1.同样先初始化socket

2.再执行connect向服务器端 的地址和端口发起连接请求,这里的地址和端口必须是客户端预先知晓的。这个过程,就是 著名的TCP 三次握手(Three-way Handshake)

一旦三次握手完成,客户端和服务器端建立连接,就进入了数据传输过程。

数据传输过程:

客户端进程向操作系统内核发起 write 字节流写操作

内核协议栈将字节流通过 网络设备传输到服务器端

服务器端从内核得到信息,将字节流从内核读入到进程中,并开始业务逻辑的处理

完成之后,服务器端再将得到的结果以同样的方式写给客户端

一旦连接建立,数据的传输就不再是单向的,而是双向的,这也是 TCP 的一个显著特 性。

这幅图的真正用意在于引入 socket 的概念,请注意,以上所有的操作,都是通过 socket 来完成的。无论是客户端的 connect,还是服务端的 accept,或者 read/write 操 作等,socket 是我们用来建立连接,传输数据的唯一途径。

一个更直观的解释:

TCP网络交互和数据传输想象成打电话,socket就是电话本机,客户端的connect就是拨号码,而服务器端的bind 好比去移动公司开卡,将电话号和我们绑定。这样别人就可以拨打电话号找到你了,而listen就是听到电话的响铃,accep好比听到响铃拿起电话回应"喂"。链接建立完毕。

客户端:你好--》write       服务端: 听到对面说你好的过程想象成read(听到并读出数)并开始应答

双方进入read/write的数据传输过程。

最后,客户端拨打电话的人完成了这次通话,挂断。对应操作就是close。接听的服务端听到"嘟嘟嘟"声音,便知道对面挂断,随即也关闭电话,也是一次close

在整个电话交流过程中,电话是我们可以和外面通信的设备,对应到网络编程的世界里, socket 也是我们可以和外界进行网络通信的途径。

套接字:

使用套接字时,首先要解决通信双方寻址的问题。套接字的地址建立连接,就像 打电话时首先需要找的通讯录,找到你想练习的人,你才可以建立链接-----交流。

套接字地址格式:


/* POSIX.1g 规范规定了地址族为2字节的值.  */
typedef unsigned short int sa_family_t;
/* 描述通用套接字地址  */
struct sockaddr{
    sa_family_t sa_family;  /* 地址族.  16-bit*/
    char sa_data[14];   /* 具体的地址值 112-bit */
  }; 

在这个结构体里,第一个字段是地址族,它表示使用什么样的方式对地址进行解释和保存, 好比电话簿里的手机格式,或者是固话格式,这两种格式的长度和含义都是不同的。地址族 在 glibc 里的定义非常多,常用的有以下几种:

  • AF_LOCAL: 表示的是本地地址,对应的是 Unix 套接字,这种情况一般用于本地 socket 通信,很多情况下也可以写成 AF_UNIX、AF_FILE;
  • AF_INET: 因特网使用的 IPv4 地址;
  • AF_INET6: 因特网使用的 IPv6 地址

这里的 AF_ 表示的含义是 Address Family

但是很多情况下,我们也会看到以 PF_ 表示的 宏,比如 PF_INET、PF_INET6 等,实际上 PF_ 的意思是 Protocol Family,也就是协议族 的意思。

我们用 AF_xxx 这样的值来初始化 socket 地址,用 PF_xxx 这样的值来初始化 socket。我们在 头文件中可以清晰地看到,这两个值本身就是一一对应 的。


/* 各种地址族的宏定义  */
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL  PF_LOCAL
#define AF_UNIX   PF_UNIX
#define AF_FILE   PF_FILE
#define AF_INET   PF_INET
#define AF_AX25   PF_AX25
#define AF_IPX    PF_IPX
#define AF_APPLETALK  PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25    PF_X25
#define AF_INET6  PF_INET6

IPV4套接字格式地址

常用的 IPv4 地址族的结构:


/* IPV4套接字地址,32bit值.  */
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };
  
/* 描述IPV4的套接字地址格式  */
struct sockaddr_in
  {
    sa_family_t sin_family; /* 16-bit */
    in_port_t sin_port;     /* 端口号  16-bit*/
    struct in_addr sin_addr;    /* Internet address. 32-bit */


    /* 这里仅仅用作占位符,不做实际用处  */
    unsigned char sin_zero[8];
  };

地址族: 和 sockaddr 一样,都有一个 16-bit 的 sin_family 字段。

端口号: 端口号最多是 16-bit,也就是说最大支持 2 的 16 次方,这个数字是 65536,所以我们应该知道支持寻址的端口号最多就是 65535。

保留端口: 就是大家约定俗成的,已经被对应服务广为使用的端口,比如 ftp 的 21 端口,ssh 的 22 端口,http 的 80 端口等。

IPv4使用32位(4字节)地址,因此地址空间中只有4,294,967,296个地址,随着地址不断被分配这个数字渐渐显得不太够用了,于是大家所熟知的 IPv6 就隆重登场 了。

IPv6 套接字地址格式

IPv6 的地址结构:


struct sockaddr_in6
  {
    sa_family_t sin6_family; /* 16-bit */
    in_port_t sin6_port;  /* 传输端口号 # 16-bit */
    uint32_t sin6_flowinfo; /* IPv6流控信息 32-bit*/
    struct in6_addr sin6_addr;  /* IPv6地址128-bit */
    uint32_t sin6_scope_id; /* IPv6域ID 32-bit */
  };

流控信息和域 ID 先不用管,这两个字段,一个在 glibc 的官网上根本没出现,另一个是当前未使用的字段。

地址族: AF_INET6;

端口: 同 IPv4 地址一样;

地址: sin6_addr从 32 位升级到 128 位,完全解决了寻址数字不够的问题。

注意: 无论IPv4 还是 IPv6 的地址格式都是因特网套接字的格式

本地套接字—AF_LOCAL

用来做为本地进程间的通信, 也就是前面提到的 AF_LOCAL。


struct sockaddr_un {
    unsigned short sun_family; /* 固定为 AF_LOCAL */
    char sun_path[108];   /* 路径名 */
};

套接字地址格式比较

IPv4 和 IPv6 套接字地址结构的长度是固定的,而本地地址结构的长度是可变的。

(因为本地地址的文件路径是不同的)

1. IPv4、IPv6、本地套接字格式以及通用地址套接 字,它们有什么共性呢?

共性:跟sock_addr结构体里描述那样,这些套接字都需要地址族和地址俩个字段。(与外部通信,至少告诉计算机对方的地址和使用的是哪一种地址。与远程计算机通信还需要一个端口号。)

本地socket不需要端口号

2.为什么本地套接字格式不需要端口号,而 IPv4 和 IPv6 套接字格式却需要端口号呢?

  • 本地socket本质上是在访问本地的文件系统,所以自然不需要端口;
  • 远程socket是直接将一段字节流发送到远程计算机的一个进程,而远程计算机可能同时有多个进程在监听,所以用端口号标定要发给哪一个进程。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值