您的位置:首页 > 编程语言

利用Winsock进行IP通信编程

2007-04-15 11:09 330 查看
1 关于WinSock
Windows Sockets (即Winsock)是在Windows上进行网络通信编程的API接口,也是Windows网络编程的事实标准。Winsock建立在Berkeley套接口之上, 包括了大量的Unix环境下开发的Berkeley函数,提供了习惯于Unix套接口编程的程序员极为熟悉的环境,并且简化了移植现有的基于套接口的应用程序源代码的工作。另外,Winsock还包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。Winsock有开发组件和运行组件两大部分,开发组件是供开发应用程序使用,而运行组件是Winsock应用程序接口的动态连接库一一Winsock.DIl, 应用程序在执行时通过装入DLL实现网络通信功能。
由于Windows的基于消息的特点,Winsock和BSD套接口相比,有如下一些新的扩充:异步选择机制、异步请求函数、阻塞处理方法、错误处理、启动和终止。在Unix中I/O是同步的,所以Berkeley Sockets没有任何异步函数, 可是Windows是一种异步操作系统, 异步选择函数WSAAsyncSelect()的使用是实现Windows网络实时通信的关键。
Socket实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具Socket接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。应用程序调用Windows Sockets的API实现相互之间的通信。Windows Sockets又利用下层的网络通信协议功能和操作系统调用实现实际的通信工作。它们之间的关系如图1。

在应用开发中就像使用文件句柄一样,可以对Socket句柄进行读、写操作。现在,Socket接口已经成为TCP/IP网络最为通用的API,也是在Internet上进行应用开发最为通用的API。

2 因特网的网间通信
因特网的互连体系结构可以称作是无连结型网络层同构互连的典范。现在,我们来简单了解一下因特网的网间通信。我们知道,好的网络互连体系结构应该能实现简单而有效的网间通信过程。当两个端系统进行通信时,传输层将来自应用系统的用户数据流分段递交给网际互连层一一IP层。
IP层完成的主要任务就是:实现在“超级的”互连网上对无连结数据报的传输、数据报寻径以及差错与故障的处理。实现数据报传输功能的核心就是网际协议IP,而网间控制报文协议ICMP是完成IP层主要控制功能的辅助协议。能够提供差错控制与报告、拥塞控制和路径控制以及故障诊断。IP层实体将数据段分割成更小的数据单元并封装成IP数据报,IP数据报在由端系统和路由器构成的虚拟网上传输,到达目的端系统后再由IP层实体对它们进行排序、卸包和组装,恢复成原来的数据段递交给目的传输层,传输层把所有数据段重装恢复成原来的用户数据流后递交给目的应用系统。虚拟网上的路径选择服从于“间接路由”协议机制,各个子网内的真实传输过程的路径选择则服从于该子网内的“直接路由”协议机制。这里就涉及到了IP路由技术。
在数据报的传输过程中,每个路由器和端系统都有路由表,但并不指导整个路由,其中仅仅定义了从某个路由器到宿端的下一个路由器的路径。路由表中记入的路由信息则是通过路由之间、根据具体的路由算法和路由协议来互相交换控制信息而得到的。具体IP路由过程为:IP实体先将从某个接口上接收到的IP数据报的目的IP地址与本地地址比较,决定转发还是接受,如果确定转发,则IP协议实体从数据报的目的IP地址中抽出目的网络号,然后在路由表中找到对应这个网络号的表项,即可确定直接还是间接路由、网络接口号、下一个路由器IP地址(若为间接路由)。假如表中没有对应的网络号,则启用缺省网络号的表项中的路由地址来转发此数据报。

3 WinSock通信编程
(1)Socket编程原理
Windows Sockets规范是建立在Bekeley套接口模型上的。这个模型现在已是TCP/IP网络的标准。套接口有三种类型:流式套接口,数据报套接口及原始套接口.流式套接口定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输;数据报套接口定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错;原始套接口允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等。无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间的相互作用,面向连接服务器处理的请求往往比较复杂,不是一来一去的请求应答所能鳃决的,而且往往是并发服务器。套接口工作过程如下:服务器首先启动,通过调用socket()建立一个套接口,然后调bind()将该套接口和本地网络地址联系在一起,再调用listen()使套接口做好侦听的准备,并规定它的请求队列的长度,之后就调用accept()来接收连接,客户在建立套接口后就可调用connect()和服务器建立连接,连接一旦建立,客户机和服务器之间就可以通过调用read()和write()来发送和接收数据,最后,待数据传送结束后,双方调用close()关闭套接口。
在网络编程中最常用的方案便是客户机/服务器模型。基于连接协议(流套接字)的服务是设计客户机/服务器应用程序时的标准,但有些服务也是可以通过无连接协议(数据报套接字)提供的。一般在使用中,面向连接协议的Socket编程模型应用最为广泛,因为面向连接协议提供了一系列的
数据纠错功能,可以保证在网络上传输的数据及时、无误地到达对方。

