ip+端口=套接字,现在所说的socket编程就是套接字编程
编程流程
对于网络编程要从客户端和服务端来看,在网络通信前,双方都要做准备工作:
对于服务端
1.创建套接字
2.绑定地址信息
绑定地址信息是绑定网络IP和进程端口
- 例如,现在有一个服务端进程,绑定地址信息是为了有一个能标识自己在哪一个进程的端口;
- 一个端口只能被一个进程绑定;
- 当客户端需要连接该进程只需要拿到端口
对于客户端
1.创建套接字
2.不推荐绑定地址信息
不推荐绑定地址的原因:
- 因为一个端口只能被一个socket结构体占用
一旦客户端绑定了固定的地址,这个客户端只能运行一个
并且还有端口冲突的风险- 可以让程序在同一机器上启动多个,绑定端口后每个主机就只能启动一个
- 但是这只是我们没有绑定,而实际操作系统会在调用sendto函数发送数据前自动绑定一个端口
前期准备工作完成后,客户端和服务端就能相互发送消息
在发送消息时,一定是UDP客户端先给服务端发消息,因为服务端是不知道客户端在哪个IP和端口
- 图示:
- 创建套接字:建立进程与网卡之间的关联
- 绑定地址信息:给创建的socket结构体内部填充源IP和端口
告诉系统收到的数据凡是地址和端口是我绑定的,就交给我处理
发送数据的时候,源IP就是绑定的地址信息 - 接收数据:从内核的socket结构体中的接收缓冲区取出数据
- 发送数据:将数据放到内核scoket结构体的发送缓冲区中
创建套接字
- 套接字描述符本质是文件描述符
- 返回值是fd,就能以读写文件的方式来访问网络
所以套接字实质是一块内存
绑定地址信息
- 套接字可以用于网络通信和主机通信,不同通信方式,两个进程所需要的同一份资源不同
- 所以设计了这个结构体来区分通信类型
- 对于主机内通信,则传路径就行,找到一个文件
- 将右边两个结构体对象传给sockaddr,用于解析是哪两个通信类型
定义一个通用类型结构体就是为了接收不同的结构体;
结合上图右边的蓝色框起部分理解
- 在使用bind函数绑定地址信息时,需要对bind函数的第二个参数进行赋值
- 也就是对sockaddr_in或sockaddr_un结构体中的变量进行赋值
- 结构体中有两个普通类型变量sin_family(地址域信息),sin_port(端口)
- 还有一个结构体变量sin_addr,该结构体内部的变量类型是一个无符号的32位整数,s_addr;要使用inet_addr函数转换
- 结构体第三个参数应传私网ip地址
- sin_family:套接字用于网络通信还是主机通信
- sin_port:填充对应的端口号信息,要发给对端主机,所以要转成网络字节序
- sin_addr:
字符串风格的点分十进制,每个区域是一个字节的数据
addr:
不管是哪种地址结构,只要拿到前两个字节的数据,就知道剩下的数据该如何解析
接收与发送函数
sendto
peer是程序员填写的,用于给谁发
recv
peer:输出型参数,用来接收数据,是谁发送的拿到后进行解析
存的谁,就是信息谁发送给我的,由函数设置
alen:输入输出型参数,表示想要接收的地址信息长度,以及实际得到的地址信息
服务端代码
- sendto函数的peer参数,因为通过recvfrom函数获取到了客户端的IP和端口信息,所以发送的时候直接把peer原本的内容填进去就行,就可以发给收到消息的客户端了
- 所以客户端要写一个socket_int类型的addr对象,把要访问的IP和端口预先处理好,让发送函数发送对端
客户端代码
客户端先发送再接收
服务端先接收再发送
字节序接口
uint_16t htons(uint_16t val)
uint_16t ntohs(uint_16t val)
typedef uint32_t in_addr
in_addr_t inet_addr(const char* ip);将点分十进制的字符串ip地址转换为网络字节序的整数IP地址
const char* inet_ntoa(struct in_addr addr),将网络字节序整数IP地址,转换成一个点分十进制字符串IP地址
ifconfig执行后,看lo和eth0的inet IP地址
端口使用1024以上的端口,0-1023端口已经被知名服务使用了
udp的发送接收缓冲区
- 创建套接字后,会在内存中创建一个套接字对应的结构体;
- 在该结构体中,根据创建套接字的不同类型(udp类型和tcp类型),都会产生两个缓冲区;
- 一个是发送缓冲区,一个是接收缓冲区(缓冲区在传输层),创建套接字就相当于初始化这两个缓冲区
- 调用sendto函数发送数据,该数据先会递交给udp的发送缓冲区当中
- 调用recvfrom函数接收数据,是从接收缓冲区中拿数据
upd是有发送和接收缓冲区,因为udp是整条数据交付的,应用层将数据给udp后,udp就会在发送缓冲区打上udp的包头,递交给网络层的IP协议
- 程序员基本是与传输层进行交互;
- 不参与传输层和网络层,与网络层和数据链路层;
- 这两者是靠net代码进行交互的
公网ip和私网ip
为什么有公网ip和私网ip
- ipv4版本的ip地址本质是一个无符号的32位整数,约为42亿9千万个不同ip;
- 但是全球实际的机器不止于此;
- 所以将其中部分ip取出,让这部分ip不能直接访问互联网;
- 但是这些ip可以在不同网络中进行附用;
- 而公网ip可以直接访问互联网;
后面再详述