1.Winsock的初始化与释放
在使用winsock相关函数时需要对Winsock库进行初始化,而在使用完后需要对Winsock库进行释放。
Winsock库的初始化函数的定义如下:
1 | int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData); |
该函数的第1个参数wVersionRequested是需要初始化的Winsock库的版本号,Winsock库有多个版本号,目前常用的版本是2.2.第二个参数lpWSAData
是一个指向WSADATA的指针。该函数的返回值为0,说明该函数调用成功。如果调用失败,则返回其他值。在程序的开始处调用该初始化函数,在程序中就可以使用Winsock相关的所有API函数。
Winsock库的释放函数的定义
int WSACleanup(void);
该函数没有参数,在程序的结束处直接调用该函数,即可释放Winsock库。
2.套接字的创建与关闭
套接字用于根据指定的协议类型
来分配一个套接字描述符
。该描述符主要用在客户端和服务器端进行通信,当套接字使用完毕时应该关闭套接字以释放资源。创建套接字与关闭套接字的函数为socket()和closesocket()。
创建套接字的函数定义如下:
1 | SOCKET socket(int af,int type, int protocol); |
socket()函数共有3个参数,第1个参数af用来指定地址族,在Windows下可以使用的参数有很多个,但是真正可以使用的只有两个,分别说AF_INET和PF_INET。这两个宏在Winsock2.h下的定义相同,分别如下:1
2
3
4
5
6
/*
* Protocol families,same as address families for now
*/
以上两个定义都摘自Winsock2.h头文件。从定义来看,PF_INET和AF_INET是相同的。看PF_INET宏定义上面的注释,AF表示地址族(Address Family),而PF表示协议族(Protocol Family)。对于Windows来说,两者相同
;对于Unix/Linux来说,两者是不同的
。一般情况下,调用socket()函数时应使用PF_INET
,而在设置地址时使用AF_INET
。
sock()函数的第2个参数type是指定新套接字描述符的类型。这里可以使用的值通常有3个,分别是SOCK_STREAM、SOCK_DGRAM和SOCK_RAW,分别表示流套接字、数据包套接字和原始协议接口。
socket()函数的第3个参数Protocol用来指定程序所使用的通信协议,这里可以选择使用IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP等协议,这个参数的值是根据第2个参数
的值进行选择。第2个参数如果使用SOCK_STREAM
,那么第3个参数应该使用IPPROTO_TCP
;如果第2个参数使用SOCK_DGRAM,那么第3个参数应该使用IPPROTO_UDP。也就是说,如果第2个参数是SOCK_STREAM或SOCK_DGRAM,那么第3个参数可以默认为0.如果第2个参数指定的是SOCK_RAW,那么第3个参数必须指定,而不能使用0值。
socket()函数调用成功返回值为一个新的套接字描述符,如果调用失败,则返回INVALID_SOCKET。调用失败后,想要知道原因,那么紧接着调用WSAGetLastError()函数得到错误码。
PS:所有的Winsock函数出错后都可以调用WSAGetLastError()得到错误码。
关闭套接字的函数定义如下:
1 | int closesocket(SOCKET s); |
closesocket()函数是socket()函数创建的套接字描述符。
PS:对于WSAStartup()/WSACleanup()和socket()/closesocket()这样的函数,最好保持成对出现。也就是说,在写完一个函数时,立刻写出另外一个函数的调用,以免忘记资源的释放。
3.面向连接协议的函数
前面的部分提到了面向连接协议与非面向连接协议所用到的函数是不相同的。这里来介绍面向连接的函数:bind()、listen()、accept()、send()和recv()。这些函数是常用的面向连接的函数
,只是一个基础。Winsock库的函数非常多,这里只是寥寥几个而已,下面介绍函数的使用方法。
通过socket()函数可以直接创建一个新的套接字描述符,但是它只是一个描述符,为网络的一些资源做准备。要想真正在网络上进行通信,需要本地的地址
与本地的端口号信息
。当然,本地的地址与端口号信息需要和套接字描述符进行关联,进行绑定。在Winsock函数中,使用bind()函数完成套接字与地址端口信息的绑定。bind()函数的定义如下:1
int bind(SOCKET s, const struct sockaddr FAR *name, int namelen);
该函数有3个参数,第1个参数s是新创建的套接字描述符,也就是用socket()函数创建的描述符,第2个参数name是一个sockaddr的结构体,提供套接字一个地址和端口信息,第3个参数namelen是sockaddr结构体的大小。
其中第二个参数sockaddr结构体的定义如下:1
2
3
4struct sockaddr{
u_short sa_family; /* address family */
char sa_data[14]; /* up to 14 bytes of direct address */
};
该结构体共有16个字节,在该结构体之前所使用的协议为sockadd_in,该结构体的定义如下:1
2
3
4
5
6struct sockadd_in{
short sin_family;
u_short sin_port;
struct in_add sin_addr;
char sin_size[8];
};
!未完待续!