-
-
[原创]网络编程技术学习笔记之基础模型
-
发表于: 2021-11-22 16:29 21038
-
网络编程技术目录:
在Windows上想要实现网络编程首先需要使用WSAStartup来初始化Winsock库,定义如下:
Winsock库有多个版本,要使用的库的版本信息使用第一个WORD类型的参数来进行指定。其中,高8位为副版本号,低8位为主版本号。操作字节不太方便,为了编程方便,通常会使用MAKEWORD宏来构建WORD型版本信息。
MAKEWORD(1, 2):主版本为1,副版本为2,返回0x201
MAKEWORD(2, 2):主版本为2,副版本为2,返回0x202
当程序需要退出的时候,需要使用WSACleanup来注销该库,定义如下:
计算机之间的网络通信是通过套接字来实现的,所以无论是客户端还是服务端都需要使用socket函数来创建套接字,该函数定义如下:
af参数指定套接字的协议族的信息,大致分为以下几类,不过最常用的还是PF_INET和PF_INET6。
type参数则指定了套接字之间传输数据的方式,常用的有以下三种:
protocol可以选择IPPROTO_TCP,IPPROTO_UDP,IPPROTO_ICMP等协议。
该参数的值由第二个参数决定。
如果第二个参数为SOCK_STREAM,那么该参数就是IPPROTO_TCP,如果第二个参数是SOCK_DGRAM,那么该参数就是IPPROTO_UDP
函数执行成功,则会获得套接字,不再使用该套接字的时候,应当用closesocket来关闭套接字,该函数定义如下:
有了套接字以后就可以实现计算机之间的网络通信,但对于客户端和服务端来说,它们接下来要做的事情就不同的。
服务端:
对于服务端来说,接下来就需要使用bind函数来给套接字分配IP地址和端口号,该函数定义如下
由此可见,IP地址和端口号是由第二个参数决定的,sockaddr结构体的定义如下:
由于该结构体不好赋值,所以Winsock库给我们提供了sockaddr_in结构体,该结构体定义如下:
其中指定IP地址的sin_addr是一个in_addr结构体,该结构体定义如下:
可以看到,该结构体中保存了联合体S_un,而S_un中则保存了整型S_addr,该成员就是用来保存IP地址的。由于IP地址的保存是用整型,所以为了编程方便需要使用inet_addr,该函数可以将字符串转为整型,定义如下:
由于在网络中字节顺序是按大尾排序,而Windows系统兼容的CPU是小尾模式的,所以在对IP地址和端口进行赋值的时候,需要使用htonl和htons将小尾排序的转为大尾排序,这两个函数定义如下:
可以看到这两个函数功能一样,只是参数与返回值大小不一样,htonl是整型,而htons是短整型。
客户端:
对于客户端来说,此时则需要使用connect函数与服务端进行连接,该函数定义如下:
服务端与客户端完成连接以后,接下来就可以进行通信。其中recv函数用来接收数据,该函数定义如下:
send函数用来发送数据,该函数定义如下:
但要注意的是,这两个函数是对缓冲区进行读数据与发数据的,并不是直接对对方计算机直接操作。也就是说,recv函数是从缓冲区读取数据,真正从对方计算机获取数据的是由操作系统完成,操作系统获取数据以后会将输入放入缓冲区中。同样send函数是向缓冲区中写入数据,真正完成向对方计算机发送数据的也是由操作系统将缓冲区中的数据发送到对方计算机。
这就是网络通信的最基础的一组操作,但是根据数据传输方式的不同,可以选择TCP或者UDP进行通信。TCP/UDP在TCP/UDP协议栈中的位置如下图所示:
也就是说,无论是TCP还是UDP,操作都是在第二层完成的。
要实现基于TCP的传输,首先在创建套接字的时候要创建的是面向连接的套接字(SOCK_STREAM),该套接字有以下三个特点:
传输过程中数据不会消失
按序传输数据
传输数据不存在数据边界
前面说过,发送与接收数据是要通过缓冲区的,那么接收数据的缓冲区已经满了,此时就会停止传输数据,这就保证了数据不会丢失
其次,在TCP传输中,在服务端在调用bind函数绑定地址信息以后,需要调用listen函数来监听端口,该函数定义如下:
该函数让套接字处于监听状态,第二个参数决定了该服务器可以发起的连接数有多少,处于监听状态的套接字可以通过accept函数来完成与客户端的连接,该函数的定义如下:
而对于客户端,只需要在初始化套接字的时候选择TCP方式传输数据的套接字,之后在与服务端连接以后就可以实现客户端与服务端的通信。
以下两张图则总结了TCP方式通信的服务端与客户端指向的步骤
还有一个问题就是连接的关闭,在两台主机通过套接字建立连接后进入可交换数据的状态,又称"流形成的状态"。也就是把建立套接字后可交换数据的状态看作一种流
一旦建立了连接,每个主机都会有单独的输入流与输出流,通过这两条流实现了数据的传输。但是考虑这样的情况,那就是客户端正在给服务端传输数据,可是服务端此时关闭了服务器,这就会造成数据的丢失,为了避免这种状况,就需要使用到shutdown函数,该函数定义如下:
how参数决定了该函数如何断开套接字,不同值得含义如下:
通过这个函数,就可以实现套接字的半关闭,其目的如下图所示,这就保证了数据的完整
最后给出完整代码。
服务端代码:
客户端代码:
最终可以看到服务端与客户端之间的成功通信
要想使用UDP通信,创建套接字的时候就要选择使用面向消息的套接字(SOCK_DGRAM),该传输方式有如下的特点:
强调快速传输而非传输顺序
传输的数据可能丢失也可能损毁
传输的数据有数据边界
限制每次传输的数据大小
相比于TCP,UDP无需调用listen和accept函数来建立连接,服务端和客户端可以直接交换数据。在UDP方式通信中,发送数据的函数是sendto,该函数定义如下
而接收数据则是用recvfrom函数来接收数据
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
- [原创]CVE-2022-21882提权漏洞学习笔记 16361
- [原创]CVE-2021-1732提权漏洞学习笔记 19460
- [原创]CVE-2014-1767提权漏洞学习笔记 15175
- [原创]CVE-2018-8453提权漏洞学习笔记 18502
- [原创]CVE-2020-1054提权漏洞学习笔记 13525