协议就是一种提前约定好的规则。
c/s模式: 客户端/服务器模式。
优点:协议选择灵活,完全可以使用自定义的协议; 能够通过客户端预先缓冲数据,适合中大型的项目;稳定性较高
缺点: 因为需要额外下载客户端,对用户安全构成威胁(不知道客户端在干什么);开发工作量相对较大,需要开发服务器和客户端两类程序。跨平台性不好。
b/s模式: 浏览器/服务器模式
优点: 不需要额外下载客户端,使用自带浏览器; 开发工作量相对较小,只需要开发服务器端;跨平台性好。
缺点:必须严格完整的支持http协议; 不能预先缓冲数据,适合小型的项目;稳定性较差。
TCP/IP模型: 网络层使用IP协议,传输层使用TCP/UDP协议,应用层使用HTTP/SSH/FTP/蓝牙等协议。
在实际编程的时候,真正打交道的是应用层,至于其他层是依靠操作系统来帮助我们进行通信。我们说的使用TCP/IP协议,并不是真正与传输层网络层打交道,本质上还是在应用层。
一个数据要在网络传输,需要逐层封装打包,接收方获取数据包后也需要逐层解包,以获取真正的数据。这个过程都是操作系统来帮我们完成了。
路由器的ip属于公网ip(唯一性),它内部有一个NAT映射表,可以把链接到路由器的那个终端ip(局域网ip)映射成公网IP。
所以,公网ip 访问 公网ip -----直接访问。
公网ip 访问 私网ip ------借助NAT映射
私网ip 访问 公网ip -------借助NAT映射
私网ip 访问 私网ip ------- NAT映射或打洞机制
套接字的概念
socket本意是插座,因此socket都是成对出现;借助ip与端口,ip可以在网络中唯一确定一台主机,而端口可以在主机中唯一确定一个进程。
linux内核实现socket抽象成文件描述符,一个文件描述符对应两个内核缓冲区,一个缓冲区专门用于读,另一个缓冲区专门用于写,所以socket既能同时读与写,不会冲突。
TCP/IP协议规定,网络数据流应采用大端字节序,x86平台用的是小端字节序,为了使网络程序具有可移植性,同样的C代码能在大端和小端计算机上正常运行,提供了以下库函数做网络字节序与主机字节序的转换。
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntons(uint16_t netshort);
h表示主机host,n表示网络network,l表示32位整数,s表示16位整数。
ip地址是32位,使用 htonl、 ntohl
端口号是16位,使用htons、ntons
如果主机是小端字节序,这些函数将参数做相应的转换然后返回,如果主机是大端字节序,这些函数不做转换,参数原封不动的返回。
因为ip是一串数字(点分十进制),而计算机内部是一串整型数字,还需要再转换成网络字节序。
比如192.168.1.12----> unsigned int ----->htonl------>大端网络字节序
linux操作系统提供了更好的方法来做这种转换,一步到位。
inet _pton函数把点分ip地址一步到位转换成网络字节序。
反之,inet_ntop函数把网络字节序一步到位转换成点分ip地址。
#include <arpa/inet.h>
int inet _pton(int af,const char*scr,void *dst);
const char*inet_ntop(int af,const void*scr, char*dst, sockelen_t size);
参数af指定选择的ip版本,AF_INET表示是ipv4,AF_INET6表示是ipv6.
scr就是点分十进制,dst就是最后转换完成的网络字节序