专业汉语词典知识平台,分享汉字词语知识、历史文学知识解答!

励北网
励北网

网络安全编程:Winsock编程基础

来源:小易整编  作者:小易  发布时间:2023-03-06 03:50
摘要:网络安全编程:Winsock编程基础网络攻防是一个比较大的话题,比如端口扫描、SQLInjection扫描、数据包嗅探、网络密码猜解、后门、木马等知识的基础技术。这些技术在入侵剖析中是比较常见的技术。在学习扫描器、嗅探器、木马等知识之前,首...

Winsock库的初始化函数的定义:

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

该函数的第1个参数wVersionRequested是需要初始化Winsock库的版本号。第2个参数lpWSAData是一个指向WSADATA的指针。该函数的返回值为0,说明函数调用成功。如果函数调用失败,则返回其他值。在程序的开始处调用该初始化函数,在程序中就可以使用Winsock相关的所有API函数。

Winsock库的释放函数的定义:

int WSACleanup (void);

该函数没有参数,在程序的结束处直接调用该函数,即可释放Winsock库。

初始化与释放Winsock库的代码示例如下:

WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD( 2, 2 );err = WSAStartup( wVersionRequested, &wsaData );if ( err != 0 ){  return -1;}if ( LOBYTE( wsaData.wVersion ) != 2 ||HIBYTE( wsaData.wVersion ) != 2 ){  WSACleanup( );  return -1;}// ……WSACleanup();

2. 套接字的创建与关闭

套接字用于根据指定的协议类型来分配一个套接字描述符。该描述符主要用在客户端和服务器端进行通信连接,当套接字使用完毕时应该关闭套接字以释放资源。创建套接字与关闭套接字的函数为socket()和closesocket()。

创建套接字的函数定义如下:

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

socket()函数共有3个参数,第1个参数af用来指定地址族,在Windows下可以使用的参数值有多个,但是真正可以使用的只有两个,分别是AF_INET和PF_INET。这两个宏在Winsock2.h下的定义是相同的,分别如下:

#define AF_INET 2 /* internetwork: UDP, TCP, etc. *//** Protocol families, same as address families for now.*/#define PF_INET AF_INET

以上两个定义都摘自Winsock2.h头文件。从定义中可以看出,PF_INET和AF_INET是相同的。看PF_INET宏定义上面的注释,AF表示地址族(Address Family),而PF表示协议族(Protocol Family)。对于Windows来说,两者相同;对于Unix/Linux来说,两者是不相同的。一般情况下,在调用socket()函数时应该使用PF_INET,而在设置地址时使用AF_INET。FP_INET上面的那句注释,同样也是出自Winsock2.h头文件中。“Protocol families,same as address families for now.”,也就是说,目前PF和AF是相同的。注释中说目前是相同的,可能这样定义是为以后预留的,为了保持良好的兼容性。调用socket()函数时,应该使用PF_INET宏,而尽量避免或不去使用AF_INET宏。

socket()函数的第 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;如果第 3 个参数使用了 SOCK_DGRAM,那么第 3 个参数应该使用 IPPROTO_UDP。为了书写简单,如果第 2 个参数是 SOCK_STREAM 或 SOCK_DGRAM,那么第 3 个参数可以默认为 0。如果第 2 个参数指定的是 SOCK_RAW,那么第 3 个参数就必须指定,而不能使用 0 值。

socket()函数调用成功返回值为一个新的套接字描述符,如果调用失败,则返回 INVALID_SOCKET。调用失败后,想要知道调用失败的原因,那么紧接着调用 WSAGetLastError()函数得到错误码。

所有的Winsock函数出错后,都可以调用WSAGetLastError()函数得到错误码,但是WSAStartup()不能通过WSAGetLastError()得到错误码,因为WSAStartup()未调用成功,不能调用WSAGetLastError()函数。

关闭套接字的函数定义如下:

int closesocket(SOCKET s);

closesocket()函数的参数是 socket()函数创建的套接字描述符。

对于WSAStartup()/WSACleanup()和socket()/closesocket()这样的函数,最好保持成对出现。也就是说,在写完一个函数时,立刻写出另外一个函数的调用,以免忘记资源的释放。

3. 面向连接协议的函数

bind()、listen()、accept()、connect()、send()和recv(),这些函数是常用的面向连接的函数,它们都是Winsock面向连接的最基本的函数。下面介绍几个函数的使用方法。

通过socket()函数可以创建一个新的套接字描述符,但是它只是一个描述符,它为网络的一些资源做准备。要真正在网络上进行通信,需要本地的地址与本地的端口号信息。当然,本地地址与端口号信息要去套接字描述符进行关联进行绑定。在Winsock函数中,使用bind()函数完成套接字与地址端口信息的绑定。bind()函数的定义如下:

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

