WinSock网络编程实用宝典.docx
- 文档编号:30656680
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:71
- 大小:97.67KB
WinSock网络编程实用宝典.docx
《WinSock网络编程实用宝典.docx》由会员分享,可在线阅读,更多相关《WinSock网络编程实用宝典.docx(71页珍藏版)》请在冰豆网上搜索。
WinSock网络编程实用宝典
Winsocket编程之TCP/IP体系结构
一、TCP/IP体系结构与特点
1、TCP/IP体系结构
TCP/IP协议实际上就是在物理网上的一组完整的网络协议。
其中TCP是提供传输层服务,而IP则是提供网络层服务。
TCP/IP包括以下协议:
(结构如图1.1)
(图1.1)
IP:
网间协议(InternetProtocol)负责主机间数据的路由和网络上数据的存储。
同时为ICMP,TCP, UDP提供分组发送服务。
用户进程通常不需要涉及这一层。
ARP:
地址解析协议(AddressResolutionProtocol)
此协议将网络地址映射到硬件地址。
RARP:
反向地址解析协议(ReverseAddressResolutionProtocol)
此协议将硬件地址映射到网络地址
ICMP:
网间报文控制协议(InternetControlMessageProtocol)
此协议处理信关和主机的差错和传送控制。
TCP:
传送控制协议(TransmissionControlProtocol)
这是一种提供给用户进程的可靠的全双工字节流面向连接的协议。
它要为用户进程提供虚电路服务,并为数据可靠传输建立检查。
(注:
大多数网络用户程序使用TCP)
UDP:
用户数据报协议(UserDatagramProtocol)
这是提供给用户进程的无连接协议,用于传送数据而不执行正确性检查。
FTP:
文件传输协议(FileTransferProtocol)
允许用户以文件操作的方式(文件的增、删、改、查、传送等)与另一主机相互通信。
SMTP:
简单邮件传送协议(SimpleMailTransferProtocol)
SMTP协议为系统之间传送电子邮件。
TELNET:
终端协议(TelnetTerminalProcotol)
允许用户以虚终端方式访问远程主机
HTTP:
超文本传输协议(HypertextTransferProcotol)
TFTP:
简单文件传输协议(TrivialFileTransferProtocol)
2、TCP/IP特点
TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通常是在操作系统内核中实现。
因此用户一般不涉及。
编程时,编程界面有两种形式:
一、是由内核心直接提供的系统调用;二、使用以库函数方式提供的各种函数。
前者为核内实现,后者为核外实现。
用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。
图1.2是TCP/IP协议核心与应用程序关系图。
(图1.2)
二、专用术语
1、套接字
套接字是网络的基本构件。
它是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连听进程。
套接字存在通信区域(通信区域又称地址簇)中。
套接字只与同一区域中的套接字交换数据(跨区域时,需要执行某和转换进程才能实现)。
WINDOWS中的套接字只支持一个域——网际域。
套接字具有类型。
WINDOWSSOCKET1.1版本支持两种套接字:
流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)
2、WINDOWSSOCKETS实现
一个WINDOWSSOCKETS实现是指实现了WINDOWSSOCKETS规范所描述的全部功能的一套软件。
一般通过DLL文件来实现
3、阻塞处理例程
阻塞处理例程(blockinghook,阻塞钩子)是WINDOWSSOCKETS实现为了支持阻塞套接字函数调用而提供的一种机制。
4、多址广播(multicast,多点传送或组播)
是一种一对多的传输方式,传输发起者通过一次传输就将信息传送到一组接收者,与单点传送
(unicast)和广播(Broadcast)相对应。
Winsocket编程之套接字原理
一、客户机/服务器模式
在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Servermodel)。
该模式的建立基于以下两点:
1、非对等作用;2、通信完全是异步的。
客户机/服务器模式在操作过程中采取的是主动请示方式:
首先服务器方要先启动,并根据请示提供相应服务:
(过程如下)
1、打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。
2、等待客户请求到达该端口。
3、接收到重复服务请求,处理该请求并发送应答信号。
4、返回第二步,等待另一客户请求
5、关闭服务器。
客户方:
1、打开一通信通道,并连接到服务器所在主机的特定端口。
2、向服务器发送服务请求报文,等待并接收应答;继续提出请求……
3、请求结束后关闭通信通道并终止。
二、基本套接字
为了更好说明套接字编程原理,给出几个基本的套接字,在以后的篇幅中会给出更详细的使用说明。
1、创建套接字——socket()
功能:
使用前创建一个新的套接字
格式:
SOCKETPASCALFARsocket(intaf,inttype,intprocotol);
参数:
af:
通信发生的区域
type:
要建立的套接字类型
procotol:
使用的特定协议
2、指定本地地址——bind()
功能:
将套接字地址与所创建的套接字号联系起来。
格式:
intPASCALFARbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);
参数:
s:
是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
其它:
没有错误,bind()返回0,否则SOCKET_ERROR
地址结构说明:
structsockaddr_in
{
shortsin_family;//AF_INET
u_shortsin_port;//16位端口号,网络字节顺序
structin_addrsin_addr;//32位IP地址,网络字节顺序
charsin_zero[8];//保留
}
3、建立套接字连接——connect()和accept()
功能:
共同完成连接工作
格式:
intPASCALFARconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);
SOCKETPASCALFARaccept(SOCKETs,structsockaddrFAR*name,intFAR*addrlen);
参数:
同上
4、监听连接——listen()
功能:
用于面向连接服务器,表明它愿意接收连接。
格式:
intPASCALFARlisten(SOCKETs,intbacklog);
5、数据传输——send()与recv()
功能:
数据的发送与接收
格式:
intPASCALFARsend(SOCKETs,constcharFAR*buf,intlen,intflags);
intPASCALFARrecv(SOCKETs,constcharFAR*buf,intlen,intflags);
参数:
buf:
指向存有传输数据的缓冲区的指针。
6、多路复用——select()
功能:
用来检测一个或多个套接字状态。
格式:
intPASCALFARselect(intnfds,fd_setFAR*readfds,fd_setFAR*writefds,
fd_setFAR*exceptfds,conststructtimevalFAR*timeout);
参数:
readfds:
指向要做读检测的指针
writefds:
指向要做写检测的指针
exceptfds:
指向要检测是否出错的指针
timeout:
最大等待时间
7、关闭套接字——closesocket()
功能:
关闭套接字s
格式:
BOOLPASCALFARclosesocket(SOCKETs);
三、典型过程图
2.1面向连接的套接字的系统调用时序图
2.2无连接协议的套接字调用时序图
2.3面向连接的应用程序流程图
WindowsSocket1.1程序设计
一、简介
WindowsSockets是从BerkeleySockets扩展而来的,其在继承BerkeleySockets的基础上,又进行了新的扩充。
这些扩充主要是提供了一些异步函数,并增加了符合WINDOWS消息驱动特性的网络事件异步选择机制。
WindowsSockets由两部分组成:
开发组件和运行组件。
开发组件:
WindowsSockets实现文档、应用程序接口(API)引入库和一些头文件。
运行组件:
WindowsSockets应用程序接口的动态链接库(WINSOCK.DLL)。
二、主要扩充说明
1、异步选择机制:
WindowsSockets的异步选择函数提供了消息机制的网络事件选择,当使用它登记网络事件发生时,应用程序相应窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。
WindowsSockets提供了一个异步选择函数WSAAsyncSelect(),用它来注册应用程序感兴趣的网络事件,当这些事件发生时,应用程序相应的窗口函数将收到一个消息。
函数结构如下:
intPASCALFARWSAAsyncSelect(SOCKETs,HWNDhWnd,unsignedintwMsg,longlEvent);
参数说明:
hWnd:
窗口句柄
wMsg:
需要发送的消息
lEvent:
事件(以下为事件的内容)
值:
含义:
FD_READ
期望在套接字上收到数据(即读准备好)时接到通知
FD_WRITE
期望在套接字上可发送数据(即写准备好)时接到通知
FD_OOB
期望在套接字上有带外数据到达时接到通知
FD_ACCEPT
期望在套接字上有外来连接时接到通知
FD_CONNECT
期望在套接字连接建立完成时接到通知
FD_CLOSE
期望在套接字关闭时接到通知
例如:
我们要在套接字读准备好或写准备好时接到通知,语句如下:
rc=WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);
如果我们需要注销对套接字网络事件的消息发送,只要将lEvent设置为0
2、异步请求函数
在BerkeleySockets中请求服务是阻塞的,WINDOWSSICKETS除了支持这一类函数外,还增加了相应的异步请求函数(WSAAsyncGetXByY();)。
3、阻塞处理方法
WindowsSockets为了实现当一个应用程序的套接字调用处于阻塞时,能够放弃CPU让其它应用程序运行,它在调用处于阻塞时便进入一个叫“HOOK”的例程,此例程负责接收和分配WINDOWS消息,使得其它应用程序仍然能够接收到自己的消息并取得控制权。
WINDOWS是非抢先的多任务环境,即若一个程序不主动放弃其控制权,别的程序就不能执行。
因此在设计WindowsSockets程序时,尽管系统支持阻塞操作,但还是反对程序员使用该操作。
但由于SUN公司下的BerkeleySockets的套接字默认操作是阻塞的,WINDOWS作为移植的SOCKETS也不可避免对这个操作支持。
在WindowsSockets实现中,对于不能立即完成的阻塞操作做如下处理:
DLL初始化→循环操作。
在循环中,它发送任何WINDOWS消息,并检查这个WindowsSockets调用是否完成,在必要时,它可以放弃CPU让其它应用程序执行(当然使用超线程的CPU就不会有这个麻烦了^_^)。
我们可以调用WSACancelBlockingCall()函数取消此阻塞操作。
在WindowsSockets中,有一个默认的阻塞处理例程BlockingHook()简单地获取并发送WINDOWS消息。
如果要对复杂程序进行处理,WindowsSockets中还有WSASetBlockingHook()提供用户安装自己的阻塞处理例程能力;与该函数相对应的则是SWAUnhookBlockingHook(),它用于删除先前安装的任何阻塞处理例程,并重新安装默认的处理例程。
请注意,设计自己的阻塞处理例程时,除了函数WSACancelBlockingHook()之外,它不能使用其它的WindowsSocketsAPI函数。
在处理例程中调用WSACancelBlockingHook()函数将取消处于阻塞的操作,它将结束阻塞循环。
4、出错处理
WindowsSockets为了和以后多线程环境(WINDOWS/UNIX)兼容,它提供了两个出错处理函数来获取和设置当前线程的最近错误号。
(WSAGetLastEror()和WSASetLastError())
5、启动与终止
使用函数WSAStartup()和WSACleanup()启动和终止套接字。
三、WindowsSockets网络程序设计核心
我们终于可以开始真正的WindowsSockets网络程序设计了。
不过我们还是先看一看每个WindowsSockets网络程序都要涉及的内容。
让我们一步步慢慢走。
1、启动与终止
在所有WindowsSockets函数中,只有启动函数WSAStartup()和终止函数WSACleanup()是必须使用的。
启动函数必须是第一个使用的函数,而且它允许指定WindowsSocketsAPI的版本,并获得SOCKETS的特定的一些技术细节。
本结构如下:
intPASCALFARWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);
其中wVersionRequested保证SOCKETS可正常运行的DLL版本,如果不支持,则返回错误信息。
我们看一下下面这段代码,看一下如何进行WSAStartup()的调用
WORDwVersionRequested;//定义版本信息变量
WSADATAwsaData;//定义数据信息变量
interr;//定义错误号变量
wVersionRequested=MAKEWORD(1,1);//给版本信息赋值
err=WSAStartup(wVersionRequested,&wsaData);//给错误信息赋值
if(err!
=0)
{
return;//告诉用户找不到合适的版本
}
//确认WindowsSocketsDLL支持1.1版本
//DLL版本可以高于1.1
//系统返回的版本号始终是最低要求的1.1,即应用程序与DLL中可支持的最低版本号
if(LOBYTE(wsaData.wVersion)!
=1||HIBYTE(wsaData.wVersion)!
=1)
{
WSACleanup();//告诉用户找不到合适的版本
return;
}
//WindowsSocketsDLL被进程接受,可以进入下一步操作
关闭函数使用时,任何打开并已连接的SOCK_STREAM套接字被复位,但那些已由closesocket()函数关闭的但仍有未发送数据的套接字不受影响,未发送的数据仍将被发送。
程序运行时可能会多次调用WSAStartuo()函数,但必须保证每次调用时的wVersionRequested的值是相同的。
2、异步请求服务
WindowsSockets除支持BerkeleySockets中同步请求,还增加了了一类异步请求服务函数WSAAsyncGerXByY()。
该函数是阻塞请求函数的异步版本。
应用程序调用它时,由WindowsSocketsDLL初始化这一操作并返回调用者,此函数返回一个异步句柄,用来标识这个操作。
当结果存储在调用者提供的缓冲区,并且发送一个消息到应用程序相应窗口。
常用结构如下:
HANDLEtaskHnd;
charhostname="rs6000";
taskHnd=WSAAsyncBetHostByName(hWnd,wMsg,hostname,buf,buflen);
需要注意的是,由于Windows的内存对像可以设置为可移动和可丢弃,因此在操作内存对象是,必须保证WIindowsSocketsDLL对象是可用的。
3、异步数据传输
使用send()或sendto()函数来发送数据,使用recv()或recvfrom()来接收数据。
WindowsSockets不鼓励用户使用阻塞方式传输数据,因为那样可能会阻塞整个Windows环境。
下面我们看一个异步数据传输实例:
假设套接字s在连接建立后,已经使用了函数WSAAsyncSelect()在其上注册了网络事件FD_READ和FD_WRITE,并且wMsg值为UM_SOCK,那么我们可以在Windows消息循环中增加如下的分支语句:
caseUM_SOCK:
switch(lParam)
{
caseFD_READ:
len=recv(wParam,lpBuffer,length,0);
break;
caseFD_WRITE:
while(send(wParam,lpBuffer,len,0)!
=SOCKET_ERROR)
break;
}
break;
4、出错处理
Windows提供了一个函数来获取最近的错误码WSAGetLastError(),推荐的编写方式如下:
len=send(s,lpBuffer,len,0);
of((len==SOCKET_ERROR)&&(WSAGetLastError()==WSAWOULDBLOCK)){...}
基于VisualC++的WinsockAPI研究
为了方便网络编程,90年代初,由Microsoft联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即WindowsSockets规范,它不是一种网络协议,而是一套开放的、支持多种协议的Windows下的网络编程接口。
现在的Winsock已经基本上实现了与协议无关,你可以使用Winsock来调用多种协议的功能,但较常使用的是TCP/IP协议。
Socket实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有Socket接口的计算机通信。
应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。
微软为VC定义了Winsock类如CAsyncSocket类和派生于CAsyncSocket的CSocket类,它们简单易用,读者朋友当然可以使用这些类来实现自己的网络程序,但是为了更好的了解WinsockAPI编程技术,我们这里探讨怎样使用底层的API函数实现简单的Winsock网络应用程式设计,分别说明如何在Server端和Client端操作Socket,实现基于TCP/IP的数据传送,最后给出相关的源代码。
在VC中进行WINSOCK的API编程开发的时候,需要在项目中使用下面三个文件,否则会出现编译错误。
1.WINSOCK.H:
这是WINSOCKAPI的头文件,需要包含在项目中。
2.WSOCK32.LIB:
WINSOCKAPI连接库文件。
在使用中,一定要把它作为项目的非缺省的连接库包含到项目文件中去。
3.WINSOCK.DLL:
WINSOCK的动态连接库,位于WINDOWS的安装目录下。
一、服务器端操作socket(套接字)
1)在初始化阶段调用WSAStartup()
此函数在应用程序中初始化WindowsSocketsDLL,只有此函数调用成功后,应用程序才可以再调用其他WindowsSocketsDLL中的API函数。
在程式中调用该函数的形式如下:
WSAStartup((WORD)((1<<8|1),(LPWSADATA)&WSAData),其中(1<<8|1)表示我们用的是WinSocket1.1版本,WSAata用来存储系统传回的关于WinSocket的资料。
2)建立Socket
初始化WinSock的动态连接库后,需要在服务器端建立一个监听的Socket,为此可以调用Socket()函数用来建立这个监听的Socket,并定义此Socket所使用的通信协议。
此函数调用成功返回Socket对象,失败则返回INVALID_SOCKET(调用WSAGetLastError()可得知原因,所有WinSocket的函数都可以使用这个函数来获取失败的原因)。
SOCKETPASCALFARsocket(intaf,inttype,intprotocol)
参数:
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。
intPASCALFARbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);
参数:
s:
Socket对象名;
name:
Socket的地址值,这个地址必须是执行这个程式所在机器的IP地址;
namelen:
name的长度;
如果使用者不在意地址或端口的值,那么可以设定地址为INADDR_ANY,及Port为0,WindowsSockets会自动将其设定适当之地址及Port(1024到5000之间的值)。
此后可以调用getsockname()函数来获知其被设定的值。
4)监听
当服务器端的Socket对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。
listen()函数使服务器端的Socket进入监听状态,并设定可以建立的最大连接数(目前最大值限制为5,最小值为1)。
该函数调用成功返回0,否则返回SOCKET_ERROR。
intPASCALFARlisten(SOCKETs,intbacklog);
参数:
s:
需要建立监听的Socket;
backlog:
最大连接个数;
服务器端的Socket调用完listen()后,如果此时客户端调用connect()函数提出连接申请的话,Server端必须再调用accept()函数,这样服务器端和客户端才算正式完成通信程序的连接动作。
为了知道什么时候客户端提出连接要求,从而服务器端的Socket在恰当的时候调用
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- WinSock 网络 编程 实用 宝典