[笔记] 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 中有两种基本的通信技术:

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


相关文章
|
25天前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
100 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
1月前
|
机器学习/深度学习 数据可视化 计算机视觉
目标检测笔记(五):详细介绍并实现可视化深度学习中每层特征层的网络训练情况
这篇文章详细介绍了如何通过可视化深度学习中每层特征层来理解网络的内部运作,并使用ResNet系列网络作为例子,展示了如何在训练过程中加入代码来绘制和保存特征图。
56 1
目标检测笔记(五):详细介绍并实现可视化深度学习中每层特征层的网络训练情况
|
1月前
|
机器学习/深度学习 数据可视化 Windows
深度学习笔记(七):如何用Mxnet来将神经网络可视化
这篇文章介绍了如何使用Mxnet框架来实现神经网络的可视化,包括环境依赖的安装、具体的代码实现以及运行结果的展示。
50 0
|
1月前
|
监控 Ubuntu Linux
视频监控笔记(五):Ubuntu和windows时区同步问题-your clock is behind
这篇文章介绍了如何在Ubuntu和Windows系统中通过设置相同的时区并使用ntp服务来解决时间同步问题。
64 4
视频监控笔记(五):Ubuntu和windows时区同步问题-your clock is behind
|
1月前
|
机器学习/深度学习 编解码 算法
轻量级网络论文精度笔记(三):《Searching for MobileNetV3》
MobileNetV3是谷歌为移动设备优化的神经网络模型,通过神经架构搜索和新设计计算块提升效率和精度。它引入了h-swish激活函数和高效的分割解码器LR-ASPP,实现了移动端分类、检测和分割的最新SOTA成果。大模型在ImageNet分类上比MobileNetV2更准确,延迟降低20%;小模型准确度提升,延迟相当。
56 1
轻量级网络论文精度笔记(三):《Searching for MobileNetV3》
|
1月前
|
机器学习/深度学习 网络架构 计算机视觉
目标检测笔记(一):不同模型的网络架构介绍和代码
这篇文章介绍了ShuffleNetV2网络架构及其代码实现,包括模型结构、代码细节和不同版本的模型。ShuffleNetV2是一个高效的卷积神经网络,适用于深度学习中的目标检测任务。
68 1
目标检测笔记(一):不同模型的网络架构介绍和代码
|
1月前
|
机器学习/深度学习 数据采集 算法
目标分类笔记(一): 利用包含多个网络多种训练策略的框架来完成多目标分类任务(从数据准备到训练测试部署的完整流程)
这篇博客文章介绍了如何使用包含多个网络和多种训练策略的框架来完成多目标分类任务,涵盖了从数据准备到训练、测试和部署的完整流程,并提供了相关代码和配置文件。
46 0
目标分类笔记(一): 利用包含多个网络多种训练策略的框架来完成多目标分类任务(从数据准备到训练测试部署的完整流程)
|
1月前
|
编解码 人工智能 文件存储
轻量级网络论文精度笔记(二):《YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object ..》
YOLOv7是一种新的实时目标检测器,通过引入可训练的免费技术包和优化的网络架构,显著提高了检测精度,同时减少了参数和计算量。该研究还提出了新的模型重参数化和标签分配策略,有效提升了模型性能。实验结果显示,YOLOv7在速度和准确性上超越了其他目标检测器。
47 0
轻量级网络论文精度笔记(二):《YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object ..》
|
1月前
|
机器学习/深度学习 Python
深度学习笔记(九):神经网络剪枝(Neural Network Pruning)详细介绍
神经网络剪枝是一种通过移除不重要的权重来减小模型大小并提高效率的技术,同时尽量保持模型性能。
49 0
深度学习笔记(九):神经网络剪枝(Neural Network Pruning)详细介绍
|
1月前
|
机器学习/深度学习 算法 TensorFlow
深度学习笔记(五):学习率过大过小对于网络训练有何影响以及如何解决
学习率是深度学习中的关键超参数,它影响模型的训练进度和收敛性,过大或过小的学习率都会对网络训练产生负面影响,需要通过适当的设置和调整策略来优化。
273 0
深度学习笔记(五):学习率过大过小对于网络训练有何影响以及如何解决