目录
1.相关历史
第一阶段:ARPAnet(阿帕网),是网络基础协议的雏形。
第二阶段:第一份IP协议说明书。
第三阶段:TCP/IP协议被制定
协议:指两者之间需遵守的一种约定
2.网络程序设计框架
(1) C/S:客户端/服务器设计框架
优点:支持本地缓存,应用层协议可以自己灵活制定
缺点:开发工作量大,用户安全性较低
(2) B/S:浏览器/服务器设计框架
优点:开发工作量小,用户安全性较高
缺点:不支持本地缓存,应用层协议必须遵守http协议
3.网络协议分层模型
(1) OSI分层模型:理想的网络分层模型
(2) TCP/IP标准协议模型:一种标准的网络分层模型
- TCP/IP协议是Internet事实上的工业标准。
- TCP/IP不是一个协议,而是一个协议族的统称。里面包括了IP协议,IMCP协议,TCP协议,以及我们更加熟悉的http、ftp、pop3协议等等
1.应用层 :可以由用户自定义的协议,方便数据传输以及数据加密。http,tftp,NFS协议等
2.传输层 :提供端对端的数据传输。TCP协议、UDP协议
3.网络层 :提供数据的路由。IP协议、ICMP协议
4.网络接口层 :实现网络底层驱动。以太网协议、ARP协议、RAPP…
3.网络接口编程
(1) socket套接字
Linux系统内核提供套接字作为网络通信的编程接口。
- 套接字本质为一个特殊的文件描述符
- 套接字不仅限于TCP/IP协议
TCP/IP四层协议中,只有应用层是程序员能够自定义的,下面三层的协议封装都是由内核完成(但是有选择权利)。
套接字分类:
- 流式套接字:数据以二进制流的方式进行传递,无大小限制。保证数据可靠,无丢失,顺序发送。主要用于TCP协议。一般情况下只要选择流式方式,那内核就会默认选择TCP传输层协议。
- 数据报套接字:主要通过数据报的方式发送,固定大小。不能保证数据可靠,可能丢失,乱序发送,主要用于UDP协议。一般情况下只要选择数据报方式,那内核就会默认选择UDP传输层协议。
- 原始套接字:可以对较低层次协议如IP,ICMP直接访问。
TCP协议适用于对传输质量要求较高,传输大量数据,即时通讯的账户登录等通信。
UDP协议适用于小尺寸数据、无线网络、广播组播式通信,即时通讯的文本、音视频通讯等
(2) IP地址和端口
IP地址:标识主机,IPv4为32位,IPV6为128位,常以点分十进制表示,例:192.168.2.1
其二进制形式:11000000 10101000 00000010 00000001
IP地址的转换:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
//功能:将cp所指字符串转换成32位的网络字节序二进制值,并将其存储在 inp 指向的结构中
返回值:如果地址有效,inet_aton() 返回非零,否则返回零
in_addr_t inet_addr(const char *cp);
//功能和上述相似:将 Internet 主机地址 cp 从 IPv4 数字和点表示法转换为网络字节顺序的二进制数据。 如果输入无效,则返回 INADDR_NONE(通常为 -1)
in_addr_t inet_network(const char *cp);
//功能:将cp(IPv4 数字和点表示法的字符串)转换为适合用作 Internet 网络地址的主机字节顺序数字。 成功时,返回转换后的地址。 如果输入无效,则返回-1。
char *inet_ntoa(struct in_addr in);
//功能:函数将以网络字节顺序给出的 Internet 主机地址转换为 IPv4 点分十进制表示法的字符串。 字符串在静态分配的缓冲区中返回,后续调用将覆盖该缓冲区。
端口:标识进程,无符号短整型:0-- 65535,其中0~1024被内核使用,1025-- 5000由系统分配,5001-- 65536用户(或程序员)分配。
网络字节序通常为大端序,大端指低地址存放高字节,字节的高位在内存中放在存储单元的起始位置
个人PC的字节序通常是小端序,小端指低地址存放在低字节,与大端相反。
所以在网络绑定port端口时要进行字节序的转换,字节序转换函数如htons、htonl等。
h:主机host
to:转换
n:网络net
s:short类型 (ipv4使用)
l:long类型 (ipv6使用)
#include <arpa/inet.h>
//IPv4
uint16_t htons(uint16_t hostshort);
将无符号短整数 hostshort 从主机字节顺序转换为网络字节顺序。
uint16_t ntohs(uint16_t netshort);
将无符号短整数 netshort 从网络字节顺序转换为主机字节顺序。
//IPv6
uint32_t htonl(uint32_t hostlong);
将无符号整数 hostlong 从主机字节顺序转换为网络字节顺序
uint32_t ntohl(uint32_t netlong);
将无符号整数 netlong 从网络字节顺序转换为主机字节顺序。
(3)编程常用函数
创建套接字 socket()
功能:socket() 为通信创建一个端点并返回一个引用该端点的文件描述符。 成功调用返回的文件描述符将是当前未为进程打开的编号最小的文件描述符。
#include <sys/types.h>
#include <sys/socket.h>
//以下关于套接字的函数都声明了这两个头文件,所以后续不再写
int socket(int domain, int type, int protocol);
/*
domain: 套接字中使用的协议族(Protocol Family)
PF_INET表示IPV4协议族
PF_INET6表示IPV6协议族
...
type: 套接字数据传输的类型信息
SOCK_STREAM 面向连接的套接字即流套接字,收发数据的套接字内部有缓冲(buffer),适用于 write 和 read 函数。流套接字必须处于连接状态,然后才能在其上发送或接收任何数据。是可靠地、按序传递的、基于字节的面向连接的数据传输方式的套接字
SOCK_DGRAM 面向消息的套接字,不可靠的、不按序传递的、以数据的高速传输为目的套接字,可能丢失数据
protocol: 计算机间通信中使用的协议信息
如果前两个参数选择ipv4和面向连接的传输,则协议使用TPPROTO_TCP ,称为TCP套接字。
如果前两个参数选择ipv4和面向消息传输,则协议使用TPPROTO_UDP 称为UDP套接字。
当然对TCP、UDP方式,第三个参数可以填0,默认选择对应方式的协议,因为其协议分别只有一个。
返回值:成功时返回文件描述符,失败时返回-1
*/
例:
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);//TCP套接字
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);//UDP套接字
绑定套接字 bind()
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
描述:当使用 socket(2)创建套接字时,它存在于名称空间(地址族)中,但没有分配给它的地址。 bind() 将 addr 指定的地址分配给文件描述符 sockfd 引用的套接字。 addrlen 指定 addr 指向的地址结构的大小(以字节为单位)。传统上,此操作称为“为套接字分配名称”
参数:
sockfd :套接字文件描述符
addr : sockaddr结构体的地址
len : addr指向的地址结构的大小
sockaddr结构体的唯一目的是转换在 addr 中传递的结构指针,以避免编译器警告。
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
查找适用于IPV4协议的套接字addr结构体的命令
grep -r "struct sockaddr_in {" /usr/include/
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET 即地址族,此处为ipv4地址族*/
in_port_t sin_port; /* port in network byte order即端口号 */
struct in_addr sin_addr; /* internet address 即IP地址*/
};
struct in_addr {
uint32_t s_addr; /* address in network byte order即32位网络地址 */
};
注:IPV6的addr结构体为 struct sockaddr_in6 ;
返回值:成功时,返回零。 出错时,返回 -1,并适当设置 errno。
addr结构体信息填入例子:
struct sockaddr_in my_addr;
memset(&my_addr,0,sizeof(myaddr));//清空
my_addr.sin_family=PF_INET;//IPv4协议族
my_addr.sin_port=htons(6666);//端口号
my_addr.sin_addr.s_addr=inet_addr("192.168.127.10");//IP地址
bind(sfd,(struct sockaddr*)(&my_addr),sizeof(my_addr))
主动发送连接请求 connect()
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:
sockfd :套接字文件描述符
addr : sockaddr结构体的地址
len : addr指向的地址结构的大小
返回值:如果连接或绑定成功,则返回零。 出错时,返回 -1,并适当设置 errno。
监听客户端连接请求 listen()
int listen(int sockfd, int backlog);
参数:
sockfd :套接字文件描述符
backlog:表示同时能够监听的客户端个数(不是同时能连接的个数)
返回值:成功返回0,出错返回-1
接收客户端连接请求 accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd :监听端口的套接字文件描述
addr :客户端地址信息,如果不需要客户端传递信息则填NULL
addrlen :客户端地址信息长度,如不需要客户端传递信息则填NULL
返回值:成功则返回用于通信的套接字,出错返回-1
//后续的数据通信全部使用通信套接字,不能使用监听套接字
关闭套接字 close()
#include <unistd.h>
int close(int fd);
参数:fd 文件描述符,此处填套接字文件描述符
返回值:成功返回0,失败返回-1