文章目录
前言
socket套接字是一个重要的,独立于协议的网络编程接口,可实现不同主机上应用程序间的双向通信,是应用程序通过网络协议进行通信的接口。
在正式学习socket套接字之前,需要掌握一些网络编程的基础知识,例如:服务器-客户端模、OSI模型、TCP/UDP协议基础。
一、网络编程基础
1.1 服务器-客户端模型
- 网络通信一般会以客户端和服务器的形式出现,客户端通过应用协议向服务器发起请求,服务器接收到请求后应答客户端,如下图所示是服务器-客户端的基本模型。
- 通常客户端每次只与一个服务器通信,特殊情况下也可以与多个不同的服务器通信。
- 客户端与服务器之间一般通过网络协议通信,例如Web服务器与客户端之间通信就是通过TCP(Transmission Control Protocol, 传输控制协议)协议来实现,TCP协议使用IP(Internet, 网际协议)进行通信,IP则通过数据链路层通信。
客户端与服务器之间采用应用协议通信,客户端与服务器之间的数据流向下通过TCP/IP(内核协议栈),数据链路层后,跨越网络,在另一端向上通过协议栈。客户端和服务器均是用户进程,TCP/IP协议则是内核中的协议栈的一部分。
1.2 OSI模型
如下图所示是国际标准化组织ISO提出的计算机通信开放系统互连模型OSI(open systems interconnection, OSI),该模型是一个七层模型结构,其与网际协议层级的映射关系如图中所示。
接下来我们逐一分析各协议层的映射关系:
- OSI模型的物理层与数据链路层对应于网际协议族的设备驱动程序和网络硬件;
- 网络层一般采用IPv4和IPv6两个协议处理;
- 传输层可采用TCP或UDP协议处理,此外应用层也可以直接绕过传输层直接使用IPv4或IPv6(即使用原始套接字);
- OSI模型的顶上三层(应用层+表示层+会话层)对应于网际协议族的应用层。
1.3 TCP协议基础
1.3.1 基本概念
传输控制协议(Transmission Control Protocol, TCP),是一个面向连接的协议,为用户进程提供可靠的全双工字节流。TCP套接字是一种流套接字。TCP可实现确认、超时重传等。
TCP提供可靠的数据传输,TCP向另一端发送数据时,它要求对端返回确认,在未收到确认的情况下,则TCP自动重传数据等待更长时间。
- TCP提供一个动态估算客户端和服务器之间往返时间RTT算法,可以评估等待一个确认所需的时间间隔。
- TCP会对发送数据进行排序,实现方式是对每个字节关联一个序列号。
- TCP可实现流量控制,其会通告对端一次性能够接收到的最大字节数,即所谓通告窗口,任意时刻,该窗口会提示接收缓冲区的空闲内存,保证对端发送过来的数据到达缓冲区后不会出现溢出现象。
- TCP连接方式是全双工,即可以实现在同一时刻接收和发送数据,故TCP需要为每个数据流方向跟踪序列号及通告窗口等信息。
1.3.2 TCP连接的建立与终止
1.3.2.1 建立连接-三次握手
- 客户端需要建立连接时,通过connect主动发起请求,告知服务器客户端将在待建立的连接中发送数据的初始序列号。一般情况下SYN不携带数据;
- 服务器通过socket、bind和listen三个接口监听及处理外来的连接请求;服务器需要确认(ACK)客户端的SYN,同时给客户端回复一个SYN,其中包含了服务器在该连接中发送数据的初始序列号。
- 客户端收到服务器的回复后,需要确认(ACK)服务器的SYN。
TCP连接与打电话的过程类似,socket接口就是电话,bind接口函数是告知对方电话号码,listen函数为电话铃声,connect函数需要我们知道对方的电话号码并拨打电话,accept函数及应答对方的来电。
1.3.2.2 终止连接-四次挥手
TCP终止连接的流程可总结为四次挥手:
- 客户端需要结束tcp连接时,先调用close()函数,告知服务器端要结束连接,执行主动关闭连接操作,该端TCP发送一个FIN,通告对端数据发送完毕;
- 服务器接收到FIN后,执行主动关闭连接操作,该FIN一般置于接收数据队列的最后,表示该连接上已无需接收的数据,并确认客户端FIN,向客户端发送确认ACK;
- 等待一段时间后,服务器端调用close接口关闭器套接字,导致服务器端TCP也发送一个FIN;
- 客户端接收到FIN后,确认该FIN,向服务器端发送ACK.
4 UDP协议基础
(1)基本概念
用户数据包协议(User Datagram Protocol, UDP),是一个无连接协议,UDP套接字是数据报套接字(Datagram socket)。UDP可靠性低,不能保证数据最终到达目的地。
应用程序在UDP套接字中写入一个消息,该消息会被封装到一个UDP数据报,UDP数据报又会被封装到一个IP数据报,最后发送给目的地。
UDP的特点:
- 不保证UDP数据包会准确到达目的地;
- 不保证各个数据报的先后顺序跨网络后保持不变;
- 不保证每个数据报只到达一次;
二、Socket套接字基本概念
2.1 套接字地址结构体
IPv4套接字地址结构体,也称网际套接字地址结构体,其定义在头文件<netinet/in.h>
头文件,其POSIX定义如下:
struct in_addr
{
in_addr_t s_addr; /* 32位IPv4地址 以网络字节序存储 */
};
struct sockaddr_in
{
uint8_t sin_len; /* 结构体长度*/
sa_family_t sin_family; /* 协议族 IP4/IP*/
in_port_t sin_port; /* 16位TCP/UDP端口号 */
struct in_addr sin_addr; /* 32位IPv4地址 */
char sin_zero[8];
}
2.2 IPv6套接字地址结构体
IPv6套接字地址结构体同样在<netinet/in.h>
中定义
struct in6_addr
{
uint8_t s6_addr[16]; /* 128位IPv6地址,采用网络字节序存储 */
}
#define SIN6_LEN /* 长度字段 */
struct sockaddr_in6
{
uint8_t sin6_len; /* 结构体长度28 */
sa_family_t sin6_family; /* 地址族 */
in_port_t sin6_port; /* 传输层端口号 */
uint32_t sin6_flowinfo; /* 流信息*/
struct in6_addr sin6_addr; /* IPv6地址 */
uint32_t sin6_scope_id; /* 标识范围 */
}
三、基于TCP的套接字编程基础
3.1 TCP客户端-服务器典型事件流程
如下图所示是TCP客户端与服务器通信的典型事件流程。服务器首先启动,而后客户端再启动,客户端给服务器发送一个请求,服务器处理该请求,并给客户回复一个响应。这个过程将循环持续,直到客户端主动关闭连接。
3.2 socket函数
#include<sys/socket.h>
int socket(int family, int type, int protocol); /* 成功:返回非负描述符,失败:返回-1 */
套接字接口相关参数说明:
- family:协议族,其取值范围如下图所示