网络编程——预备知识
- 🍃套接字
- 🌿什么是套接字
- 🌿套接字的类型
- 🌿套接字的位置
- 🍃IP
- 🍃端口号Port
- 🍃字节序
- 🍃地址信息结构(结构体类型)
🍃套接字
🌿什么是套接字
本质:独立于协议之上的一种通用的网络编程的接口。
用户程序可以借助套接字来实现数据的发送以及接收,像是对”文件“的操作(读写)。
因此:套接字可以被看成一种特殊的文件,好处是:将应用程序访问协议栈的的方式进行了统一。
访问协议栈之前:先拥有一个访问协议栈的句柄(套接字对象socket)–》实现了用户空间访问内核空间。
socket:单词就是插座,意味着将外设和电路进行了连接
socket的官方表示:IP:PORT
通信本质:用户进程使用TCP/IP协议栈中的网络协议以及数据链路层所提供的的驱动程序模块进行数据交互。
思考:既然套接字被看成是一种特殊的文件,对于文件的操作一般使用句柄来完成。
句柄:套接字对象(被创建出来的)—》使用接口(函数)来获取的。
TCP/IP协议栈:网络协议的集合,构成网络通信的核心骨干。
总结:用户进程在用户空间即用户态,多种网络协议存在内核态,因此用户需要借助套接字这个接口来使用协议栈中的部分协议搭配驱动程序模块来完成网络通信。
🌿套接字的类型
为了满足应用程序的各种性能要求,套接字被分为3种类型:
- 流式套接字:SOCK_STREAM —》针对TCP协议
- 数据报套接字:SOCK_DGRAM ----》针对UDP协议
- 原始套接字:SOCK_RAW 可以让应用层直接访问网络层中的IP协议
🌿套接字的位置
处于应用层传输层之间
TCP/IP四层:应用层和传输层之间
OSI七层模型:会话层和传输层之间
🍃IP
概念:网络中一台主机的唯一标识,IPv4协议占据4个字节,以点分十进制形式表示。
IP的构成:网络地址 (高位) + 主机地址(低位)
IP分类:
A类:1个字节网络地址 + 3字节主机地址 ,最高位为:0
B类:2个字节网络地址 + 2字节主机地址 ,最高位为:10
C类:3个字节网络地址 + 1字节主机地址 ,最高位为:110
D类:不区分网络地址和主机地址,用于组播,固定最高字节的前4位必须为:1110
- 组播地址的范围:224.0.0.0 ~ 239.255.255.255
-
- 正常使用的组播地址范围:224.0.0.1 ~ 239.255.255.254
- 注意:凡是以255结尾的IP地址均属于“广播地址”
E类:等待将来使用
🍃端口号Port
Port占据2个字节short类型
端口号是用来唯一标识一台主机上的某一个进程。
为了区分一台主机接收到的数据包应该转交给哪个任务来进行处理,使用端口号来区别
端口号一般由IANA (Internet Assigned Numbers Authority) 管理
众所周知端口:11023(1255之间为众所周知端口,256~1023端口通常由UNIX系统占用)
已登记端口:1024~49151(我们选择端口的范围)eg:8888 9999 10086…
动态或私有端口:49152~65535
🍃字节序
大端字节序:将低字节处的内容存储在高地址,将高字节内容存储在低地址
小端字节序:将低字节处的内容存储在低地址,将高字节内容存储在高地址
思考:如果两台机器在通信是,字节序不一致,会出现数据接收错序的问题!但是也不能避免!
解决方案:网络通信时,所有的主机在发送数据时,全部按照网络字节序去发送
主机字节序:本地字节序(小端存储/大端存储)
网络字节序:(本质:大端存储)
注意:在大部分PC机上,基本都是小端字节序,当应用进程将整数送入socket前,需要转化成网络字节
序;当应用进程从socket取出整数后,要转化成小端字节序)
故:字节序解决的主要是多字节的数据存储的问题!
- IP字节序转换函数:4个字节
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
inet_aton()
将strptr所指的字符串转换成32位的网络字节序二进制值
#include <arpa/inet.h>
int inet_aton(const char *strptr,struct in_addr *addrptr);
(1)inet_addr() -----》常用的(发送时,将IP地址从主机字节序转换成网络字节序)
功能同上,返回转换后的地址。
in_addr_t inet_addr(const char *strptr);
(2)inet_ntoa()
将32位网络字节序二进制地址转换成点分十进制的字符串。
char *inet_ntoa(stuct in_addr inaddr);
- Port字节序转换函数:2个字节
#include <arpa/inet.h>
主机字节序到网络字节序
u_long htonl (u_long hostlong);
u_short htons (u_short short); ---》常用
网络字节序到主机字节序
u_long ntohl (u_long hostlong);
u_short ntohs (u_short short); ---》常用
🍃地址信息结构(结构体类型)
查询地址信息结构的方法:man 7 ip
(1)通用地址结构:
struct sockaddr
{
u_short sa_family; // 地址族, AF_xxx
char sa_data[14]; // 14字节协议地址
};
(2)Internet协议地址结构: ----》平常自定义地址信息结构首选的类型!(因为更详细)
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
IPv4地址结构:
struct in_addr {
uint32_t s_addr; /* address in network byte order*/
};
注意:地址信息结构如果使用Internet协议定义的,最后在绑定时,要强转为通用的地址信息结构类型
//定义地址信息结构,保存服务器端的Ip地址和端口
struct sockaddr_in serverAddr;
//将serverAddr这个结构变量的空间内容清空
//memset(&serverAddr, '\0', sizeof(serverAddr));
bzero(&serverAddr, sizeof(serverAddr));
//赋值
//给地址族赋值
serverAddr.sin_family = AF_INET;
//给端口号赋值
serverAddr.sin_port = htons(8888);
//给IP地址赋值
serverAddr.sin_addr.s_addr = inet_addr("192.168.16.188");
补充:清空函数
#include <strings.h>
void bzero(void *s, size_t n);
功能:清空一片地址空间内容
参数1:空间首地址
参数2:空间大小
案例:验证连接服务器这边连接成功的客户端的IP和端口是谁?
#include <stdio.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, const char *argv[])
{:
//定义地址信息结构,用来存储客户端的IP 和 端口
struct sockaddr_in clientAddr;
//清空
bzero(&clientAddr, sizeof(clientAddr));
//打印接收到的客户端的IP 和 端口
printf("客户端IP:%s\n",inet_ntoa(clientAddr.sin_addr));//主机字节序
printf("客户端Port:%hd\n",ntohs(clientAddr.sin_port));//主机字节序
return 0;
}