相关函数
1、WSAStartup函数
用于初始化Winsock
[声明]
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
[参数]
wVersionRequested - 要求使用Winsock的最低版本号
用来指定准备加载的Winsock库的版本。高位字节指定做需要的Winsock库的副版本,而低位字节则是主版本。通常版本号为2.1,其中2就是主版本号,1就是副版本号。可以利用MAKEWORD(x,y)宏(其中x是高位字节,y是d低位字节)方便的获得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主版本。szDescription和szSystemStatus这两个字段由特定的Winsock实施方案设定,事实上并没有用。不要使用下面这两个字段:szSystemStatus和iMaxUdpDg,它们是假定同时最多可打开多少套接字和数据报的最大长度。然而,要知道数据报的最大长度应该通过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/IP的socket编程中,可以用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给出的是套接字的主机IP,sin_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_addr的u_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是缓冲区的长度;第四个参数flags与send函数的第四个参数类似,通过设置这个值可以影响这些函数调用的行为;第五个参数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、htons和htonl函数
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()。
WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
WSAEADDRINUSE:试图用listen()去监听一个正在使用中的地址。
WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
WSAEINVAL:该套接口未用bind()进行捆绑,或已被连接。
WSAEISCONN:套接口已被连接。
WSAEMFILE:无可用文件描述字。
WSAENOBUFS:无可用缓冲区空间。
WSAENOTSOCK:描述字不是一个套接口。
WSAEOPNOTSUPP:该套接口不正常listen()调用。