对于网络套字编程,就是对网络通信程序的编写。
网络通信的概念
1.在网络通信中,我们去将一个数据给另一个用户去发送的时候,其实并不是直接交到另一个用户的手里的,而是将这个数据发给一个服务端,服务端通过客户端发送的数据,从里面提取到一些给谁的信息,此时服务端会找到这些信息所对应的用户,然后等待这些用户上线之后,将这些数据给其发过去,实现了网络通信。
2.通过上面的网络通信的大致路径,我们也就知道了,进行网络通信的时候,我们为什么在发送的数据的时候要加上源端ip地址,源端口号,对端ip地址,对端口号,这样服务端才能对发来的数据进行操作。
3.客户端和服务端
①:客户端:网络通信中用户的一端,也是进行业务请求的一端,也是主动发送数据的一端。
②:服务端:网路通信中服务的一端,用来接收客户端发送的请求,然后根据请求去处理,是被动接收请求的一端。
4.对于网络通信,都是客户端与服务端的通信。
注意:没有客户端与客户端的通信,也没有服务端和服务端的通信。
TCP/UDP协议的认识和区别
对于TCP/UDP协议,它俩都是传输层的协议,但是它俩有不同的区别。
1.TCP协议特点:
- 有连接。
- 可靠传输。
- 面向字节流。
其中:在TCP协议中,它的有连接和面向字节流都是为了可靠的传输。
所以,对于TCP协议,它在通信之前,必须先建立连接,只有连接成功(服务端此时处于可接收状态)了才能进行通信(这个下面我们会详细讲到),而面向字节流就是,我们在传输数据的时候,我们传输的这个数据是不能有任何误差的,就像是我们传输一个文件,我们传输的这个文件一旦有一点误差,那么就会出错,这个文件也会相应的不能用了。
2.UDP协议特点:
- 无连接。
- 不可靠传输。
- 面向数据包。
其中:
①:在UDP协议中,他与TCP协议的特点不同,他是无连接和面向数据报的不可靠传输。
②:而无连接,就是我们在通信之前,不需要知道服务端此时是否是处于接收状态还是闭合状态,我只管发数据就可以了,其他的不用管,大不了我发的数据他不要,直接丢弃了。而面向数据报的就是,将数据分成数据报分别进行发送。
3.TCP协议和UDP协议的区别:
在上面的描述特点的时候,我们已经知道它俩的区别是在传输的时候,对于数据的操作的不同,一个是可靠传输的TCP协议,一个是不可靠传输的UDP协议。因为特点不同,所以在不同的场景下,性能也不同。
区别:
- 安全性大于性能要求:选择TCP协议,因为在使用TCP协议传输数据时,他要保证每个数据都是正确无误的,所以会进行检查错误,一旦出错,还要进行重新发送等一系列操作,所以大大降低了性能的要求。
- 性能要求大于安全性:选择UDP协议,因为在使用UDP协议传输数据时,不需要保证每个数据都是正确的,有点小损失也没事,就像是我们看视频一样,我们看着看着就有可能花一下,这没啥,这总比卡一下来的舒服,所以视频中的数据就是通过UDP协议传输的。所以UDP协议没有太多对数据进行细节改错的行为,没有那么多操作,所以性能比较高,所以相对的,安全性就比较差了。
套接字编程的函数接口
1.创建一个套接字:int socket(int domain,int type,int protocal);
其中:
- domain:地址域类型,用于决定通信使用什么地址接口。
其中:
①:IPV4的地址域类型为:AF_INET。
②:IPV6的地址域类型为:AF_INET6。 - type:套接字类型,用于决定使用什么套接字传输方式。
其中:
①:SOCK_STREAM:流式套接字,基于连接的,可靠的,面向字节流的传输服务(TCP)。
②:SOCK_DGRAM:数据报套接字,无连接的,不可靠的数据报传输服务(UDP)。 - protocal:使用的协议类型。
其中:
①:流式套接字默认0表示的是TCP,数据报套接字默认0表示的是UDP。
②:IPPROTO_TCP:TCP协议。
③:IPPROTO_UDP:UDP协议。
返回值:成功返回套接字的操作句柄,失败返回-1。
2.地址信息绑定:int bind(int sockfd,struct sockaddr* addr,socklen_t len);
其中:
- sockfd:第一步创建套接字返回的操作句柄,为给哪个套接字进行绑定。
- addr:绑定的地址信息。
- len:地址信息的长度。
对于addr,他是通用的接口,用来获取我们所建立的地址(通过将我们定义的地址进行强制类型转换,为struct sockaddr*),因为在网络通信中,不同的通信方式有不同的地址结构。
其中,我们常用的有IPV4和IPV6的通信结构,如下:
①:首先,通用接口的底层结构如下:
对于结构题图像:
其中,family为它们的地址域类型,因为通用的这个结构体,是为了接收我们自定义的结构体的地址信息,所以它的地址域类型以及数据都是没有的,主要是为了接收我们定义的。
②:IPV4的地址信息结构
结构图如下:
其中sin_port保存的端口号信息,而在其内部还有一个结构体,这个结构体名为in_addr,它主要是保存的是32位的IP地址信息。
in_addr:
由于地址结构有很多种,所以我们传入的时候,使用通用接口,这个通用接口会根据传递过来的地址域来判断这个地址信息是什么结构,然后根据结构进行解析。
其中:成功返回0,失败返回-1。
3.发送数据:size_t sendto(int sockfd,void* buf,int len,int flag,struct sockaddr* peer_addr,socklen_t addrlen);
其中:
- sockfd:创建好的套接字返回的操作句柄。
- buf:要发送的信息的首地址。
- len:要发送的信息的长度。
- flag:标志位(0-默认阻塞)
- peer_addr:目的端地址信息。
- addrlen:地址信息的长度。
返回值:成功返回实际发送的数据长度,失败返回-1。
4.接收数据:size_t recvfrom(int sockfd,void* buf,size_t len,int flag,struct sockaddr* peer_addr,socklen_t* addrlen)
其中:
- sockfd:创建套接字返回的操作句柄。
- buf:接收数据的地址。
- len:要接受数据的长度。
- flag:标志位(0-默认阻塞,没有缓冲区的时候默认阻塞等待)
- peer_addr:接收数据的源端口的地址。(就是谁发的)
- addrlen:输入输出参数,用于指定要获取的地址长度,以及要返回的实际长度。
返回值:成功返回实际获取的数据长度,失败返回-1。
5.关闭套接字:int colse(int fd);
其中:
- fd:为创建套接字返回的操作句柄。
6.由于网络字节序与主机字节序的不同,我们在进行数据传输的时候,需要通过接口将其转换:
①:16位数据的主机与网络字节序的转换(用于IPV4地址域类型的端口号的转换)
- 主机转网络:
unit16_t htons(unit16_t port);
- 网络转主机:
unit16_t ntohs(unit16_t port);
②:32位数据的主机与网络字节序的转换(用于IPV6地址域类型的端口号的转换)
- 主机转网络:
unit32_t htonl(unit32_t port);
- 网络转主机:
unit32_t ntohl(unit32_t port);
7.由于我们传入的ip地址一般都是以点分十进制的方式传入给电脑的,所以在传入电脑之前和从电脑传出之前,都要分别将点分十进制转换为电脑网络字节序的整数ip地址和将网络字节序的整数ip地址转换为点分十进制ip地址
头文件:#include<arpa/inet.h>
下面主要是对ipv4地址的操作。
- 点分十进制ip地址转换为电脑网络字节序的整数ip地址:
in_addr_t inet(char* ip);
- 将电脑网络字节序转换为点分十进制ip地址:
char* inet_ntoa(struct in_addr sin_addr);
其中:还有inet_ntop/inet_pton转换函数:不仅可以转换ipv4信息,还可以转换ipv6。
UDP客户端和服务端
UDP客户端和服务端的操作步骤
对于客户端和服务端,它俩虽然的操作接口函数是相同的,但是在有些地方是不同的。
1.服务端操作步骤:
- 创建套接字:就是在内核创建一个套接字结构(struct socket)用于关联网卡与当前通信进程。
- 为套接字绑定地址信息:将源IP地址和源端口号描述到stocket中,用于告诉操作系统收到的哪些数据,应该交到这个socket中。
它的结构图像也就如下:
3.接收数据:从创建的套接字的接收缓冲区中,取出数据,顺便获取这个数据是谁发送的。
4.发送数据:将要发送的数据放入到套接字的发送缓冲区中,并且告诉套接字数据要发送给谁。
5.关闭套接字:释放资源。
特别的:对于服务端,它必须绑定地址信息,因为它就像是一个服务台,我们要到这个地方服务,就必须在服务台去登记一下;由此可见,由于来服务的人可以是任何人,所以对于客户端,它不需要绑定地址信息。
2.客户端的操作步骤:
- 创建套接字:用于关联网卡与通信进程。
- 绑定地址信息:不建议,因为上面说过,对于客户端,我们只需要知道服务端的地址信息就可以了,而且,如果我们绑定了地址信息,那么在与客户端通信的时候,发送的数据源端地址就是什么,但是一个端口只能被一个进程占用,所以容易冲突。如果我们不绑定地址信息,那么客户端与服务端进行通信的时候,系统就会给客户端随机匹配一个地址信息,所以不用担心不绑定地址信息的后果。
- 发送数据:数据发送到缓冲区,并且告诉套接字数据要发送给谁。就是在这个时候,系统在发送数据的时候,如果发现源端地址没有绑定信息,那么就会选择一个合适的地址信息进行绑定。
- 接收数据:从套接字接收缓冲区取来的数据。
- 关闭套接字。
注意:
①:在服务端与客户端通信的时候,五元组信息是必须有的:源端IP地址,源端端口号,目的端ip地址,目的端端口号,协议。
②:对于服务端,它必须先接收数据,才能知道是谁给他发的消息,所以后发数据。
UDP客户端和服务端的模拟实现
1.服务端的模拟实现:
2.客户端的模拟实现:
①:定义一个客户端的udp套接字类
②:用户端实现