(2)基于Winsock的应用开发介绍
Winsock是一组用C语言写的API,用于通过Internet进行数据传输。通过Winsock编程可以获得更大的灵活性。可是,Socket提供的原语数目很多,接口参数复杂,对一般用户尤其非网络专业用户,直接调用Socket有较大的困难,要有一个熟悉和理解的过程。在Win2000,WindowsNT 进行
Winsock开发使用的编程语言有很多,VC++,Java,Delphi,VB等。其中VC的使用最普遍,和Winsock
结合最紧密的。并且VC++对原来的Windows Sockets库函数进行了一系列封装,继而产生了CAsynSocket、CSocket、CSocketFile等类,它们封装着有关Socket的各种功能,使编程变得更加简单。另外,也可以使用Winsock最基本的API函数进行编程。在VC中进行Winsock的API编程开发,需要使用到下面三个文件:
~Winsock.H:这是WinsockAPI的头文件;
~WINSOCK32.LIB:NSOCK API连接库文件,在使用中,一定要把它作为项目的非缺省的连接库包含到项目文件中去;
~Winsock.DLL:Winsock的动态连接库,位于Windows的安装目录下。

(3)编程实例
以Ping程序为例, 我们来讨论如何利用Winsock在WindowsNT或Win2000上进行IP通信编程。Ping程序的目的是为了测试另一台主机是否可达,该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显应答。ICMP回显请求和回显应答报文如图2所示。

一般来说,如果不能Ping到某台主机,那么就不能Telnet或者FtP到那台主机,反过来,如果不能Telnet到某台主机,那么通常可以用Ping程序来确定问题出在哪里。Ping程序还能通过在ICMP报文数据中存放发送请求的时间值来计算出到这台主机的往返时间,以表明该主机离我们有多远。现在,我们就来修改Ping程序,使它能够记录路由。Ping程序为我们提供了查看IP记录路由选项的机会,即-R选项。下面是对其中部分程序做出的一些注释(注意Winsock的使用):

