Windows网络编程(三)

相关函数

1、WSAStartup函数   

用于初始化Winsock   

[声明  

int WSAStartup(WORD wVersionRequested, LPWSADATA  lpWSAData);   

[参数  

wVersionRequested - 要求使用Winsock的最低版本号

用来指定准备加载的Winsock库的版本。高位字节指定做需要的Winsock库的副版本,而低位字节则是主版本。通常版本号为2.1,其中2就是主版本号,1就是副版本号。可以利用MAKEWORD(x,y)宏(其中x是高位字节,yd低位字节)方便的获得wVersionRequested的正确值。

lpWSAData - Winsock的详细资料

这是一个返回值,指向WSADATA结构的指针,WSAStartup函数用其加载的库版本有关的信息填在这个结构中。

WSADATA结构的定义如下:

typedef struct WSAData{

    WORD wVersion;

    WORD wHighVersion;

    char szDescription[WSADESCRIPTION_LEN+1];

    char szSystemStatus[WSASYS_STATUS_LEN+1];

    unsigned short iMaxSockets;

    unsigned short iMaxUdpDg;

    Char FAR * lpVendorInfo;

} WSADATA, *LPWSADATA;

WSAStartup函数把WSAData结构中的第一个字段wVersion设置为打算使用的Winsock版本。wHighVersion字段容纳的是现有的Winsock库的最高版本。注意:这两个字段中,高位字节代表的是Winsock副版本,而低位字节代表的则是Winsock主版本。szDescriptionszSystemStatus这两个字段由特定的Winsock实施方案设定,事实上并没有用。不要使用下面这两个字段:szSystemStatusiMaxUdpDg,它们是假定同时最多可打开多少套接字和数据报的最大长度。然而,要知道数据报的最大长度应该通过WSAEnumProtocols函数来查询协议信息。同时最多可打开套接字的数目不是固定的,很大程度上和可用物理内存的多少有关。最后,lpVendorInfo字段是为Winsock实施方案有关的指定厂商信息预留的,任何一个Win32平台上都没有使用这个字段。

[返回值]

当函数成功调用时返回0,失败时返回非0的值。

在利用套接字编程时,第一步需要加载套接字库,这通过WSAStartup函数来实现。该函数有两个功能:一是加载套接字库,一是进行套接字库的版本协商,也就是确定将使用的socket版本。

对于每一个WSAStartup函数的成功调用(即成功加载WinSock动态库后),在最后都对应一个WSACleanUp调用,以便释放为该应用程序分配的资源,终止对WinSock动态库的使用。

2、socket函数

用于生成socket(soket Descriptor)

加载了套接字库之后,就可以调用socket函数创建套接字了。 

[声明  

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

[参数  

af - 地址家族(通常使用AF_INET),用来指定地址族,对于TCP/IP协议的套接字,它只能是AF_INET(也可写成PF_INET)   

type - socket的种类,指定Socket的类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字

protocol - 所使用的协议,这个参数是与特定的地址家族相关的协议,如果指定为0,那么系统就会根据地址格式和套接字类别,自动选择一个合适的协议。这是推荐使用的一种选择协议的方法。

[返回值  

当函数成功调用时返回一个新的SOCKET(Socket Descriptor),失败时返回INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。

3、bind函数

在创建了套接字之后,应该将该套接字绑定到本地的某个地址和端口上,这需要通过bind函数来实现。

[声明  

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

[参数  

s - 指向用Socket函数生成的Socket Descriptor,即指定要绑定的套接字

addr - 指定该套接字的本地地址信息,这是一个指向sockaddr结构的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构可能随所使用的网络协议不同而不同,所以,要使用第三个参数(namelen)指定该地址结构的长度

namelen - 该地址的长度

sockaddr结构的定义如下所示:

struct sockaddr{

u_short sa_family;

char   sa_data[14];

};

第一个字段sa_family指定地址家族,对于对于TCP/IP协议的套接字,必须设置为AF_INET;第二个字段sa_data仅仅是表示要一块内存分配区,起到占位的作用该区域中指定与协议相关的具体地址信息。由于实际要求的知识内存区,所以对于不同的协议家族,用不同的结构来替换socketaddr。出了sa_family外,socketaddr是按网络字节顺序表示的。在基于TCP/IPsocket编程中,可以用socket_in结构替换socketaddr,以方便填写地址信息。

socket_in结构体的定义如下:

struct sockaddr_in{

short sin_family;

unsigned short sin_port;

struct in_addr sin_addr;

char sin_zero[8];

};

sa_family指定地址家族,sin_port指定将要分配给套接字的端口,sin_addr给出的是套接字的主机IPsin_zero只是一个填充数,以使socket_in结构和socketaddr结构的长度一样。

其中,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_b;

u_long S_addr;

}

} S_un;

可以看到,in_addr结构实际上是一个联合,通常利用这个结构将一个点分十进制格式的IP地址转换为u_long类型,并将结果付赋给成员S_addr

[返回值  

当函数成功调用时返回0,调用失败时返回SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

4、inet_addr函数和inet_ntoa函数

可以将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。多数情况下,每个机器只有一个IP地址,但有的机器可能有多个网卡,每个网卡都可以有自己的IP地址,用INADDR_ANY可以简化应用程序的编写。将地址指定为INADDR_ANY,将允许一个独立应用接受发自多个接口的回应。如果只想让套接字使用多个IP地址中的一个,就必须指定实际地址,要做到这一点,可以用inet_addr函数来实现,该函数原型声明如下所示:   

[声明  

unsigned long inet_addr ( const char FAR *cp );   

[参数  

cp - 指向用"xxx.xxx.xxx.xxx"10进制来表示的IP地址字符串的指针   

[返回值  

当函数成功调用时返回用32位整数表示的IP地址(按网络字节排列顺序),失败时返回INADDR_NONE

inet_addr函数需要一个字符串作为其参数,该字符串指定了以点分十进制格式表示的IP地址。而且inet_addr函数会返回一个适合分配各S_addru_long类型的数值。

inet_ntoa函数会完全相反的转换,它接受一个in_addr结构体类型的参数并返回一个以点分十进制格式表示的IP地址字符串,该函数的原型声明如下所示:

char FAR * inet_ntoa (struct in_addr in);

5、listen函数

该函数的作用是将制定的套接字设置为监听模式。其原型声明如下所示:

int listen (SOCKET s, int backlog);

第一个参数是套接字描述符,第二个参数是等待连接队列的最大长度。如果设置为SOMAXCONN,那么下层的服务提供者将负责将这个套接字设置为最大的合理值。要注意的是,设置这个值是为了设置等待连接队列的最大长度,而不是在一个端口上同时可以进行连接的数目。

6、accept函数

该函数接受客户端发送的连接请求。

SOCKET accept(

SOCKET s,

struct sockaddr FAR * addr,

int FAR * addrlen

);

其中第一个参数是套接字描述符,该套接字已经通过listen函数将其设置为监听状态;第二个参数是指向一个缓冲区的指针,该缓冲区用来接收连接实体的地址,也就是当客户端向服务器发起连接,服务器端接受这个连接时,保存发起连接的这个客户端的IP 地址信息和端口信息;第三个参数也是一个返回值,指向一个整型的指针,返回包含地址信息的长度。

7、send函数

该函数通过一个已建立连接的套接字发送数据。其原型声明如下:

int send (

SOCKET s,

const char FAR * buf,

int len,

int flags

);

第一个参数是一个已建立连接的套接字;第二个参数指向一个缓冲区,该缓冲区包含将要传递的数据;第三个参数是缓冲区的长度;第四个参数设定的值将影响函数的行为,一般将其设置为0即可。

8、recv函数

该函数从一个已连接的套接字接收数据。

[声明]

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

第一个参数是一个已建立连接的套接字;第二个参数指向一个缓冲区,该缓冲区用来保存接收的数据;第三个参数是缓冲区的长度;第四个参数设定的值将影响函数的行为,一般将其设置为0即可。

[返回值  

成功时返回收到的字节数

如果连接被中断则返回0

失败时返回 SOCKET_ERROR

9、connect函数   

用于与服务器建立连接,发出连接请求,必须在参数中指定服务器的IP地址和端口号

[声明  

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

[参数  

s - 是即将在其上建立连接的那个套接字;

name - 设定连接的服务器端地址信息

namelen - 指定服务器端地址的长度

[返回值]

当函数成功调用时返回0

调用失败时返回SOCKET_ERROR

10、recvfrom函数

该函数将接收一个数据报信息并保存源地址,函数声明如下:

int recvfrom(SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen);

该函数共有6个参数,第一个参数s是准备接收数据的套接字;第二个参数buf是一个指向缓冲区的指针,该缓冲区用来接收数据;第三个参数len是缓冲区的长度;第四个参数flagssend函数的第四个参数类似,通过设置这个值可以影响这些函数调用的行为;第五个参数from是一个指向地址结构体的指针,主要是用来接收发送数据方的地址地址信息;最后一个参数fromlen是一个整型的指针,并且它是一个in/out类型的参数,表明在调用前需要给它指定一个初始值,当函数调用之后,会通过这个参数值返回一个值,该返回值是地址结构的大小。

11、sendto函数 

利用Socket进行发送数据。

[声明  

int sendto (SOCKET s , const char FAR *buf , int len , int flags , const struct sockaddr FAR *to , int token);   

[参数]

s - 是一个(可能已建立连接)的套接字描述符;

buf - 是一个指向缓冲区的指针,该缓冲区包含将要发送的数据;

len - 指定缓冲区中数据的长度;

flags - 调用方式(MSG_DONTROUTE , MSG_OOB)

to - 指向发送方SOCKET地址的指针

token - 发送方SOCKET地址的大小,是参数to中指定的地址的长度   

[返回值  

成功时返回已经发送的字节数

失败时返回SOCKET_ERROR

12、htonshtonl函数

htons函数将把一个u_short类型的值从主机字节顺序转换为TCP/IP网络字节顺序,其原型声明如下所示:

u_short htons(u_short hostshort);

参数hostshort是一个主机字节顺序表示的16位数值。

htons函数相类似的还有一个函数:htons,该函数将把一个u_long类型的值从主机字节顺序转换为TCP/IP网络字节顺序,其原型声明如下所示:

u_long htonl(u_long hostlong);

参数hostlong是一个以主机字节顺序表示的32位数值。

13、gethostbyname函数   

可以从主机名获取主机资料  

[声明  

struct hostent FAR * gethostbyname ( const char FAR *name );   

[参数  

name - 指向主机名字符串的指针   

[返回值  

当函数成功调用时返回主机信息   

失败时返回NULL(空值

14、select函数   

可以用于调查一个或多个SOCKET的状态 

[声明]

int select ( int nfds , fd_set FAR *readfds , fd_set FAR *writefds , fd_set FAR *exceptfds , const struct timeval FAR *timeout );

[参数]

nfds - WINDOWS SOCKET API 中该参数可以忽略,通常赋予NILL

readfds - 由于接受的SOCKET设备的指针   

writefds - 用于发送数据的SOCKET设备的指针   

exceptfds - 检查错误的状态   

timeout - 超时设定   

[返回值  

返回大于0的值时,表示与条件相符的SOCKET数   

返回0表示超时   

失败时返回SOCKET_ERROR 

15、listen()函数(补充)

(1)简述

创建一个套接口并监听申请的连接.

#include <winsock.h>

int PASCAL FAR listen( SOCKET s, int backlog);

S:用于标识一个已捆绑未连接套接口的描述字。

backlog:等待连接队列的最大长度。

(2)注释

为了接受连接,先用socket()创建一个套接口,然后用listen()为申请进入的连接建立一个后备日志,然后便可用accept()接受连接了。listen()仅适用于支持连接的套接口,如SOCK_STREAM类型的。套接口s处于一种“变动”模式,申请进入的连接请求被确认,并排队等待被接受。这个函数特别适用于同时有多个连接请求的服务器;如果当一个连接请求到来时,队列已满,那么客户将收到一个WSAECONNREFUSED错误。

当没有可用的描述字时,listen()函数仍试图正常地工作。它仍接受请求直至队列变空。当有可用描述字时,后续的一次listen()accept()调用会将队列按照当前或最近的“后备日志”重新填充,如有可能的话,将恢复监听申请进入的连接请求。

(3)兼容性   

后备日志当前被(默认地)限制为5。如同4.3 BSD Unix中的一样,小于1或大于5的数都会被舍入最近的有效值。

(4)返回值

如无错误发生,listen()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

(5)错误代码

WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()

WSAENETDOWNWINDOWS套接口实现检测到网络子系统失效。

WSAEADDRINUSE:试图用listen()去监听一个正在使用中的地址。

WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

WSAEINVAL:该套接口未用bind()进行捆绑,或已被连接。

WSAEISCONN:套接口已被连接。

WSAEMFILE:无可用文件描述字。

WSAENOBUFS:无可用缓冲区空间。

WSAENOTSOCK:描述字不是一个套接口。

WSAEOPNOTSUPP:该套接口不正常listen()调用。

你可能感兴趣的:(Windows网络编程(三))