简单的 Winsock 应用程式设计3.docx

上传人:b****6 文档编号:6521295 上传时间:2023-01-07 格式:DOCX 页数:13 大小:20.84KB
下载 相关 举报
简单的 Winsock 应用程式设计3.docx_第1页
第1页 / 共13页
简单的 Winsock 应用程式设计3.docx_第2页
第2页 / 共13页
简单的 Winsock 应用程式设计3.docx_第3页
第3页 / 共13页
简单的 Winsock 应用程式设计3.docx_第4页
第4页 / 共13页
简单的 Winsock 应用程式设计3.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

简单的 Winsock 应用程式设计3.docx

《简单的 Winsock 应用程式设计3.docx》由会员分享,可在线阅读,更多相关《简单的 Winsock 应用程式设计3.docx(13页珍藏版)》请在冰豆网上搜索。

简单的 Winsock 应用程式设计3.docx

简单的Winsock应用程式设计3

简单的Winsock应用程式设计(3)

 

 

在前两期的文章中,笔者介绍了如何在Winsock环境下建立主从架构的

TCPSocket,以及如何利用Socket来收送资料;今天,我们接著来看一看如何

利用Winsock所提供的函式来取得一些基本的网路资料,包括我们本身主机的

名称是什麽、系统主动指定给我们的Socket的IP位址及portnumber、我们的

Socket所连接的对方是谁、如何查得某些主机的IP位址或名称、以及某些

well-known服务(如ftp、telnet等)所用的portnumber是哪一个等等。

 

今天我们使用的展示程式是笔者以前所撰写的一个针对Winsock1.1的46

个函式做测试或教学用的程式,有兴趣了解46个函式该如何呼叫的读者,可用

anonymousftp方式到「.tw」的「UPLOAD/WINKING/JNLIN」

目录下取得此程式的执行档及原始程式码,档名为hello.*。

读者们也可利用

hello程式来模拟Server或Client程式,以验证我们所做的动作。

 

【如何知道我们所使用的local主机名称】

 

通常我们都会帮我们自己所使用的这台主机设定一个名称;在程式中,我

们也可以透过Winsock所提供的一个称为gethostname()的函式来取得这一个主

机名称。

 

◎gethostname():

获取目前使用者使用的localhost的名称。

格式:

intPASCALFARgethostname(charFAR*name,intnamelen);

参数:

name用来存放localhost名称的暂存区

namelenname的大小

传回值:

成功-0

失败-SOCKET_ERROR(呼叫WSAGetLastError()可得知原因)

说明:

此函式用来获取localhost的名称。

 

在程式中我们呼叫的方法如下:

 

gethostname((charFAR*)hname,sizeof(hname))

 

读者们如果使用过TrumpetWinsock的话,可能知道Trumpet的环境设定中

并没有让我们设定localhost名称的栏位,所以在执行一些PublicDomain的

Winsock应用程式(如ws_ping、wintalk)时,在呼叫gethostname()时会产生错

误;解决的方法是在Trumpet的「hosts」档中加上您的主机IP位址及名称,

那麽呼叫这个函式时就不会再产生错误了。

 

【如何得知系统主动指定给我们的IP位址及portnumber】

 

以前的文章中,笔者曾提到Client端的TCPSocket在呼叫connect()函式去

连接Server端之前,可以呼叫bind()函式来指定Client端Socket所用的IP位址

及portnumber;但是一般而言,我们Client端并不需要呼叫bind()来指定特定

的IP位址及portnumber的,而是交由系统主动帮我们的Socket设定IP位址及

portnumber(呼叫connect()函式时)。

但是我们如何得知系统指定了什麽IP

位址及portnumber给我们呢?

这就要借助getsockname()这个函式了。

 

◎getsockname():

获取Socket的Local位址及portnumber资料。

格式:

intPASCALFARgetsockname(SOCKETs,

structsockaddrFAR*name,intFAR*namelen);

参数:

sSocket的识别码

name存放此Socket的Local位址的暂存区

namelenname的长度

传回值:

成功-0

失败-SOCKET_ERROR(呼叫WSAGetLastError()可得知原因)

说明:

此函式是用来取得已设定位址或已连接之Socket的本端位址资料。

是此Socket被设定为INADDR_ANY,则需等真正建立连接成功後才会传回正确

的位址。

 

在程式中呼叫的方法为:

 

structsockaddr_insa;

intsalen=sizeof(sa);

getsockname(sd,(structsockaddrFAR*)&sa,&salen)

 

【如何知道和我们的Socket连接的对方是谁】

 

连接的Socket是有两端的,所以相对於getsockname()函式,Winsock也提

供了一个getpeername()函式,来让我们获得与我们连接的对方的IP位址与port

number。

 

◎getpeername():

获取连接成功之Socket的对方IP位址及portnumber。

格式:

intPASCALFARgetpeername(SOCKETs,

structsockaddrFAR*name,intFAR*namelen);

参数:

sSocket的识别码

name储存与此Socket连接的对方IP位址的暂存区

namelenname的长度

传回值:

成功-0

失败-SOCKET_ERROR(呼叫WSAGetLastError()可得知原因)

说明:

此函式可用来取得已连接成功的Socket的彼端之位址资料。

 

呼叫的方式如下:

 

structsockaddr_insa;

intsalen=sizeof(sa);

getpeername(sd,(structsockaddrFAR*)&sa,&salen)

 

现在我们仍然利用WinKing来当我们的WinsockStack,并利用它所提供的

工具来观察Sockets的连结及资料是否正确。

 

由图1,我们可以由WinKing的视窗看到我们设定这台主机的名称是

「vincent」,IP位址是「140.92.61.24」。

我们并利用两个hello程式,一个当

成Client(画面右边打开者),一个当成Server(画面左边最小化者)。

Server

所用的portnumber是「7016」;Client并没有呼叫bind()来指定port

number,而是呼叫connect()时由系统指定。

 

我们呼叫gethostname(),得到的答案是「vincent」;而Client呼叫

getsockname()得到自己的IP位址是「140.92.61.24」,portnumber是「2110」

(笔者以前曾提过,由系统主动指定的portnumber会介於1024到5000间);

再呼叫getpeername()得到与Client连接的Server端IP位址是「140.92.61.24」

(因为我们的Client和Server都在同一台主机),portnumber是「7016」。

然没错!

(由WinKing的Sockets'Status视窗亦可观察到相互连接的Sockets资

料,与我们呼叫函式所得结果相同)

 

(图1)利用hello程式来模拟Client和Server

 

读者必须注意一点,getsockname()及getpeername()所取得的IP位址及port

number都是networkbyteorder,而不是hostbyteorder;如果您想转成hostbyte

order,就必须借助ntohl()及ntohs()两个函式。

而我们能看到IP位址以「字

串」方式表达出来,则又是利用了inet_ntoa()函式;相对地,我们也可利用

inet_addr()函式将字串方式的IP位址转换成in_addr格式(networkbyteorder的

unsignedlong)。

 

◎inet_ntoa():

将一网路位址转换成「点格式」字串。

格式:

charFAR*PASCALFARinet_ntoa(structin_addrin);

参数:

in一个代表Internethost位址的结构

传回值:

成功-一个代表位址的「点格式」(dotted)字串

失败-NULL

说明:

此函式将一Internet位址转换成「a.b.c.d」字串格式。

 

◎inet_addr():

将字串格式的位址转换成32位元in_addr的格式。

格式:

unsignedlongPASCALFARinet_addr(constcharFAR*cp);

参数:

cp一个代表IP位址的「点格式」(dotted)字串

传回值:

成功-一个代表Internet位址的unsignedlong

失败-INADDR_NONE

说明:

此函式将一「点格式」的位址字串转换成适用之Intenet位址。

「点格式」字串可为以下四种方式之任一:

(i)a.b.c.d(ii)a.b.c(iii)a.b(iv)a

 

图1的hello程式中,我们将Local资料写到dispmsg中,再显示出来;其

用法如下:

 

wsprintf((LPSTR)dispmsg,"OK!

localip=%s,localport=%d",

inet_ntoa(sa.sin_addr),ntohs(sa.sin_port));

 

【Winsock提供的资料库函式】

 

Winsock也提供了同步与非同步的网路资料库函式;不过读者们要知道,此

处的资料库指的并非如Informix,Oracle等商业用途的资料库系统,而是指主机

IP位址及名称、well-known服务的名称及Socket型态及所用的portnumber、

以及协定(protocol)名称及代码等。

 

【同步资料库函式】

 

首先我们来看一下第一组:

gethostbyname()及gethostbyaddr()函式

 

这两个函式的用途是让我们可以由某个主机名称求得它的IP位址,或是由

它的IP位址求得它的名称。

一般我们经常会用到的是由名称求得IP位址;因

为很少人会去记某台机器的IP位址的,另外TCP/IP封包的IPheader上也必须

记载送、收主机的IP位址,而不是主机名称。

 

◎gethostbyname():

利用某一host的名称来获取该host的资料。

格式:

structhostentFAR*PASCALFAR

gethostbyname(constcharFAR*name);

参数:

namehost的名称

传回值:

成功-指向一个hostent结构的指标

失败-NULL(呼叫WSAGetLastError()可得知原因)

说明:

此函式是利用host名称来获取该主机的其他资料,如host的位址、

别名,位址的型态、长度等。

 

◎gethostbyaddr():

利用某一host的IP位址来获取该host的资料。

格式:

structhostentFAR*PASCALFAR

gethostbyaddr(constcharFAR*addr,intlen,inttype);

参数:

addrnetwork排列方式的位址

lenaddr的长度

typePF_INET(AF_INET)

传回值:

成功-指向一个hostent结构的指标

失败-NULL(呼叫WSAGetLastError()可得知原因)

说明:

此函式是利用IP位址来获取该主机的其他资料,如host的名称、别

名,位址的型态、长度等。

 

程式中呼叫的方式分别如下:

 

charhost_name[30];

structhostentfar*htptr;

/*假设host_name的值已先设定为我们要求得资料的主机名称*/

htptr=(structhostentFAR*)gethostbyname((charfar*)host_name)

 

structin_addrhost_addr;

structhostentfar*htptr;

/*假设host_addr的值已先设定为我们要求得资料的主机的networkbyte

order方式的IP位址*/

htptr=(structhostentFAR*)gethostbyaddr((charfar*)&host_addr,4,

PF_INET)

 

一般言,程式中呼叫到gethostbyname()及gethostbyaddr()时,Winsock

Stack会先在local的「hosts」档中找看看是否有这个主机的资料;如果没有,

则可能再透过「领域名称服务」(DomainNameService)的功能,向「名称伺

服器」(NameServer)查询;所以呼叫这两个函式时,有时会等一下子才获得

答覆。

如果您想让程式执行快一些的话,可将常用主机的资料放在hosts档中,

这样就不必透过DNS去查询了。

 

接下来我们来看getservbyname()及getservbyport()这两个函式。

 

大部份的读者应该都用过telnet、mail、ftp、news等服务应用程式;这些应

用程式的协定,比如服务名称、伺服器端所用的portnumber、以及Socket的型

态,都是固定的;这些资料,我们就可以利用getservbyname()或getservbyport()

来取得,而不必刻意去记颂它们。

 

◎getservbyname():

依照服务(service)名称及通讯协定(tcp/udp)来获取该

服务的其他资料。

格式:

structservent*PASCALFAR

getservbyname(constcharFAR*name,constcharFAR*proto);

参数:

name服务名称

proto通讯协定名称

传回值:

成功-一指向servent结构的指标

失败-NULL(呼叫WSAGetLastError()可得知原因)

说明:

利用服务名称及通讯协定来获得该服务的别名、使用的port号码

等。

 

◎getservbyport():

依照服务(service)的port号码及通讯协定(tcp/udp)来

获取该服务的其他资料。

格式:

structservent*PASCALFAR

getservbyport(intport,constcharFAR*proto);

参数:

port服务的port编号

proto通讯协定名称

传回值:

成功-一指向servent结构的指标

失败-NULL(呼叫WSAGetLastError()可得知原因)

说明:

利用port编号及通讯协定来获得该服务的名称、别名等。

 

程式中的使用方法分别为:

 

charserv_name[20];

charproto[10];

structserventfar*svptr;

/*假设serv_name及proto已先设好服务名称及通讯协定*/

svptr=(structserventFAR*)getservbyname((charfar*)serv_name,(charfar

*)proto)

 

intserv_port;

charproto[10];

structserventfar*svptr;

/*假设serv_port及proto已先设好服务所用的portnumber及通讯协定*/

svptr=(structserventFAR*)getservbyport(htons(serv_port),(charfar

*)proto))

 

Winsock环境下,我们能够查询到的服务资料都是存放在local的

「services」档中;这个档所存放的都是well-known的服务,基本上我们是不需

去更改它的。

读者也可以将自己提供的服务加到这个档中,不过您所用的服务

资料要公诸於世,不然别人的services档中可是没有您的服务的资料哟。

 

最後的这组getprotobyname()及getprotobynumber()函式是用来取得一些

「协定」的资料,比如tcp、udp、igmp等。

一般而言,我们是不太会用到的。

 

◎getprotobyname():

依照通讯协定(protocol)的名称来获取该通讯协定的其

他资料。

格式:

structprotoentFAR*PASCALFAR

getprotobyname(constcharFAR*name);

参数:

name通讯协定名称

传回值:

成功-一指向protoent结构的指标

失败-NULL(呼叫WSAGetLastError()可得知原因)

说明:

利用通讯协定的名称来得知该通讯协定的别名、编号等资料。

 

◎getprotobynumber():

依照通讯协定的编号来获取该通讯协定的其他资料。

格式:

structprotoentFAR*PASCALFAR

getprotobynumber(intnumber);

参数:

number以hostorder排列方式的通讯协定编号

传回值:

成功-一指向protoent结构的指标

失败-NULL(呼叫WSAGetLastError()可得知原因)

说明:

利用通讯协定的编号来得知该通讯协定的名称、别名等资料。

 

程式中呼叫方式分别如下:

 

structprotoentfar*ptptr;

charproto_name[20];

/*假设proto_name已先设好协定名称*/

ptptr=(structprotoentFAR*)getprotobyname((charfar*)proto_name)

 

structprotoentfar*ptptr;

intproto_num;

/*假设proto_num已先设好协定编号*/

ptptr=(structprotoentFAR*)getprotobynumber(proto_num)

 

WinsockStack对於应用程式呼叫getprotobyname()及getprotobynumber()的

资料,是取自於local的「protocol」档;如无需要,我们也不用去变更这个档

案的内容。

 

(图2)hello程式呼叫同步资料库函式

 

【非同步资料库函式】

 

Winsock1.1针对前面笔者所描述的6个同步资料库函式,也提供了相对的

6个非同步资料库函式,它们分别是WSAAsyncGetHostByName()、

WSAAsyncGetHostByAddr()、WSAAsyncGetServByName()、

WSAAsyncGetServByPort()、WSAAsyncGetProtoByName()、

WSAAsyncGetProtoByNumber()。

 

由於它们取得的资料与同步资料库函式相同,所以笔者仅以

WSAAsyncGetHostByName()为例,说明这些非同步函式,并告诉各位读者,同

步和非同步资料库函式不同的地方。

 

由字面来看,「非同步」的意思就是我们发出问题时,并不会马上得到答

覆,而等到系统取到资料时再告知我们。

没错,这些非同步资料库函式的作用

就是这样。

和WSAAsyncSelect()函式一样,我们要告诉Winsock系统一个接受

通知讯息的视窗及讯息代码,以便系统通知我们。

 

我们呼叫同步资料库函式时,return值是一个指到相对资料的暂存区,而这

个资料暂存区是由系统所提供的;但是呼叫非同步资料库函式时,我们必须自

己准备资料暂存区,并将此暂存区的位址当成参数,传给系统,以便系统用来

储存取到的资料。

读者们必须特别注意一点:

在系统通知资料取得成功或失败

前,千万不可将传给系统的资料暂存区删除释放,不然当系统取得资料要写入

时,资料区已不见了,会导至当机的。

除此之外,资料暂存区的大小一定要够

大,才足够让系统用来存放取得的资料。

(Winsock规格中的建议值是

MAXGETHOSTSTRUCT1024bytes大小的暂存区,笔者认为太大了,100byets

差不多就太够了?

 

呼叫非同步资料库函式时,得到的return值是一个代码,此代码代表的就

是此项呼叫在系统内的编号;由於是非同步,所以我们在得到答案前,仍可呼

叫WSACancelAsyncRequest()函式来取消原先的呼叫,这个取消的动作就要利

用到该代码了。

另外,当我们收到结果通知时,wParam的值也是这个代码;我

们此时可以利用WSAGETASYNCERROR(lParam)来得知资料取得是成功或失

败;如果失败的原因是原先传入的暂存区太小的话,我们亦可利用

WSAASYNCGETBUFLEN(lParam)来得知至少要多大的暂存区才够。

 

◎WSAAsyncGetHostByName():

利用某一host的名称来获取该host的资

料。

(非同步方式)

格式:

HANDLEPASCALFARWSAAsyncGetHostByName(HWNDhWnd,

unsignedintwMsg,constcharFAR*name,charFAR*buf,int

buflen);

参数:

hWnd动作完成後,接受讯息的视窗handle

wMsg传回视窗的讯息

namehost名称

buf存放hostent资料的暂存区

buflenbuf的大小

传回值:

成功-代表此非同步动作的handle代码

失败-0(呼叫WSAG

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1