WinSock网络编程指南
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
WinSock网络编程
Windows Sockets是一套开放的、支持多种协议的Windows下的网络编程接口。
现在的Winsock已经基本上实现了与协议无关,你可以使用Winsock来调用多种协议的功能,但较常使用的是TCP/IP协议。
Winsockets无疑是我们进行网络编程的利器。
Winsocket编程之TCP/IP体系结构
一、什么是TCP/IP
TCP/IP指的是Internet上使用的两种网络协议:传输控制协议和网际协议;事实上,TCP协诡计和IP协议只是一个称为TCP/IP协议族中的两种而已。
TCP/IP协议族可以将各种操作系统和网络部件连接起来,它能提供一种在各种系统间移动数据的标准方法。
这些协议既可以用于Internet上,也可以用于专用网中。
TCP/IP协议族中的协议为当今网络用户的各种服务提供数传输功能,这些服务包括:
·电子邮件的传送
·文件传输
·即时消息
·访问万维网
开放式系统互联(OSI)参考模型
OSI参考模型用于对涉网络的各种技术进行标准化,它定义的七层结构代表了数据通信协议的基本结构。
OSI模型中的每一层都指定了特定的功能,它可以看作是各层紧挨着另一屋的一个栈。
对于给定的层,它所提供的服务是由这一层的协议定义的。
理解了OSI模型及其各层有助于理解TCP/IP网络中的各部分与应用程序之间是如何进行交互的。
OSI模型栈的七层分别是:
·第7层(应用层),这是OSI模型的最高层,本层定义了应用程序与网络及其他系统之间的交互方式
·第6层(表示层),包含了部分操作系统的协议,这一层定义了信息的显示格式,数据加密和解释属于本层功能。
·第5层(会话层),协调端到端的通信,本层维护各种会话状态,提供安全、登录和管理功能。
·第4层(传输层),控制各系统之间的数据流,定义各种消息的数据结构,并进行差错校验。
Web浏览器的加密通常在本层实现。
·第3层(网络层),定义了系统间路由数据的各种协议,点到点通信发生在此层,确保数据到达正确的目的的主机。
·第2层(数据链路层或网络接口层),定义了局部网络环境(即局域网)中从一个节点到另一节点发送和接收消息的规则。
·第1层(物理层媒体层),控制硬件的连接和字节流的编码,这是惟一涉及到网络节点间信息的物理传输的一层。
ISO的OSI对服务、接口和协议的概念区别十分明了,但它却没有真正的用户群。
TCP/IP模型对服务、接口和协议的概念区别不象OSI模型那样明晰,但很实用。
对于TCP/IP而言,最重要的是应用层、传输层、网络层和数据链路层;这些层都有特定的协议与这关联,这些协议将在后面的章节中讨论。
这些协议可以分为两大类:网络类和应用类(参见图4-1)。
OSI
图4-1
TCP/IP 结构事实上并不严格遵循OSI 模型。
但当前关于如何使用分层模型来描述TCP/IP 又没有一个统一的协定。
一般承认TCP/IP 比7层OSI 模型层次少(3到5层)。
这里我们以4层TCP/IP 结构进行讲解。
TCP/IP 结构中忽略了OSI 模型中的某些特征,只综合了部分相邻OSI 层的特征并分离其它各层。
信息由
4层结构中的应用层传送到物理层。
当发送数据时,每层将其从上层接收到的信息作为本层数据,并在数据前添加控制信息头,然后一起传送到下一层。
每层的接收数据过程与以上发送过程正好相反,其中在数据被传送到上一层之前要将其控制信息头移去。
TCP/IP 4 层模型以及每层主要功能描述如下:
应用层(Application Layer)
TCP/IP 组中的应用层综合了OSI 应用层、表示层以及会话层的功能。
因此,在TCP/IP 结构中,传输层以上的任何过程都称之为应用。
在TCP/IP 中,使用套接字(socket)和端口描述应用程序通信路径。
大多数应用层协议与一个或多个端口号相关联。
传输层(Transport Layer)
TCP/IP 结构中包含两种传输层协议。
其一传输控制协议(TCP),确保信息传输过程。
其二用户数据报协议(UDP),直接传输数据报,而不需要提供端对端可靠校验。
两种协议对应不同的应用具有各自功能。
IP地址用来寻址指定的计算机或者网络设备,而TCP的端口号用来确定运行在目的设备上的哪个应用程序应该接受这个封包。
端口号是16位的。
连接的两端都要使用端口号,但没必要相同。
网络层(Network Layer)
TCP/IP 网络层中的主要协议是网际协议(IP)。
所有网络层以下或以上的各层通信在跨越TCP/IP 协议栈时,都必须通过IP 完成。
此外,网络层还包含部分支持性协议,如ICMP,实施和管理路由过程。
互联网使用IP地址来唯一标志一台计算机。
IP地址可以通过软件分配给网络接口,将IP地址和网络接口的MAC地址关联在一起。
为了使用IP寻址,关联的MAC地址需要保存起来,这由ARP(地址解析协议)负责。
每个主机都维护了一个记录IP和MAC地址对的清单。
网络访问层(Network Access Layer)
在TCP/IP 结构中,网络访问层由数据链路层和物理层合并而成。
TCP/IP 网络访问层并没有重新定义新标准,而是有效利用原有数据链路层和物理层标准。
很多RFC 中描述了IP 如何使用数据链路协议并作为其接口界面,如以太网、令牌环、FDDI、HSSI 和A TM 等。
物理层中规定了硬件通信属性,但它不直接作为网络层及以上层的TCP/IP 协议的接口。
这一层也称为介质访问控制层(MAC).例如网卡就属于这一层;以太网接口的各层都有不同的寻址方法,在MAC层,寻址是通过MAC号进行的。
MAC号是一个48位的标志,它被硬性分配到每一个网络接口单元,它是由IEEE注册分配的,保证每个以太网结点都有世界唯一的号码。
数据传输
TCP/IP协议的基本传输单位是数据包(datagram),TCP协议负责把数据分成若干个数据包,并给每个数据包加上包头(就像给一封信加上信封),包头上有相应的编号,以保证在数据接收端能将数据还原为原来的格式,IP协议在每个包头上再加上接收端主机地址,这样数据找到自己要去的地方(就像信封上要写明地址一样),如果传输过程中出现数据丢失、数据失真等情况,TCP协议会自动要求数据重新传输,并重新组包。
总之,IP协议保证数据的传输,TCP协议保证数据传输的质量。
TCP/IP协议数据的传输基于TCP/IP协议的四层结构:应用层、传输层、网络层、接口层,数据在传输时每通过一层就要在数据上加个包头,其中的数据供接收端同一层协议使用,而在接收端,每经过一层要把用过的包头去掉,这样来保证传输数据的格式完全一致。
与其他协议相比,TCP/IP具有两大优点:它是一种轻量级的、实现成本比较低的协议。
基于上述原因,TCP/IP成为最受欢迎的协议。
1983年,TCP/IP被集成到BSD UNIX4.2发布版本中,然后很快又被集成到UNIX 的商业版本中,并且被纳为Internet的标准,沿用至今。
今天,TCP/IP得到了除Internet通信以外的广泛应用。
例如企业内部网(Internet)通常都使TCP/IP来构建。
在这些应用中,TCP/IP显示出了其他网络协议无与伦比的优势。
比如TCP/IP可以运行在各种各样的硬件和操作系统平台之上;使用TCP/IP可以快速方便地构建异构网络,实现Macs、IBM兼容机、大型机、SunUNIX服务器、MIPS各种类型机器的互联。
这些机器可以通过一种通用的协议族进行通信,这也是TCP/IP自问世以来能够长盛不衰的原因。
二、TCP/IP 体系结构与特点
1、TCP/IP体系结构
TCP/IP协议实际上就是在物理网上的一组完整的网络协议。
其中TCP是提供传输层服务,而IP则是提供网络层服务。
TCP/IP包括以下协议:(结构如图1.1)
(图1.1)
IP:网间协议(Internet Protocol) 负责主机间数据的路由和网络上数据的存储。
同时为ICMP,TCP,UDP提供分组发送服务。
用户进程通常不需要涉及这一层。
ARP:地址解析协议(Address Resolution Protocol)
此协议将网络地址映射到硬件地址。
RARP:反向地址解析协议(Reverse Address Resolution Protocol)
此协议将硬件地址映射到网络地址
ICMP:网间报文控制协议(Internet Control Message Protocol)
此协议处理信关和主机的差错和传送控制。
TCP:传送控制协议(Transmission Control Protocol)
这是一种提供给用户进程的可靠的全双工字节流面向连接的协议。
它要为用户进程提供虚电路服务,并为数据可靠传输建立检查。
(注:大多数网络用户程序使用TCP)
UDP:用户数据报协议(User Datagram Protocol)
这是提供给用户进程的无连接协议,用于传送数据而不执行正确性检查。
FTP:文件传输协议(File Transfer Protocol)
允许用户以文件操作的方式(文件的增、删、改、查、传送等)与另一主机相互通信。
SMTP:简单邮件传送协议(Simple Mail Transfer Protocol)
SMTP协议为系统之间传送电子邮件。
TELNET:终端协议(Telnet Terminal Procotol)
允许用户以虚终端方式访问远程主机
HTTP:超文本传输协议(Hypertext Transfer Procotol)
TFTP: 简单文件传输协议(Trivial File Transfer Protocol)
2、TCP/IP特点
TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通常是在操作系统内核中实现。
因此用户一般不涉及。
编程时,编程界面有两种形式:一、是由内核心直接提供的系统调用;二、
使用以库函数方式提供的各种函数。
前者为核内实现,后者为核外实现。
用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。
图1.2是TCP/IP协议核心与应用程序关系图。
(图1.2)
三、专用术语
1、套接字
套接字是网络的基本构件。
它是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连听进程。
套接字存在通信区域(通信区域又称地址簇)中。
套接字只与同一区域中的套接字交换数据(跨区域时,需要执行某和转换进程才能实现)。
WINDOWS 中的套接字只支持一个域——网际域。
套接字具有类型。
WINDOWS SOCKET 1.1 版本支持两种套接字:流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)
2、WINDOWS SOCKETS 实现
一个WINDOWS SOCKETS 实现是指实现了WINDOWS SOCKETS规范所描述的全部功能的一套软件。
一般通过DLL文件来实现(WS2_32. DLL)
3、阻塞处理例程
阻塞处理例程(blocking hook,阻塞钩子)是WINDOWS SOCKETS实现为了支持阻塞套接字函数调用而提供的一种机制。
4、多址广播(multicast,多点传送或组播)
是一种一对多的传输方式,传输发起者通过一次传输就将信息传送到一组接收者,与单点传送(unicast)和广播(Broadcast)相对应。
练习题:
Q1:请你分别划划OSI的七层网络结构图,和TCP/IP的五层结构图?
OSI TCP/IP
第七层:Application
第六层:Presentation
第五层:Session 第五层:Application
第四层:Transport 第四层:Transport
第三层:Network 第三层:Internet
第二层:Data Link 第二层:Data Link
第一层:Physical 第一层:Physical
Q2:请你详细的解释一下IP协议的定义,在哪个层上面,主要有什么作用?TCP与UDP呢?
Internet Protocol协议是为跨越局域网和广域网环境的大规模互联网络而设计的世界标准的协议组。
IP协议工作在第三层,主要用于实现连接到互联网上的结点之间的通信。
Q3:请问交换机和路由器分别的实现原理是什么?分别在哪个层次上面实现的?
交换机工作在第二层,通过MAC地址进行数据帧的交换。
路由器工作在第三层,通过最优路径来传输数据包。
Winsocket编程之套接字原理
一、客户机/服务器模式
在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Server model)。
该模式的建立基于以下两点:1、非对等作用;2、通信完全是异步的。
客户机/服务器模式在操作过程中采取的是主动请示方式:
首先服务器方要先启动,并根据请示提供相应服务:(过程如下)
1、打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。
2、等待客户请求到达该端口。
3、接收到重复服务请求,处理该请求并发送应答信号。
4、返回第二步,等待另一客户请求
5、关闭服务器。
客户方:
1、打开一通信通道,并连接到服务器所在主机的特定端口。
2、向服务器发送服务请求报文,等待并接收应答;继续提出请求……
3、请求结束后关闭通信通道并终止。
二、基本套接字
为了更好说明套接字编程原理,给出几个基本的套接字,在以后的篇幅中会给出更详细的使用说明。
1、创建套接字——socket()
功能:使用前创建一个新的套接字
格式:SOCKET PASCAL FAR socket(int af,int type,int procotol);
参数:af: 通信发生的区域
type: 要建立的套接字类型
procotol: 使用的特定协议
2、指定本地地址——bind()
功能:将套接字地址与所创建的套接字号联系起来。
格式:int PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);
参数:s: 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
其它:没有错误,bind()返回0,否则SOCKET_ERROR
地址结构说明:
struct sockaddr_in
{
short sin_family;//AF_INET
u_short sin_port;//16位端口号,网络字节顺序
struct in_addr sin_addr;//32位IP地址,网络字节顺序
char sin_zero[8];//保留,空字节,要设为0
}
sockaddr_in SA1,SA2;
SA1. sin_family= AF_INET;
SA1. sin_addr. S_un. S_un_b. s_b1=127;
SA1. sin_addr. S_un. S_un_b. s_b2=0;
SA1. sin_addr. S_un. S_un_b. s_b3=0;
SA1. sin_addr. S_un. S_un_b. s_b4=1;
SA1. sin_addr. S_un. S_addr= inet_addr(“127.0.0.1”);
Little-endian ox9812 ox12,ox 98 big- endian 0x98 0x12
描述32位IP地址的in_addr结构定义:
struct in_addr
{
union{
struct { u_char s_b1, s_b2, s_b3, s_b4;} S_un_b;
struct { u_short s_w1,s_w2;} S_un_w;
u_long S_addr;
} S_un;
}
3、建立套接字连接——connect()和accept()
功能:共同完成连接工作
格式:int PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);
SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR * name,int FAR * addrlen);
参数:同上
4、监听连接——listen()
功能:用于面向连接服务器,表明它愿意接收连接。
格式:int PASCAL FAR listen(SOCKET s, int backlog);
5、数据传输——send()与recv()
功能:数据的发送与接收
格式:int PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);
int PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);
参数:buf:指向存有传输数据的缓冲区的指针。
6、多路复用——select()
功能:用来检测一个或多个套接字状态。
格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds,
fd_set FAR * exceptfds,const struct timeval FAR * timeout);
参数:readfds:指向要做读检测的指针
writefds:指向要做写检测的指针
exceptfds:指向要检测是否出错的指针
timeout:最大等待时间
7、关闭套接字——closesocket()
功能:关闭套接字s
格式:BOOL PASCAL FAR closesocket(SOCKET s);
三、典型过程图
2.1 面向连接的套接字的系统调用时序图
2.2 无连接协议的套接字调用时序图
2.3 面向连接的应用程序流程图
Windows Socket1.1 程序设计
一、简介
Windows Sockets 是从Berkeley Sockets 扩展而来的,其在继承Berkeley Sockets 的基础上,又进行了新的扩充。
这些扩充主要是提供了一些异步函数,并增加了符合WINDOWS消息驱动特性的网络事件异步选择机制。
Windows Sockets由两部分组成:开发组件和运行组件。
开发组件:Windows Sockets 实现文档、应用程序接口(API)引入库和一些头文件。
运行组件:Windows Sockets 应用程序接口的动态链接库(WINSOCK.DLL)。
二、主要扩充说明
1、异步选择机制:
Windows Sockets 的异步选择函数提供了消息机制的网络事件选择,当使用它登记网络事件发生时,应用程序相应窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。
Windows Sockets 提供了一个异步选择函数WSAAsyncSelect(),用它来注册应用程序感兴趣的网络事件,当这些事件发生时,应用程序相应的窗口函数将收到一个消息。
函数结构如下:
int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);
参数说明:
hWnd:窗口句柄
wMsg:需要发送的消息
lEvent:事件(以下为事件的内容)
值:含义:
FD_READ 期望在套接字上收到数据(即读准备好)时接到通知
FD_WRITE 期望在套接字上可发送数据(即写准备好)时接到通知
FD_OOB 期望在套接字上有带外数据到达时接到通知
FD_ACCEPT 期望在套接字上有外来连接时接到通知
FD_CONNECT 期望在套接字连接建立完成时接到通知
FD_CLOSE 期望在套接字关闭时接到通知
(OOB data is a logically independent transmission channel associated with each pair of connected stream sockets.)
例如:我们要在套接字读准备好或写准备好时接到通知,语句如下:
rc=WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);
如果我们需要注销对套接字网络事件的消息发送,只要将lEvent 设置为0
2、异步请求函数
在Berkeley Sockets 中请求服务是阻塞的,WINDOWS SICKETS 除了支持这一类函数外,还增加了相应的
异步请求函数(WSAAsyncGetXByY();)。
3、阻塞处理方法
Windows Sockets 为了实现当一个应用程序的套接字调用处于阻塞时,能够放弃CPU让其它应用程序运行,它在调用处于阻塞时便进入一个叫“HOOK”的例程,此例程负责接收和分配WINDOWS消息,使得其它应用程序仍然能够接收到自己的消息并取得控制权。
WINDOWS 是非抢先的多任务环境,即若一个程序不主动放弃其控制权,别的程序就不能执行。
因此在设计Windows Sockets 程序时,尽管系统支持阻塞操作,但还是反对程序员使用该操作。
但由于SUN 公司下的Berkeley Sockets 的套接字默认操作是阻塞的,WINDOWS 作为移植的SOCKETS 也不可避免对这个操作支持。
在Windows Sockets 实现中,对于不能立即完成的阻塞操作做如下处理:DLL初始化→循环操作。
在循环中,它发送任何WINDOWS 消息,并检查这个Windows Sockets 调用是否完成,在必要时,它可以放弃CPU让其它应用程序执行(当然使用超线程的CPU就不会有这个麻烦了^_^)。
我们可以调用WSACancelBlockingCall() 函数取消此阻塞操作。
在Windows Sockets 中,有一个默认的阻塞处理例程BlockingHook() 简单地获取并发送WINDOWS 消息。
如果要对复杂程序进行处理,Windows Sockets 中还有WSASetBlockingHook() 提供用户安装自己的阻塞处理例程能力;与该函数相对应的则是SW AUnhookBlockingHook(),它用于删除先前安装的任何阻塞处理例程,并重新安装默认的处理例程。
请注意,设计自己的阻塞处理例程时,除了函数WSACancelBlockingHook() 之外,它不能使用其它的Windows Sockets API 函数。
在处理例程中调用WSACancelBlockingHook()函数将取消处于阻塞的操作,它将结束阻塞循环。
4、出错处理
Windows Sockets 为了和以后多线程环境(WINDOWS/UNIX)兼容,它提供了两个出错处理函数来获取和设置当前线程的最近错误号。
(WSAGetLastEror()和WSASetLastError())
5、启动与终止
使用函数WSAStartup() 和WSACleanup() 启动和终止套接字。
三、Windows Sockets网络程序设计核心
我们终于可以开始真正的Windows Sockets 网络程序设计了。
不过我们还是先看一看每个Windows Sockets 网络程序都要涉及的内容。
让我们一步步慢慢走。
1、启动与终止
在所有Windows Sockets 函数中,只有启动函数WSAStartup() 和终止函数WSACleanup() 是必须使用的。
启动函数必须是第一个使用的函数,而且它允许指定Windows Sockets API 的版本,并获得SOCKETS的特定的一些技术细节。
本结构如下:
int PASCAL FAR WSAStartup(WORD wV ersionRequested, LPWSADATA lpWSAData);
其中wVersionRequested 保证SOCKETS 可正常运行的DLL 版本,如果不支持,则返回错误信息。
我们看一下下面这段代码,看一下如何进行WSAStartup() 的调用
WORD wVersionRequested;// 定义版本信息变量
WSADATA wsaData;//定义数据信息变量
int err;//定义错误号变量
wVersionRequested = MAKEWORD(1,1);//给版本信息赋值
err = WSAStartup(wVersionRequested, &wsaData);//给错误信息赋值
if(err!=0)
{
return;//告诉用户找不到合适的版本
}
//确认Windows Sockets DLL 支持1.1 版本
//DLL 版本可以高于1.1
//系统返回的版本号始终是最低要求的1.1,即应用程序与DLL 中可支持的最低版本号
if(LOBYTE(wsaData.wVersion)!= 1|| HIBYTE(wsaData.wVersion)!=1)
{
WSACleanup();//告诉用户找不到合适的版本
return;
}
//Windows Sockets DLL 被进程接受,可以进入下一步操作
关闭函数使用时,任何打开并已连接的SOCK_STREAM 套接字被复位,但那些已由closesocket() 函数关闭的但仍有未发送数据的套接字不受影响,未发送的数据仍将被发送。
程序运行时可能会多次调用WSAStartup() 函数,但必须保证每次调用时的wVersionRequested 的值是相同的。
2、异步请求服务
Windows Sockets 除支持Berkeley Sockets 中同步请求,还增加了了一类异步请求服务函数WSAAsyncGerXByY()。
该函数是阻塞请求函数的异步版本。
应用程序调用它时,由Windows Sockets DLL 初始化这一操作并返回调用者,此函数返回一个异步句柄,用来标识这个操作。
当结果存储在调用者提供的缓冲区,并且发送一个消息到应用程序相应窗口。
常用结构如下:
HANDLE taskHnd;
char hostname="rs6000";
taskHnd = WSAAsyncBetHostByName(hWnd,wMsg,hostname,buf,buflen);
需要注意的是,由于Windows 的内存对像可以设置为可移动和可丢弃,因此在操作内存对象是,必须保证WIindows Sockets DLL 对象是可用的。
3、异步数据传输
使用send() 或sendto() 函数来发送数据,使用recv() 或recvfrom() 来接收数据。
Windows Sockets 不鼓励用户使用阻塞方式传输数据,因为那样可能会阻塞整个Windows 环境。
下面我们看一个异步数据传输实例:假设套接字s 在连接建立后,已经使用了函数WSAAsyncSelect() 在其上注册了网络事件FD_READ 和FD_WRITE,并且wMsg 值为WM_SOCK,那么我们可以在Windows 消息循环中增加如下的分支语句:case WM_SOCK:
switch(lParam)
{
case FD_READ:
len = recv(wParam,lpBuffer,length,0);
break;
case FD_WRITE:
while(send(wParam,lpBuffer,len,0)!=SOCKET_ERROR)
break;
}
break;
4、出错处理
Windows 提供了一个函数来获取最近的错误码WSAGetLastError(),推荐的编写方式如下:
len = send (s,lpBuffer,len,0);
if((len==SOCKET_ERROR)&&(WSAGetLastError()==WSAWOULDBLOCK)){...}
基于Visual C++的Winsock API研究
为了方便网络编程,90年代初,由Microsoft联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即Windows Sockets规范,它不是一种网络协议,而是一套开放的、支持多种协议的Windows下的网络编程接口。
现在的Winsock已经基本上实现了与协议无关,你可以使用Winsock来调用多种协议的功能,但较常使用的是TCP/IP协议。
Socket实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有Socket接口的计算机通信。
应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。
微软为VC定义了Winsock类如CAsyncSocket类和派生于CAsyncSocket 的CSocket类,它们简单易用,读者朋友当然可以使用这些类来实现自己的网络程序,但是为了更好的了解Winsock API编程技术,我们这里探讨怎样使用底层的API函数实现简单的Winsock 网络应用程式设计,分别说明如何在Server端和Client端操作Socket,实现基于TCP/IP的数据传送,最后给出相关的源代码。
在VC中进行WINSOCK的API编程开发的时候,需要在项目中使用下面三个文件,否则会出现编译错误。
1.WINSOCK.H: 这是WINSOCK API的头文件,需要包含在项目中。
2.WSOCK32.LIB: WINSOCK API连接库文件。
在使用中,一定要把它作为项目的非缺省的连接库包含到项目文件中去。
3.WINSOCK.DLL: WINSOCK的动态连接库,位于WINDOWS的安装目录下。
一、服务器端操作socket(套接字)
1)在初始化阶段调用WSAStartup()
此函数在应用程序中初始化Windows Sockets DLL ,只有此函数调用成功后,应用程序才可以再调用其他Windows Sockets DLL中的API函数。
在程式中调用该函数的形式如下:WSAStartup((WORD)((1<<8|1),(LPWSADA TA)&WSAData),其中(1<<8|1)表示我们用的是WinSocket1.1版本,WSAdata用来存储系统传回的关于WinSocket的资料。
2)建立Socket
初始化WinSock的动态连接库后,需要在服务器端建立一个监听的Socket,为此可以调用Socket()函数用来
建立这个监听的Socket,并定义此Socket所使用的通信协议。
此函数调用成功返回Socket对象,失败则返回INV ALID_SOCKET(调用WSAGetLastError()可得知原因,所有WinSocket 的函数都可以使用这个函数来获取失败的原因)。
SOCKET PASCAL FAR socket( int af, int type, int protocol )
参数: af:目前只提供PF_INET(AF_INET);
type:Socket 的类型(SOCK_STREAM、SOCK_DGRAM);
protocol:通讯协定(如果使用者不指定则设为0);
如果要建立的是遵从TCP/IP协议的socket,第二个参数type应为SOCK_STREAM,如为UDP(数据报)的socket,应为SOCK_DGRAM。
3)绑定端口
接下来要为服务器端定义的这个监听的Socket指定一个地址及端口(Port),这样客户端才知道待会要连接哪一个地址的哪个端口,为此我们要调用bind()函数,该函数调用成功返回0,否则返回SOCKET_ERROR。
int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR *name,int namelen );
参数:s:Socket对象名;
name:Socket的地址值,这个地址必须是执行这个程式所在机器的IP地址;
namelen:name的长度;
如果使用者不在意地址或端口的值,那么可以设定地址为INADDR_ANY,及Port为0,Windows Sockets 会自动将其设定适当之地址及Port (1024 到5000之间的值)。
此后可以调用getsockname()函数来获知其被设定的值。
4)监听
当服务器端的Socket对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。
listen()函数使服务器端的Socket 进入监听状态,并设定可以建立的最大连接数(目前最大值限制为5, 最小值为1)。
该函数调用成功返回0,否则返回SOCKET_ERROR。
int PASCAL FAR listen( SOCKET s, int backlog );
参数:s:需要建立监听的Socket;
backlog:最大连接个数;
服务器端的Socket调用完listen()后,如果此时客户端调用connect()函数提出连接申请的话,Server 端必须再调用accept() 函数,这样服务器端和客户端才算正式完成通信程序的连接动作。
为了知道什么时候客户端提出连接要求,从而服务器端的Socket在恰当的时候调用accept()函数完成连接的建立,我们就要使用WSAAsyncSelect()函数,让系统主动来通知我们有客户端提出连接请求了。
该函数调用成功返回0,否则返回SOCKET_ERROR。
int PASCAL FAR WSAAsyncSelect( SOCKET s, HWND hWnd,unsigned int wMsg, long lEvent );
参数:s:Socket 对象;
hWnd :接收消息的窗口句柄;
wMsg:传给窗口的消息;
lEvent:被注册的网络事件,也即是应用程序向窗口发送消息的网络事件,该值为下列值FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE的组合,各个值的具体含意为:FD_READ:希望在套接字S收到数据时收到消息;
FD_WRITE:希望在套接字S上可以发送数据时收到消息;
FD_ACCEPT:希望在套接字S上收到连接请求时收到消息;
FD_CONNECT:希望在套接字S上连接成功时收到消息;
FD_CLOSE:希望在套接字S上连接关闭时收到消息;
FD_OOB:希望在套接字S上收到带外数据时收到消息。
具体应用时,wMsg应是在应用程序中定义的消息名称,而消息结构中的lParam则为以上各种网络事件名称。
所以,可以在窗口处理自定义消息函数中使用以下结构来响应Socket的不同事件:
case WM_SOCK://WM_SOCK为自定义网络通知消息
switch(lParam)
{case FD_READ:
…
break;
case FD_WRITE、
…
break;
…。