[笔记] Microsoft Windows网络编程《一》WinSock简介(一)

简介: [笔记] Microsoft Windows网络编程《一》WinSock简介

前言

本章专门讲解编写成功的 Winsock 用程序的基木方法。

Winsock 是一种标准 API(Application Programming Interface,应用程序编程接口),要用于网络中的数据通信,它允许两个或者多个应开程序(或进程)在同一台机器上或通过网络相互通信。

有点我们必须明白:

  • Winsock 是一种网络编程接口,而不是协议

使用 Winsock 编程接口,应用程序可通过通网络协议如 TCP/IP(TransmissionControl Protocol/Internet Protocol,传输控制协议/网际协议) 或 IPX(Intermet Packet Exchange, Internet 数据包交换)协议建立通信。

Winsock 接口从在UNX 平台上实现的 BSD Socket(套接字中继承了大量的特性。在 Windows 坏境中,这种接口演变成一种真正独立于协议的接口,新发布的 Winsock2版本更是如此。

本章将讨论 从网络上的一台机器到另一台机器建立通信的基本知识,以及如何收发数据

为了便于大家理解接受连接、建立连接和收发数据所需的 Winsock 调用,本章给出了多个示例。

由于本章的目的是学习这些基本的 Winsock 调用,因而所举的示例均采用了直接阻塞的 Winsock 调用。

第5章将讲述 Winsock 支持的非阻塞调用及其他各种 I/O 方法,其中包含示例代码。

除此以外,本章还将介绍各种 API函数的:

  • Winsock 1版本
  • Winsock 2版本。

通过前 WSA 可以区分该函数的两种版本,若 Winsock 2 在其规范中更新或增添了一个新的 API 函数,该函数名将带有WSA前缀。比如,建立套接字的 Winsock1函数只是被简单称为socket。而Winsock2引入该函数的新版本时,则将它命名为 WSASocket,该函数可以使用 Winsock2中出现的一些新特性。

但请注意这个命名规则有几个例外,如WSAStartupWSACleanupWSARecvExWSAGetLastError 都属于Winsock 1.1规范的函数

在使用 Winsock 开发应用程序前,必须了解创建应用程序时学要哪些文件和库。


1.1 WinSock头文件及库文件

如前所述,Winsock 有两个主要版本,

  • Winsock1
  • Winsock2

两者都能在除 Windows CE之外(Windows CE 只支持 Winsock 1)的所有 Windows 平台上运行。

开发新的应用程序时,把WINSOCK2.H 文件包含在应用程序中,该程序将使用 Winsock2 规范。为了和其他旧的 Winsock 应用程序兼容以及保证 Windows CE 平台上的程序开发,可以使用 WINSOCKH。

另外,还有个头文件MSWSOCKH,该头文件用于微软专用编程扩展,这些扩展通常用于高效 Winsock 应用程序的开发第6章中将对此加以描述在编译采用广WINSOCK2.H 的应用程序时,须链接 WS2_32.LIB 库。

使用 WINSOCKH比如在Windows CE 中)时须使用 WSOCK32LIB。如果从 MSWSOCKH 中使用扩展 API,还必须链接MSWSOCKDLL。一旦包含了必需的头文件和链接环境,就以开始编写应用程序代码了,这时需要初始化WinSock。


1.2 WinSock的初始化

每个Wimsock 应用都必须加载合适的 Winsock DLL版本。如调用·个Winsock 所数之前没有加载 Winsock 库,这个函数就会返回一个 SOCKET ERROR,错误信息是 WSANOTINITIALISED加载 Winsock 库是通过调用 WSAStartup 函数实现的。这个函数的定义如下:

int WSAStartup(

WORD wVersionRequested,

LPWSADATA lpwsAData

);

wVersionRequested参数用于指定准备加载的 Winsock 库的版本。高位字节指定所需 Winsock 库的次版本,而低位字节则是主版本。可以使用宏 MAKEWORD(x,yX其中,x是高位字节,y 是低位字节)来方便地获得 wVersionRequested 的正确值。

IpWSAData参数是指向 LPWSADATA 结构的指针WSAStartup 用与其加载的库版本有关的信息填充这个结构:

typedef struct WSAData
{
  WORD wVersion
  WORD wHighVersion
  char szDescription[WSADESCRIPTION LEN +1]:
  char szSystemstatus[WSASYS STATUS LEN +1];
  unsigned short iMaxSockets;
  uns igned short iMaxUdpDq;
  char FAR* lpVendorInfo;
  WSADATA,ALPWSADATA;
}

WSAStartup 将第一个字段 wVersion 设置为将要使用的 Winsock 版本。

wHighVersion 参数包含了现有 Winsock 库的最高版本。

记住,这两个字段中,高位字节代表的是 Winsock 次版本,而低位字节代衣的则是 Winsock 主版本,szDescriptionszSystemStatus 这两个宁段由特定的Winsock 来实现和设置,它们并没有实际作用。

不要使用下面这两个字段:

  • iMaxSockets
  • iMaxUdpDg

它们分别表示可以时打开的取大套接字数量,以及数报的最大长度。

通过 WSAEnumProtocols(请参阅第 2章查询协议信息,才能知道数据报的最人长度:可以同时打开的最大套接字数量个是固定的,它的值在很人程度上取决于可用的物理资源。最后的 pVendorlnfo是 个保留子段,用下存储实现 Winsock的特定供应商的信息,所有的 Windows 平台都没有使用这个学段。

1.1 列出了微软各种 Windows 平台文持的 Winsock 版本。必须注意这两个版本之间的差别Winsock l.x 不支持描述的很多高级Winsock 特性

注意,即使某个平台支持 Winsock 2,也可以不使用这个 Winsock 的最新版本。也就是说,如果打算编写大多数平台均能支持的应用程序,应根据 Winsock 1.1 规范来编写。为所有的 Winsock 1.1调用都通过 Winsock 2 DLL 映射,所以所编程序可以在Windows NT 4.0 平台上确无误地运行同时,如果市面上出现了您所用平台可以使用的 Winsock 库更新版本,那么您很可能要进行版本升级。内为这些新版本中仙含了对错误的修止,所以从理论上来说,旧代码完全可以正常运行。

在有些情况下,Wimsock 堆栈的行为和规范中的定义有所不同,这样,许多程序员是根据特定!标平台的行为来编程,前不是根据规范来编写程序。

但是,在很多情况下,编写新应用程序时,程序员都想加载已发布的 Winsock 库的最新版本。当然,如果 Winsock 3 发布了,加载 2.2 版本的应用程序仍可一如既社地运行。如果用户要求的 Winsock版本比平台所能支持的版本新,WSAStartup 就会失败。

如果WSAStartup 正确返回,WSADATA 结构中的wHighVersion 就是当前系统中的 Windsock 库能够支持的最新版本。

在使用Winsock 接口编写好应用程序之后,应该调用WSACleanup函数这个函数能够使 Winsock释放所有由 Winsock分配的资源,并取消这个应用程序挂起的 Winsock 调用。

WSACleanup数的定义为:

int WSACleanup(void);

因为操作系统将会自动释放资源,所以退出应用程序时也可以不调用 WSACleanup 函数:

然而如果这样做,您的应用程序就不再符合 Winsock 规范了。另外,每次调用 WSAStartup 后都应该调用WSACleanup。

1.3 错误检查和处理

要想成功编写 Winsock 应用程序,检查和处理错误是全关重要的,所以这里首先对此进行介绍事实上,对 Winsock 函数来说,返回错误是很常见的,但是,在有些情况下,这些错误是无关紧要的通信仍可在那个套接子上进行。Winsock 调用失败时最常见的返回值是 SOCKET ERROR。本书在详细介绍各个API调用时,都将省出和各种错误对应的返回值。

SOCKET ERROR 常量实际上是-1。

如果调用 Winsock 函数时出现了错误,可以用 WSAGetLastError 函数来获得一段代码,这段代码专用来说明错误。该函数的定义如下:

int WSAGetLastError(void);

发生错误之后调用这个函数,返回的是所发生的错误的整数代码。

WSAGetLastError 函数返回的这些错误代码都有已经预先定义的常量值;

因 Winsock 版本的不同,这些值的声明可能在WINSOCK1.H中,也可能在 WINSOCK2 中。

两个头文件的惟一差别是:

  • WINSOCK2H 中包含更多针对 Winsock2中引入的一些新API函数和功能的错误代码。
  • 为各种错误代码定义的常量(带有#dfinc指令)一般都以 WSAE 开头。

相时于 WSAGetLastError函数,另一个与此关的函数是WSASetLastError,使用该函数可以手动设置WSAGetLastError 获取的错误代码。

下述程序演示了如何基于上述内容构建一个Winsock 应用程序框架:

#includecwinsock2.h>
void main(void)
{
WSADATA wsaDatd;
//初始化winsock版本2.2
if ((Ret = WSAStartup(MAKEWORD(2,2),EwsaData))!= 0)
//注意:因为winsock没有加载,所以我们不能使用WSAGetLastError来确定导致故障的特定错//误。但我们将根据WSAStartup的返回状态进行判断
  prinTf("WSAStartup failed with error ed n",Ret);return;
//当应用程序结束调用WSACleanup之片,设置Winsock通信代码
if (wSACleanup() == SOCKET ERROR)
  printf("wSACleanup falled with error d n",WSAGetLastError());
}

下面开始叙述如何使用网络协议建立通信。

1.4 协议寻址

随着Internet 的不断普及,IP 协议随处可得,当今大多数 Winsock 应用程序开发都使用IP,因此为了简便和避免重复,本章其余部分将只描述怎样使用IP(Internet Protocol,网际协议)协议创建基本的Winsock 调用来建立通信。前面已提到,Winsock 是一种独立于协议的接口,第4章将讲述使用其他协议(如 IPX)的具体内容。另外,本章对 IP 的讨论仅限于简单描述 IPV4(IP 第4版)第3章将全面而详细地讲述所有IP版本,包括IPv4 和IPV6(IP 第6版)

本章的其余部分将介绍使用 IPy4 协议建立 Winsock 通信的基本知识。IP 在大多数计算机操作系统中都能得到支持,并可在多数局域网(LAN)上使用,如办公室中的小型网络,也可在广域网(WAN)中使用,例如 Intemmet。从设计角度看,IP 是种无连接协议,它不能确保数据传输的成功。两个高级协议-TCP(Transmission Control Protocol,输控制协议)和 UDP(User Datagram Protocol,用户数据报协议)用通过 IP 进行面向连接和无连接的数据通信,这方面的内容将在以后讲述。TCP 和UDP 都使用IP 进行数据传输,通常被称作 TCP/IP 利UDP/IP。如果需要在 Winsock 中使用IPy4,需要知道怎样为IPv4 寻址。

在IPv4 中,计算机都分配有一个地址,该地址用一个 32 位的数值来表示。客户机需要通过 TCP或UDP 和服务器通信时,必须指定服务器的 IP 地址和服务端号。另外,服务器打算监听代入的客户机请求时,也必须指定一个IP地址和一个端口号。

在Winsock 中,应用程序通过SOCKADDR IN结构来指定 IP 地址和服务端口信息,该结构的格式如下:

struct sockaddr_in{
  short sin family;
  u_short sin port;
  sin addr struct in addr;
  charsin_zero[8];
}

sin family 字段必须设为AF_INET ,以告知 Winsock 此时正在使用IP 地址族。

用标识服务器服务的 TCP 或 UDP 通信端口 sin port 字定义。因为有些可用端口号是为“过知的"服务保留的,如 FTP(文件传输协议,File Transfer Protocol) 和 HTTP(Hypertext Transfer Protocol.超文本传输协议),所以应用程序在选择端口时,必须特别小心。第 2 章中有更多关于选择端的详细内容。

SOCKADDRIN结构的sin addr 宁段把IP4 地址作为一个4字节的量存储起来,它是无符号长整数的数据类型。

根据这个字段的不同用法,它还可表示一个本地或远程 IP 地址。IP 地址一般是用“Internet 标准点分表示法”像 a.b.c.d 一样指定的,其中每个字母代表一个宁节的数字(用十进制、八进制或十六进制格式表示),从无到右分配了一个无符号长整数的 4个字节。后-·个字段 si zero只充当填充项,以使SOCKADDR IN 结构和 SOCKADDR 结构的长度一样。

inet_addr是一个很实用的支持函数,它可把一个点分IP地址转换成一个32 位的无符号长整数

它的定义如下:

unsigned long inet_addr (const char FAR *cp);

cp 字段是一个空终止字符串,用于接受点分表示法的 P 地址。注意,这个函数把IP 地址当作-个按网络字节顺序排列的 32 位无符号长整数返回(网络字节顺序在下面的“字节排序”小节中有简费说明)。

1.4.1 字节排序

不同的计算机处理器采用 big-endian 和 little-endian 形式进行编号,具体采用哪种表示方法,由各自的设计决定。比如,Intel86 处理器上,多字节编号用 little-endian 形式来表示:

  • 字节的排序是从最无意义的字节到最有意义的字节。

在计算机中把 IP 地址和端口号指定成多字节数时,这个数就按 主机字节(host-byte)顺序 来表示。

但是,如果在网络上指定P 地址和端口号,“Intemet 联网标准”指定多字节值必须用 big-endian 形式来表示(从最有意义的宁节到最无意义的字节),般称之为 网络字节(network-byte)顺序

有一系列函数可用于多字节数的转换,把后者从主机字节顺序转换成网络字节顺序,或进行反方向的转换。

下面 4 个API函数使将一个数从机宁节顺序转换成网络字节顺序:

u_long htonl(u long hostlorg);
int WSAHtonl(
SOCKET s,
u_long hostlong,
u_long FAR * lpnetlong
);
u_short htons( short hostshort);
int WSAHtons(
SOCKET s,
u_short hostshort,
u_short FAR * lpnetshort
)

htonlWSAHtonlhostlong 参数是按主机子节顺序排序的一个 4字节数。

htonl函数返回的数是网络字节顺序,而 WSAHtonl 函数通过 pnetlong 参数返的数也按网络节顺序排列。

htons 和WSAHtons 的 hostshort 参数是按机字节顺序排列的一个2节数。

htons 函数把这个数当作按网络字节顺序排列的一个2字节数值返回,而WSAHtons 函数则通过lpnetshort 参数返这个数。

下面这 4 个函数是前面4 个函数的逆向数,它们把网络字节顺序转换成主机字节顺序。

u_long ntohl(u long netlong);
int WSANtohl(
SOCKET s,
u_long netlong,
u_long FAR * lphostlong,
u_short ntohs,
u_short netshort
);
int WSANtohs (
SOCKET s,
u_short netshort,
u_short FAR * lphostshort
)

现在,演示··下如何利用上面描述的 inet_addr 和 htons 函数来创建 SOCKADDR IN 结构,并进行IPv4 寻址。

SOCKADDR_IN InternetAddr;
INT nPortId = 5150;
InternetAddr.sin family = AF_INET;
//将准备使用的点分Internet 地址 136,149.329 转换为4字节整数,并把它分配给 sin addx
InternetAddr.sin addr.s addr = inet_addr("136,149,3,29");
//nPoxtId 变量按存储主机宁节顺序排列。将 nPortId 转换为网络字节顺序,并分配给 sin port
InternetAddr.sin port = htons(nPortid);

IP 地址不便于记忆,因此,大多数人更喜欢使用一个容易记忆且容易掌握的主机名。

第3 章将叙述一些有用的地址利名称解析两数,使用它们可以将主机名(如 www.somewebsite.com)解析为IP地址服务名称(如 FTP)或端口号。

这些函数有 getaddrinfo、getameinfo、gethostbyaddr、gethostbyname、gethostname、getprotobyname、getprotobynumber、getservbyname 以及 getservbyport 等。

同时还有这些函数的异步版本,如: WSAAsyncGetHostByAddr 、 WSAAsyncGetHostByNameWSAAsyncGetProtoByNameWSAAsyncGetProtoByNumber、WSAAsyncGetServByNameWSAAsyncGetServByPort 等。

现在有了协议寻址的基础,诸如 IPv4,就可以准备通过创建套接字来建立通信了。

1.5 创建套接字

熟悉 Winsock 的人应该知道,API 是建立在套接字概念基础上的。套接字是传输提供程序的句柄,在 Windows 中,套接字和文件描述符不是一回事,因而是一个独立的类型,即 WINSOCK2.H 中的SOCKET类型。

有两个函数可以用来创建套接字:

  • socket
  • WSASocket。

随后的3 章将详细讲述如何创建每种可用协议的套接字。

为简便起见,对套接字将仅作简要叙述:

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

第1个参数 af 是协议的地址族。由于本仅使用 IP4 来描述 Winsock,因此应将这个字段设为AF_INET

第 2个参数 type 是协议的套接字类型。如果使用 TCP/P 创建接字,应将该字段设为SOCK_STREAM,而用 UDP/IP 时则应设为 SOCK DGRAM。

第3 个参数是 protocol,用于在给定地址族和套接字类型具有多重入口时,对具体的传送作限定。对于 TCP,应将该字段设为 IPPROTO_TCP而对于UDP则设为IPPROTO_UDP第2章将详细讲述如何创建所有协议的接字,包括 WSASocketAPI在内。

为控制各种套接字选项和套接字行为,Winsock 提供了4个有用的函数:

  • setsockopt
  • getsockopt
  • ioctisocket
  • WSAloctl

在简单的 Wisock 编程中,没有必要特别使用这些函数。第7章节,将描述这些函数及其所有可用选项。

在成功地创建了套接字之后,就可以开始在套接字上建立通信,并为收发数据做好准备。

在 Winsock 中有两种基本的通信技术:

  • 面向连接的通信
  • 无连接的通信


相关文章
|
29天前
|
安全 Windows
【Azure Cloud Service】在Windows系统中抓取网络包 ( 不需要另外安全抓包工具)
通常,在生产环境中,为了保证系统环境的安全和纯粹,是不建议安装其它软件或排查工具(如果可以安装,也是需要走审批流程)。 本文将介绍一种,不用安装Wireshark / tcpdump 等工具,使用Windows系统自带的 netsh trace 命令来获取网络包的步骤
67 32
|
2月前
|
Oracle 关系型数据库 MySQL
Mysql(1)—简介及Windows环境下载安装
MySQL 是一个流行的关系型数据库管理系统(RDBMS),基于 SQL 进行操作。它由瑞典 MySQL AB 公司开发,后被 Sun Microsystems 收购,现为 Oracle 产品。MySQL 是最广泛使用的开源数据库之一,适用于 Web 应用程序、数据仓库和企业应用。
64 2
|
3月前
|
安全 Windows
怎样利用 Windows XP实现网络统一关机
怎样利用 Windows XP实现网络统一关机
怎样利用 Windows XP实现网络统一关机
|
4月前
|
Web App开发 SQL 存储
警惕可能对Windows网络带来风险的工具
警惕可能对Windows网络带来风险的工具
警惕可能对Windows网络带来风险的工具
|
4月前
|
网络协议 Linux 网络安全
【Azure 应用服务】更便捷的方式抓取Azure App Service for Windows的网络包
【Azure 应用服务】更便捷的方式抓取Azure App Service for Windows的网络包
|
4月前
|
Windows
【Azure 环境】在Windows环境中抓取网络包(netsh trace)后,如何转换为Wireshark格式以便进行分析
【Azure 环境】在Windows环境中抓取网络包(netsh trace)后,如何转换为Wireshark格式以便进行分析
|
4月前
|
存储 安全 网络安全
【Azure 环境】使用Azure中的App Service部署Web应用,以Windows为主机系统是否可以启动防病毒,防恶意软件服务呢(Microsoft Antimalware)?
【Azure 环境】使用Azure中的App Service部署Web应用,以Windows为主机系统是否可以启动防病毒,防恶意软件服务呢(Microsoft Antimalware)?
|
4月前
|
安全 Windows
Windows关闭Microsoft Defender服务
【8月更文挑战第19天】以下是关闭Windows系统中Microsoft Defender的几种方法:通过设置界面可临时关闭实时保护等功能;使用组策略编辑器(专业版适用)可较彻底关闭;修改注册表需谨慎;或利用第三方工具如Defender Control便捷操作。关闭后系统将失去实时防护,请确保有其他可靠安全软件保护。
161 0
|
4月前
|
Java 数据安全/隐私保护 Windows
Windows Service 服务中,不能访问挂载目录(网络映射盘)
Windows Service 服务中,不能访问挂载目录(网络映射盘)
134 0
|
4月前
|
Windows
Windows 映射网络驱动器及删除-此网格连接不存在
Windows 映射网络驱动器及删除-此网格连接不存在
464 0
下一篇
DataWorks