BOOL bRecordRoute;/*如果带选项一r,则该变量为真*/
int datasize:
char lpdest; / 保存目的IP*/
void usage(char progname)
{
prinf(”usage:ping—r【data size】/n );
prinf(” 一r record route/n');//记录路由
prinf(“ host remote machine to ping/n”);
prinf(“datasize can be up to 1 KB /n”);
ExitProcess(-1);
1
void FillICMPData(char icmp_data.int datasize)
{
IcmpHeader icmp_hdr=NULk
char *datapart = NULL
icmp_hdr = (IcmpHeader *)icmp_data;
icmp_hdr->i_type = ICMP_ ECHO;
icmp_hdr->i_code=0;
//i_type =8,i_code=O表示“请求回显”报文,i_type=0,i_code=0~ 表示“回显应答”报文

icmp_hdr->i_id=(USHORT)GetCurrentProcessld();
/*得到当前进程的id号,它是唯一的,因为ICMP协议是无端口的,需要用i_id来判断接受到的ICMP报文是否是自己需要的*/
icmp_hdr->i_cksum = O;
icmp_ hdr->i_seq = O;
datapart = icmp_data+sizeof(1cmpHeader);
memset(datapart,'E ,datasize—sizeof(IcmpHeader));
}

void DecodelPOptions(char buf,int bytes) //然后打印出记录的路由
{
Ip0ptionHeader *ipopt = NULL;
IN_ ADDR inaddr;
int i;
H0STENT host=NULL;

Ipopt = (IpOptionHeader)(buf+2O);
//buf指向IP报文开始的地方,buf+20指向IP选项开始的位置
prinf(“RR:”);
for(i=O;i<(ipopt一>ptr/4)-1;i++)
{
inaddr.S_un.S_addr=ipopt->addr[i];
if(i! = 0)
prinf(" “);
host = gethostbyaddr ((char *)&inaddr.S_un.S_addr, sizeof(inaddr.S_un.S_addr),AF_INET);
/*gethostbyaddr函数通过IP地址解析得主机名*/
if(host)
prinf(“(%-15s)%s\n”,inet_ntoa(inaddr),host->h_name);
else
printf("(%-I5s)/n ,inet_ntoa(inaddr));
)
return ;
}

int main (intargc.char argv)
{
WSADATA wsaData;
SOCKET sockRaw=INVALID_SOCKET;
stmct sockaddr_ in dest/*Ipv4套接口地址结构*/,from;
int bread,
fromlen = sizeof(from),
timeout= 1000. ret;
char icmp_data= NULL,
recvbuf=NULL:
unsigned int addr=O:
USHORT seq_no=O:
struct hostent hp = NULL;
IpOptionHeader ipopt;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=o)
{
printf("WSAStartup()failed:%dkn ,GetLastError());
return-1:
}
ValidateArgs(argc,argv);
//解析输入的参数,给变量赋值
sockRaw=WSASocket (AF_INET,SOCK_RAW, IPPROTO— ICMP。NULL,0,WSA_FLAG_OVERLAPPED);
/*生成一个Socket,其中指定SOCK_RAW 和IPPROTO— ICMP使得可以用原始套接口发送ICMP “请求回显”和“回显应答”这两种报文*/
if rsockRaw ==INVALID_ SOCKET)
{
print~'WSASocket()failed:vTtttkn",WSAGetLastError());
return-1:
}
if(bRecordRoute)/*为真说明用户需要记录路由
{
ZeroMemory(&ipopt,sizeof(ipopt));
//先清空ipopt结构为0
ipopt.code=IP— RECORD— ROUTE;
//因为要记录IP路由所以需把ipopt.code设为7*/
ipopt.ptr=4:
ipopt.1en =39:
ret=setsockopt(sockRaw,IPPROTO—IP,IP OPTIONS
(char)&ipopt,sizeof(ipopt));
/*带IPPROTO—IP和IP_ POTIONS参数表示通过setsockopt函数设置要发送的IP报文的选项字段为ipopt*/
if(ret==SOCKET—ERROR)
{
prinf(”setsoekoptfIP_Om'IONS)failed:%d /n”);
WSAGetLastErmr();
}
}

以上的程序在发送出去的IP数据报中设置IP RR选项,这样,每个处理该数据报的路由器都把它IP地址放入选项字段中。当数据报到达目的端时,lP地址清单应该复制到ICMP回显应答中,那么返回途中所经过的路由器地址也被加入清单中。一旦Ping程序收到回显应答,它就打印出这份IP地址清单。这个程序最大的问题在于IP首部只有有限的空间来存放IP地址,最多只能存放9个IP地址。对于早期的ARPANET来说,9个地址似乎是多了,但是现在看来是非常有限的。除了这些缺点,记录路由选项工作得很好,为详细查看如何处理IP选项提供了一个机会。其中,该程序调用socket函数创建了一个待使用的套接字,并通过这个函数的参数定义了套接字的类型、网络地址类型和使用的协议。经调试,该程序在VC++6.0下编译通过,在使用TCP/IP协议的WindowsNT 和Win2oo0局域网上运行良好,运行时,在DOS环境下在生成的可执行文件后加一r, 即可列出IP地址清单。
总之,从这个程序我们可以看出,一个完整的网络通信必须具备协议、本地地址、本地端口、远地地址和远地端口5个基本要素,在创建套接字时确定了协议类型,而在绑定套接字时确定本地地址和本地端口,远地地址和远地端口的确认,则需要通过调用连接和通信类函数来建立Socket连接和通过套接字进行网络通信。

结语
由Microsoft联合其他几家公司共同制定的一套Windows下的网络编程接口,即Windows Sockets规范,是一套开放的、支持多种协议的Windows下的网络编程接口。本文以可以记录路由的Ping程序为例,讨论了在WindowsNT或Win2000环境下,利用Winsock进行IP通信编程的相关知识,并且程序调试成功。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: