分享
 
 
 

Winsock API 编程介绍

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Winsock API 编程介绍

作者:太子

email: tablejiang@21cn.com

page: http://wolfftp.51.net

相信很多人都对网络编程感兴趣,下面我们就来介绍,在网络编程

中应用最广泛的编程接口Winsock API.

使用Winsock API的编程,应该了解一些TCP/IP的基础知识.虽然你可

以直接使用Winsock API来写网络应用程序,但是,要写出优秀的网络

应用程序,还是必须对TCP/IP协议有一些了解的.

1. TCP/IP协议与Winsock网络编程接口的关系.

在开始之前,我们先说一下Winsock和TCP/IP到底是什么关系.

我碰到很多人问我:怎样使用Winsock协议编程? 其实,这话说的有点

错误,Winsock并不是一种网络协议,他只是一个网络编程接口,也就

是说,他不是协议,但是他可以访问很多种网络协议,你可以把他当作

一些协议的封装.现在的Winsock已经基本上实现了与协议无关.你可

以使用Winsock来调用多种协议的功能.

那么,Winsock和TCP/IP协议到底是什么关系呢?实际上,Winsock就是

tcp/ip协议的一种封装,你可以通过调用winsock的接口函数来调用

tcp/ip的各种功能.例如我想用Tcp/ip协议发送数据,你就可以使用

winsock的接口函数send()来调用tcp/ip的发送数据功能,至于具体

怎么发送数据,Winsock已经帮你封装好了这种功能.

2.TCP/IP协议介绍

现在来介绍一些tcp/ip的原理.tcp/ip协议包含的范围非常的广,他

是一种四层协议,包含了各种,硬件软件需求的定义,我们这里只介绍

软件方面的知识.tcp/ip协议确切的说法应该是tcp/udp/ip协议.

udp协议(User Datagram Protocol 用户数据报协议).是一种保护消

息边界的,不保障可靠数据的传输.

tcp协议(Transmission Control Protocol 传输控制协议).是一种

流传输的协议.他提供可靠的,有序的,双向的,面向连接的传输.

3.保护消息边界和流

那么什么是保护消息边界和流呢?

保护消息边界,就是指传输协议把数据当作一条独立的消息在网上

传输,接收端只能接收独立的消息.也就是说存在保护消息边界,接收

端一次只能接收发送端发出的一个数据包.

而面向流则是指无保护消息保护边界的,如果发送端连续发送数据,

接收端有可能在一次接收动作中,会接收两个或者更多的数据包.

我们举个例子来说,例如,我们连续发送三个数据包,大小分别是2k,

4k , 8k,这三个数据包,都已经到达了接收端的网络堆栈中,如果使

用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有

三次接收动作,才能够把所有的数据包接收完.而使用TCP协议,我们

只要把接收的缓冲区大小设置在14k以上,我们就能够一次把所有的

数据包接收下来.只需要有一次接收动作.

这就是因为UDP协议的保护消息边界使得每一个消息都是独立的.而

流传输,却把数据当作一串数据流,他不认为数据是一个一个的消息.

所以有很多人在使用tcp协议通讯的时候,并不清楚tcp是基于流的

传输,当连续发送数据的时候,他们时常会认识tcp会丢包.其实不然,

因为当他们使用的缓冲区足够大时,他们有可能会一次接收到两个甚

至更多的数据包,而很多人往往会忽视这一点,只解析检查了第一个

数据包,而已经接收的其他数据包却被忽略了.所以大家如果要作这

类的网络编程的时候,必须要注意这一点.

4。Winsock编程简单流程

下面我们介绍一下Win32平台的Winsock编程方法.

通讯则必须有服务器端,和客户端.

我们简单介绍tcp服务器端的大体流程.

对于任何基于Winsock的编程首先我们必须要初始化Winsock DLL库.

int WSAStarup( WORD wVersionRequested , LPWSADATA lpWsAData ).

wVersionRequested是我们要求使用的Winsock的版本.

调用这个接口函数可以帮我们初始化Winsock .然后我们必须创建一

个套接字(socket).

SOCKET socket( int af , int type , int protocol );

套接字可以说是Winsock通讯的核心.Winsock通讯的所有数据传输,

都是通过套接字来完成的,套接字包含了两个信息,一个是IP地址,一

个是Port端口号,使用这两个信息,我们就可以确定网络中的任何一

个通讯节点.

当我们调用了socket()接口函数创建了一个套接字后,我们必须把套

接字与你需要进行通讯的地址建立联系,我们可以通过绑定函数来实

现这种联系.

int bind(SOCKET s , const struct sockaddr FAR* name , int namelen ) ;

struct sockaddr_in

{

short sin_family ;

u_short sin_prot ;

struct in_addr sin_addr ;

char sin_sero[8] ;

}

就包含了我们需要建立连接的本地的地址,包括,地址族,ip和端口信

息.sin_family字段我们必须把他设为AF_INET,这是告诉Winsock使

用的是IP地址族.sin_prot 就是我们要用来通讯的端口号.sin_addr

就是我们要用来通讯的ip地址信息.

在这里,必须还得提一下有关'大头(big-endian)'小头(little-endian)'.

因为各种不同的计算机处理数据时的方法是不一样的,Intel 86处理

器上是用'小头'形势来表示多字节的编号,就是把低字节放在前面,

把高字节放在后面,而互联网标准却正好相反,所以,我们必须把主机

字节转换成网络字节的顺序.Winsock API提供了几个函数.

把主机字节转化成网络字节的函数;

u_long htonl( u_long hostlong );

u_short htons( u_short hostshort );

把网络字节转化成主机字节的函数;

u_long ntohl( u_long netlong ) ;

u_short ntohs( u_short netshort ) ;

这样,我们设置ip地址,和port端口时,就必须把主机字节转化成网络

字节后,才能用bind()函数来绑定套接字和地址.

当绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的

连接请求.

int listen( SOCKET s ,int backlog );

这个函数可以让我们把套接字转成监听模式.

如果客户端有了连接请求,我们还必须使用

int accept( SOCKET s , struct sockaddr FAR* addr , int FAR* addrlen );

来接受客户端的请求.

现在我们基本上已经完成了一个服务器的建立,

而客户端的建立的流程则是初始化WinSock ,然后创建socket套接字

,再使用

int connect( SOCKET s , const struct sockaddr FAR* name , int namelen ) ;

来连接服务端.

下面是一个最简单的创建服务器端和客户端的例子:

服务器端的创建 :

WSADATA wsd ;

SOCKET sListen ;

SOCKET sclient ;

UINT port = 800 ;

int iAddrSize ;

struct sockaddr_in local , client ;

WSAStartup( 0x11 , &wsd );

sListen = socket ( AF_INET , SOCK_STREAM , IPPOTO_IP ) ;

local.sin_family = AF_INET ;

local.sin_addr = htonl( INADDR_ANY ) ;

local.sin_port = htons( port ) ;

bind( sListen , (struct sockaddr*)&local , sizeof( local ) ) ;

listen( sListen , 5 ) ;

sClient = accept( sListen , (struct sockaddr*)&client , &iAddrSize ) ;

客户端的创建:

WSADATA wsd ;

SOCKET sClient ;

UINT port = 800 ;

char szIp[] = "127.0.0.1" ;

int iAddrSize ;

struct sockaddr_in server ;

WSAStartup( 0x11 , &wsd );

sClient = socket ( AF_INET , SOCK_STREAM , IPPOTO_IP ) ;

server.sin_family = AF_INET ;

server.sin_addr = inet_addr( szIp ) ;

server.sin_port = htons( port );

connect( sClient , (struct sockaddr*)&server , sizeof( server ) ) ;

当服务器端和客户端建立连接以后,无论是客户端,还是服务器端都

可以使用

int send( SOCKET s , const char FAR* buf , int len , int flags );

int recv( SOCKET s , char FAR* buf , int len , int flags );

函数来接收和发送数据,因为,TCP连接是双向的.

当要关闭通讯连结的时候,任何一方都可以调用

int shutdown( SOCKET s , int how ) ;

来关闭套接字的指定功能。再调用

int closesocket( SOCKET s) ;

来关闭套接字句柄。这样一个通讯过程就算完成了。

注意:上面的代码没有任何检查函数返回值,如果你作网络编程就一定要

检查任何一个Winsock API函数的调用结果,因为很多时候函数调用

并不一定成功.上面介绍的函数,返回值类型是int的话,如果函数调

用失败的话,返回的都是SOCKET_ERROR.

5。Winsock编程的五种模型

上面介绍的仅仅是最简单的winsock通讯的方法,而实际中很多网络

通讯的却很多难以解决的意外情况.

例如,Winsock提供了两种套接字模式:锁定和非锁定.当我们使用锁

定套接字的时候,我们使用的很多函数,例如accpet,send,recv等等,

如果没有数据需要处理,这些函数都不会返回,也就是说,你的应用程

序会阻塞在那些函数的调用处.而 如果使用非阻塞模式,调用这些函

数,不管你有没有数据到达,他都会返回,所以,有可能我们在非阻塞

模式里,调用这些函数大部分的情况下会返回失败,所以就需要我们

来处理很多的意外出错.

这显然不是我们想要看到的情况.我们可以采用Winsock的通讯模型

来避免这些情况的发生。

Winsock提供了五种套接字I/O模型来解决这些问题.他们分别是

select(选择),WSAAsyncSelect(异步选择),

WSAEventSelect (事件选择), overlapped(重叠) , completion

port(完成端口) .

我们在这里详细介绍一下select,WSAASyncSelect两种模型.

select模型是最常见的I/O模型.

使用

int select( int nfds , fd_set FAR* readfds , fd_set FAR* writefds , fd_set FAR* exceptfds ,

const struct timeval FAR * timeout ) ;

函数来检查你要调用的socket套接字是否已经有了需要处理的数据.

select包含三个socket队列,分别代表:

readfds ,检查可读性,writefds,检查可写性,exceptfds,例外数据.

timeout是select函数的返回时间.

例如,我们想要检查一个套接字是否有数据需要接收,我们可以把套

接字句柄加入可读性检查队列中,然后调用select,如果,该套接字没

有数据需要接收,select函数会把该套接字从可读性检查队列中删除

掉,所以我们只要检查该套接字句柄是否还存在于可读性队列中,就

可以知道到底有没有数据需要接收了.

Winsock提供了一些宏用来操作套接字队列fd_set.

FD_CLR( s,*set) 从队列set删除句柄s.

FD_ISSET( s, *set) 检查句柄s是否存在与队列set中.

FD_SET( s,*set )把句柄s添加到队列set中.

FD_ZERO( *set ) 把set队列初始化成空队列.

WSAAsyncSelect(异步选择)模型:

WSAASyncSelect模型就是把一个窗口和套接字句柄建立起连接,套接

字的网络事件发生时时候,就会把某个消息发送到窗口,然后可以在

窗口的消息响应函数中处理数据的接收和发送.

int WSAAsyncSelect( SOCKET s, HWND hWnd , unsigned int wMsg , long lEvent ) ;

这个函数可以把套接字句柄和窗口建立起连接,

wMsg 是我们必须自定义的一个消息.

lEvent就是制定的网络事件.包括FD_READ , FD_WRITE , FD_ACCEPT

, FD_CONNECT , FD_CLOSE .

几个事件.

例如,我需要接收FD_READ , FD_WRITE , FD_CLOSE 的网络事件.可

以调用

WSAAsyncSelect( s , hWnd , WM_SOCKET , FD_READ | FD_WRITE | FD_CLOSE ) ;

这样,当有FD_READ , FD_WRITE 或者 FD_CLOSE网络事件时,窗口

hWnd将会收到WM_SOCKET消息,消息参数的lParam标志了是什么事件

发生.

其实大家应该见过这个模型,因为MFC的CSocket类,就是使用这个模

型.

上面为大家介绍了Winsock编程的一些方法.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有