该函数有3个参数,第1个参数s是新创建的套接字描述符,也就是用socket()函数创建的描述符,第2个参数name是一个sockaddr的结构体,提供套接字一个地址和端口信息,第3个参数namelen是sockaddr结构体的大小。

其中第2个参数中的sockaddr结构体定义如下:

struct sockaddr { u_short sa_family; char sa_data[14];};

该结构体共有16字节,在该结构体之前所使用的结构体为sockaddr_in,该结构体的定义如下:

struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; };

sockaddr结构体是为了保持各个特定协议之间的结构体兼容性而设计的。为bind()函数指定地址和端口时,向sockaddr_in结构体填充相应的内容,而调用函数时应该使用sockaddr结构体。

在sockaddr_in结构体中,还有一个结构体in_addr,该结构体在winsock2.h中的定义如下:

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_w; u_long S_addr; } S_un;};

该结构体中是一个共用体S_un,包含两个结构体变量和1个u_long类型变量。一般使用的IP地址是使用点分十进制表示的,而in_addr结构体中却没有提供用来保存点分十进制表示IP地址的数据类型,这时需要使用转换函数,把点分十进制表示的IP地址转换成in_addr结构体可以接受的类型。这里使用的转换函数是inet_addr(),该函数的定义如下:

unsigned long inet_addr(const char FAR *cp);

该函数是将点分十进制表示IP地址转换成unsigned long类型的数值。该函数的参数cp是指向点分十进制IP地址的字符指针。同时该函数有一个逆函数,是将unsigned long型的数值型IP地址转换为点分十进制的IP地址字符串,该函数的定义如下:

char FAR * inet_ntoa(struct in_addr in);

sockaddr_in 结构体中的 sin_port 表示端口,这个端口需要使用大尾方式字节序存储(大尾方式和小尾方式是两种不同的存储方式)。在 Intel X86 架构下,数值存储方式默认都是小尾方式字节序,而 TCP/IP 的数值存储方式都是大尾方式的字节序。为了实现方便的转换,winsock2.h中提供了方便的函数,即 htons()和 htonl()两个函数,并且提供了它们的逆函数 ntohs()和 ntohl()。

htons()和 htonl()函数的定义分别如下:

u_short htons(u_short hostshort);u_long htonl(u_long hostlong);

ntohs()和 ntohl()函数的定义分别如下:

u_short ntohs(u_short netshort);u_long ntohl(u_long netlong);

这4个函数中,前两个函数是将主机字节序转换为网络字节序(host to network),后两个函数是将网络字节序转换为主机字节序(network to host)。在有些架构系统下,主机字节序和网络字节序是相同的,那样转换函数不进行任何转换,但是为了代码的移植性,还是会进行转换函数的调用。

具体bind()函数的使用方法如下:

// 创建套接字SOCKET sLisent = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);// 对 sockaddr_in 结构体填充地址、端口等信息struct sockaddr_in ServerAddr;ServerAddr.sin_family = AF_INET;ServerAddr.sin_addr.S_un.S_addr = inet_addr("10.10.30.12");ServerAddr.sin_port = htons(1234);// 绑定套接字与地址信息bind(sLisent, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr));

对于服务器端的地址可以指定为INADDR_ANY宏,表示“任意地址”或“所有地址”。当客户端发起连接时,服务器操作系统接收到客户端的连接,根据网络的配置情况会自动选择一个IP地址和客户端进行通信。

当套接字与地址端口信息绑定以后,就需要让端口进行监听,当端口处于监听状态以后就可以接受其他主机的连接了。监听端口和接受连接请求的函数分别为listen()和accept()。

监听端口的函数定义如下:

int listen(SOCKET s, int backlog);

该函数有两个参数,第1个参数s是指定要监听的套接字描述符,第2个参数backlog是允许进入请求连接队列的个数,backlog的最大值由系统指定,在winsock2.h中,其最大值由SOMAXCONN表示,该值的定义如下:


本文地址:百科问答频道 https://www.neebe.cn/wenda/916638_2.html,易企推百科一个免费的知识分享平台,本站部分文章来网络分享,本着互联网分享的精神,如有涉及到您的权益,请联系我们删除,谢谢!


百科问答
小编:小易整编
相关文章相关阅读
  • 网络安全编程:Winsock编程基础

    网络安全编程:Winsock编程基础

    网络安全编程:Winsock编程基础网络攻防是一个比较大的话题,比如端口扫描、SQLInjection扫描、数据包嗅探、网络密码猜解、后门、木马等知识的基础技术。这些技术在入侵剖析中是比较常见的技术。在学习扫描器、嗅探器、木马等知识之前,首...

  • 周排行
  • 月排行
  • 年排行

精彩推荐