|
3.4 Windows网络编程基础 |
|
张将军,你平时对于网络了解有多少?
军师,你可算是问对人了,俺平时酷爱上网,什么网络游戏啊、QQ啊玩得可转了。
咳咳,你只知其表面,却完全不知它们是如何实现的。
哦,那军师你倒是说说它们是如何实现的呢?
网络数据的传输依靠TCP/IP协议,而在Windows下编写QQ之类的软件还得依靠强大的API——Winsock API。
咦,还有这种事?
确切地说,TCP/IP协议是一组包括TCP协议和IP协议,UDP(User Datagram Protocol)协议、ICMP(Internet Control Message Protocol)协议和其他一些协议的协议组。TCP/IP协议组之所以流行,部分原因是它可以用在各种各样的信道和底层协议(如T1和X.25、以太网,以及RS-232串行接口)之上。
传统的开放式系统互连参考模型是一种通信协议的七层抽象的参考模型,称为OSI七层模型。其中每一层都执行某一特定任务。该模型的目的是使各种硬件在相同的层次上相互通信。这七层是:物理层、数据链路层、网路层、传输层、话路层、表示层和应用层。
TCP/IP协议并不完全符合OSI的七层参考模型。而TCP/IP通信协议采用了四层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。这四层分别为:
1)应用层——应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。
2)传输层——在此层中,提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据。
3)互连网络层——负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
4)网络接口层——对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial Line等)来传送数据。
Windows Sockets(Windows套接字,简称Winsock)是Windows下得到广泛应用的、开放的、支持多种协议的网络编程接口。Winsock规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Microsoft Windows下网络编程接口。它不仅包含了人们所熟悉的Berkeley Socket风格的库函数,也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。Windows Sockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。
任何能够与Windows Sockets兼容实现协同工作的应用程序就被认为是具有Windows Sockets接口。通常称这种应用程序为Windows Sockets应用程序。Windows Sockets规范定义并记录了如何使用API与Internet协议族连接,尤其要指出的是所有的Windows Sockets实现都支持流套接字和数据报套接字。应用程序调用Windows Sockets的API实现相互之间的通信。Windows Sockets又利用下层的网络通信协议功能和操作系统调用实现实际的通信工作。
通信的基础是套接字(Socket),一个套接字是通信的一端。在这一端上可以找到与其对应的一个名字。一个正在被使用的套接字都有它的类型和与其相关的进程。套接字存在于通信域中。通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。Windows Sockets规范支持单一的通信域,即Internet域。各种进程使用这个域及Internet协议族来进行相互通信。利用Socket进行通信,有两种主要的方式:流套接字和数据报套接字。
流套接字提供了双向的、有序的、无重复并且无记录边界的数据流服务。数据报套接字支持双向的数据流,但并不保证是可靠、有序、无重复的。也就是说,一个从数据报套接字接收信息的进程有可能发现信息重复了,或者和发出时的顺序不同。典型应用为向网络发送广播数据包。
又是长篇大论,军师,俺都快要睡着了。
其实,简要来说,我们用一张图就可以概括。
……你干嘛不早说,这不是忽悠俺老张嘛。
图3.4
1.Winsock工作方式
两个TCP套接字在开始传输数据之前,必须先建立一个连接,也就是说由一方首先向另一方发送请求信息,双方互相确认后才能开始通信。TCP通信采用超时及重传机制来保证不丢失数据,确保可靠性。从TCP协议的特征可以看出通信双方的工作方式是不同的,这种工作方式可以用客户机/服务器模型(Client/Server或C/S)来描述,通信的发起端称为客户机,通信等待方称为服务器,如图3.5所示,显示了两者工作方式的不同。
图3.5
客户端在发送UDP数据包前,不需要先与服务器端进行相互确认,不管服务器是否在线,是否已经准备接收UDP数据包,客户端都可以随时发送数据。由于UDP协议是无连接的,所以通过一个UDP套接字可以向任何服务器地址发送数据,而不需要创建多个套接字。但UDP协议不对数据的可靠性与有序性等进行控制,它不是面向连接的,而是直接将数据包按照指定IP地址和端口发出,如果对方不在线,则数据就会丢失。
在客户端,UDP套接字不必经过连接的过程就可以直接发送数据。在服务器端,UDP套接字无所谓是否进入“监听”状态,只要有数据发送到套接字对应的端口,就可以被接收,所以TCP类型程序架构中的连接(connect)、监听(listen)和接受连接(accept)等动作都是不存在的。服务器和客户端唯一的区别就是服务器端程序必须首先将套接字绑定到一个固定的端口,以便客户端能够向约定的端口发送数据,如图3.6所示。
图3.6
这个WinSock相关的API好多啊。
共有11个,我已经帮你总结出来了,这些API都是以后进行网络编程时要熟练使用的哦。
2.Winsock相关函数
(1)WSAStarup()函数
功能: 该函数用于初始化WS2_32动态库,一般是应用程序第一个调用的Windows Socket函数,用于确定Windows Socket使用的版本。
参数:
wVersionRequired表示调用者使用的Windows Socket的版本,高字节记录修订版本,低字节记录主版本。
lpWSAData,一个WSADATA结构指针,该结构记录了Windows Socket的详细信息。
返回值: 函数执行成功返回0,否则返回错误代号。
(2)socket()函数
功能: 该函数用于创建一个套接字,为发起连接做准备。
参数:
af表示一个地址家族,通常为AF_INET。
type表示套接字类型,如果为SOCK_STREAM,表示创建面向连接的流式套接字;如果为SOCK_DGRAM,表示创建面向无连接的数据包套接字;如果为SOCK_RAW,表示创建原始套接字。
protocol表示一个特殊的协议被用于这个套接字。
返回值: 函数执行成功,返回创建的套接字句柄,否则返回INVALID_SOCKET。
(3)connect()函数
功能: 使用TCP套接字进行通信时,客户端必须使用该函数用于发送一个连接请求,连接到指定端口。发起连接时,系统会自动为套接字选择一个空闲的端口,如果一定要用特定的端口连接到服务器端,可以在调用connect函数前用bind函数将套接字绑定到指定的端口上。
参数:
s表示一个套接字标识。
name表示套接字s想要连接的主机地址和端口号。
namelen是name缓冲区的长度。
返回值: 函数执行成功返回0,否则返回SOCKET_ERROR。
(4)bind()函数
功能: 不管是TCP套接字还是UDP套接字,如果要套接字指定IP地址和端口监听,必须使用该函数将它绑定到该IP和端口上。绑定成功后,UDP套接字马上就可以接收数据包了。但TCP套接字还需要用listen函数进行监听,并使用accept函数接受进入的连接后才能进行通信。
参数:
s表示套接字标识。
addr是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。
namelen是addr缓冲区的长度。
返回值: 函数执行成功返回0,否则返回SOCKET_ERROR。
(5)listen()函数
功能: 该函数用于将TCP套接字设置为监听状态。
参数:
s表示套接字标识。
backlog表示等待连接的最大队列长度。
返回值: 函数执行成功返回0,否则返回SOCKET_ERROR。
(6)accept()函数
功能: 当客户端向监听的TCP套接字发起连接后,必须对监听的套接字调用该函数,然后连接才最后被确定。
参数:
s是一个套接字,应处于监听状态。
addr是一个sockaddr_in结构指针,包含一组客户端的端口号、IP地址等信息。
addrlen用于接收参数addr的长度。
返回值: 函数执行成功,返回一个新的对应于已经接受的客户端连接的套接字,否则返回INVALID_SOCKET。
(7)send()函数
功能: 在使用TCP连接时,用该函数向对方发送数据包。
参数:
s表示套接字标识。
buf存放要发送数据的缓冲区。
len表示缓冲区长度。
flags表示函数的调用方式。
返回值: 函数执行成功,返回发送的总字节数,否则返回SOCKET_ERROR。
(8)recv()函数
功能: 在使用TCP连接时,用该函数从指定套接字中接收数据包。
参数:
s表示套接字标识。
buf用于接收数据的缓冲区。
len,buf缓冲区的长度。
flags表示函数的调用方式,可选值为MSG_PEEK或MSG_OOB。
返回值: 若函数执行成功,返回收到的字节数,否则返回SOCKET_ERROR。
(9)sendto()函数
功能: UDP协议不是面向连接的,但UDP套接字被创建后,可以直接通过该函数向服务器端发送数据。
参数:
s表示套接字标识。
buf,存放要发送数据的缓冲区。
len,buf缓冲区的长度。
flags表示函数的调用方式。
to(可选),指向目的套接字地址的指针。
tolen:to所指地址的长度。
返回值: 函数执行成功,返回发送的总字节数,否则返回SOCKET_ERROR。
(10)recvfrom()函数
功能: 在使用UDP连接时,用该函数从指定套接字中接收数据包。
参数:
s表示套接字标识。
buf,存放接收数据的缓冲区。
len,buf缓冲区的长度。
flags,表示函数的调用方式。
from(可选),指向装有源地址的缓冲区。
fromlen(可选),指向from缓冲区长度值的指针。
返回值: 函数执行成功,返回收到的字节数,否则返回SOCKET_ERROR。
(11)WSACleanup()函数
功能: 该函数与WSAStartup函数相对,用于终止使用WS2_32动态库。
参数:
无
返回值: 函数执行成功返回0,否则返回SOCKET_ERROR。