socket原意插座,是互联网中的插座;(套接字?怒喷翻译)
插头插到插座上就能从电网获得电力供应;为了与远程计算机进行数据传输,需要连接到因特网,而 socket 就是用来连接到因特网的工具;
一 简略了解网络
如果你想看编程接口,请不要看这章,本章内容繁杂直接劝退;
1 三种socket
socket有流式SOCK_STREAM和数据报SOCK_DGRAM两种,前者用于TCP模型,后者用于UDP模型;
1.1 SOCK_STREAM
是一种可靠的、双向的通信数据流,TCP协议能够使数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送;
SOCK_STREAM 有以下几个特征:
数据在传输过程中不会消失;
数据是按照顺序传输的;
数据的发送和接收不是同步的(有的教程也称“不存在数据边界”)。
1.2 SOCK_DGRAM
数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字
计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。
因为数据报套接字所做的校验工作少,所以在传输效率方面比流格式套接字要高。
SOCK_DGRAM 有以下特征:
强调快速传输而非传输顺序;
传输的数据可能丢失也可能损毁;
限制每次传输的数据大小;
数据的发送和接收是同步的(有的教程也称“存在数据边界”)。
1.3 SOCK_RAW
可以对较低层次协议如IP、ICMP直接访问;
2 面向连接和无连接
面向连接和无连接指的都是协议。也就是说,这些术语指的并不是物理介质本身,而是用来说明如何在物理介质上传输数据的。面向连接和无连接协议可以,而且通常也确实会共享同一条物理介质。
2.1 面向连接
面向连接的协议则维护了分组之间的状态,使用这种协议的应用程序通常都会进行长期的对话。记住这些状态,协议就可以提供可靠的传输。
典型的面向连接协议有三个阶段。第一阶段,在对等实体间建立连接。接下来是数据传输阶段,在这个阶段中,数据在对等实体间传输。最后,当对等实体完成数据传输时,连接被拆除。
有连接socket非常可靠,万无一失,但是传输效率低,耗费资源多
2.2 无连接
无连接协议中的分组被称为数据报(datagram),每个分组都是独立寻址,并由应用程序发送的。从协议的角度来看,每个数据报都是一个独立的实体,与在两个相同的对等实体之间传送的任何其他数据报都没有关系,这就意味着协议很可能是不可靠的。也就是说,网络会尽最大努力传送每一个数据报,但并不保证数据报不丢失、不延迟或者不错序传输。
无连接socket传输效率高,但是不可靠,有丢失数据包、捣乱数据的风险
3 网络模型OSI与TCP/IP
3.1 OSI
Open System Interconnection 的缩写,译为“开放式系统互联”。
我找到了这篇文章:OSI七层模型传输过程的通俗理解
看完我就懂了每层到底在干嘛。现在我把文章中每层的作用整理在此:
- 应用层:用户通过应用程序向应用层提出请求,比如发送邮件、访问网页,在这一层用到确定发送方式,比如邮件选择STMP或者IMAP,网页请求是HTTP还是HTTPS。
- 表示层:准备数据,把格式和编码变成网路传输中通用的形式;
- 会话层:一个进程的传输请求就是一个会话,会话层负责建立,维护,断开每个用户进程与各个服务器的连接;
- 传输层:在建立好的连接中,把表示好的数据进行合并,编号,发送;并处理各种传输中的异常,保证传输可靠性;
- 网络层:计算出主机局域网与目标服务器所在局域网的最佳线路并把数据发送到目标局域网;
- 数据链路层:通过MAC地址在局域网中找到目标服务器;
- 物理层:信号转换和物理传输,将数据转换成光信号、电信号,然后通过光缆、铜缆传输。
也就是说,(1)用户通过应用层传入数据和数据协议,(2)表示层把本地数据转换成网络通用格式,(3)会话层建立和维护与服务器的连接,(4)传输层控制数据发送方式,解决数据发送异常,确保传输可靠性,(5)网络层把传输层的数据送到服务器所在局域网,(6)数据链路层在局域网内找到服务器,(7)物理层是中间的光缆等物理线路;
还要注意:两台计算机进行通信时,必须遵守以下原则:
- 必须是同一层次进行通信,比如,A 计算机的应用层和 B 计算机的传输层就不能通信,因为它们不在一个层次,数据的拆包会遇到问题。
- 每一层的功能都必须相同,也就是拥有完全相同的网络模型。如果网络模型都不同,那不就乱套了,谁都不认识谁。
- 数据只能逐层传输,不能跃层。
- 每一层可以使用下层提供的服务,并向上层提供服务。
3.2 TCP/IP
OSI的上层应用层,表示层,会话层统一为应用层;数据链路层和物理层统一为网络接口层;
OSI复杂且多,放到现实中无法实现;
TCP/IP可以且好实现,风靡现实世界;
3.2.1 用于各层常见的协议
- 应用层:Telnet、FTP、HTTP、DNS、SMTP;
- 传输层:TCP、UDP;
- 网络层:IP、ICMP、IGMP;
- 网络接口层:以太网、令牌环网、FDDI等;
4 网络地址
4.1 IP地址
Internet Protocol Address 的缩写,译为“网际协议地址”;
有IPv4和IPv6两种,IPv4是32位,IPv6是128位;
表示有点分十进制的字符串表示法和32位二进制表示法;比如"192.168.4.1"和0b11111111101010100101010110101010;(随便写的,不要拿去计算),编程时使用点分十进制,在网络传输前要转换为二进制;
4.2 MAC地址
Media Access Control Address 的缩写,直译为“媒体访问控制地址”,也称为局域网地址(LAN Address),以太网地址(Ethernet Address)或物理地址(Physical Address)。
真正能唯一标识一台计算机的是 MAC 地址,每个网卡的 MAC 地址在全世界都是独一无二的。计算机出厂时,MAC 地址已经被写死到网卡里面了;。局域网中的交换机会记录每台计算机的 MAC 地址。
数据链路层,当数据已经到达目标路由器所在的局域网,数据转给交换机,由交换机根据MAC地址找到目标主机;
4.3 端口号
一台计算机可以同时提供多种网络服务,例如 Web 服务(网站)、FTP 服务(文件传输服务)、SMTP 服务(邮箱服务)等,仅有 IP 地址和 MAC 地址,计算机虽然可以正确接收到数据包,但是却不知道要将数据包交给哪个网络程序来处理,所以通信失败。
为了区分不同的网络程序,计算机会为每个网络程序分配一个独一无二的端口号(Port Number),例如,Web 服务的端口号是 80,FTP 服务的端口号是 21,SMTP 服务的端口号是 25。
端口(Port)是一个虚拟的、逻辑上的概念。可以将端口理解为一道门,数据通过这道门流入流出,每道门有不同的编号,就是端口号
5 字节序及转换
5.1 网络字节序为大端序
表示层将用户的数据转换为网络传输中的同意通用格式,字节序就是一种;
首先,字节序本身只有两种,大端序和小端序;
大端序Big-Endian:低地址放的是高字节;
小端序Little-Endian:低地址放的是低字节;
在网络和本地主机通讯期间,就分出了网络字节序和主机字节序;
网络字节序NBO - Network Byte Order:使用统一的字节顺序,避免兼容性问题;
主机字节序HBO - Host Byte Order:不同的机器HBO是不一样的,这与CPU的设计有关;
网络字节序为大端序;在数据传输之前,需要把数据都转成大端序;
Linux库函数中提供了:
5.2 用于端口号的转换函数
主机字节序到网络字节序:
u_long htonl (u_long hostlong);
u_short htons (u_short short);
网络字节序到主机字节序:
u_long ntohl (u_long hostlong);
u_short ntohs (u_short short);
5.3 用于IP地址的转换函数
inet_aton( )将strptr所指的字符串转换成32位的网络字节序二进制值
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
inet_addr( )功能同上,返回转换后的地址。
int_addr_t inet_addr(const char *strptr);
inet_ntoa( )将32位网络字节序二进制地址转换成点分十进制的字符串。
char *inet_ntoa(stuct in_addr inaddr);
inet_pton()将IPV4/IPV6的地址转换成binary格式
int inet_pton(int af, const char *src, void *dst);
二 socket编程接口
socket通信流程:
socket() 创建套接字
bind() 绑定本机地址和端口
connect() 建立连接
listen() 设置监听端口
accept() 接受TCP连接
recv(), read(), recvfrom() 数据接收
send(), write(), sendto() 数据发送
close(), shutdown() 关闭套接字
各函数详解:
1 socket()
创建一个socket,以文件形式,并返回其描述符;这个描述符就是此进程的socket fd
;在服务器端为服务器socket fd,在客户端为客户端socket fd;
#include <sys/types.h>
#include <sys/socket.h>
int socket (int domain, int type, int protocol);
成功返回一个描述符,失败返回-1;并设置errno;
- domain 是地址族
AF_INET // internet 协议
AF_UNIX // unix internal协议
AF_NS // Xerox NS协议
AF_IMPLINK // Interface Message协议 - type // 套接字类型
SOCK_STREAM // 流式套接字- TCP
SOCK_DGRAM // 数据报套接字- UDP
SOCK_RAW // 原始套接字- 直接传输 - protocol 参数通常置为0,表示系统自动匹配;
int tcp_socket = socket(AF_INET, SOCK_ST