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);
返回值:如果地址有效,inet_aton() 返回非零,否则返回零
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
端口:标识进程,无符号短整型: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>
uint16_t htons(uint16_t hostshort);
将无符号短整数 hostshort 从主机字节顺序转换为网络字节顺序。
uint16_t ntohs(uint16_t netshort);
将无符号短整数 netshort 从网络字节顺序转换为主机字节顺序。
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);
例:
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_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;
in_port_t sin_port;
struct in_addr sin_addr;
};
struct in_addr {
uint32_t s_addr;
};
注: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;
my_addr.sin_port=htons(6666);
my_addr.sin_addr.s_addr=inet_addr("192.168.127.10");
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
|