本文介绍 Windows Sockets 的性质和用途。其他内容还包括:
Windows Sockets 规范为 Microsoft Windows
定义了一个二进制兼容网络编程接口。Windows Sockets 基于 Berkeley Software Distribution(BSD,4.3 版)中的
UNIX 套接字实现,后者是美国加州大学伯克利分校开发的。该规范包括针对 Windows 的 BSD 样式套接字例程和扩展。通过使用 Windows
Sockets,应用程序能够在任何符合 Windows Sockets API 的网络上通信。在 Win32 上,Windows Sockets 提供线程安全。
许多网络软件供应商支持网络协议下的 Windows Sockets,这些协议包括:传输控制协议/网际协议
(TCP/IP)、Xerox 网络系统 (XNS)、Digital Equipment Corporation 的 DECNet 协议和
Novell Corporation 的互联网包交换协议/顺序分组报文交换协议 (IPX/SPX) 等。虽然目前的 Windows Sockets 规范定义了
TCP/IP 的套接字抽象化,但任何网络协议都可以通过提供自己版本的、实现 Windows Sockets 的动态链接库 (DLL) 来满足 Windows
Sockets。用 Windows Sockets 编写的商用应用程序示例包括 X Windows 服务器、终端模拟器和电子邮件系统。
注意: Windows Sockets
的用途是将基础网络抽象出来,这样,您不必对网络非常了解,并且您的应用程序可在任何支持套接字的网络上运行。因此,本文档不讨论网络协议的细节内容。
Microsoft 基础类库 (MFC) 通过提供两个类来支持使用 Windows Sockets
API 进行编程。其中一个类为 CSocket ,它提供高级抽象化来简化网络通信编程。
Windows Sockets 规范“Windows Sockets:用于 Microsoft
Windows 环境下的网络计算的开放接口”现在为 1.1 版本,它是 TCP/IP
群体中一个由个人和公司组成的大团体开发的,是一个开放的网络标准,可免费使用。套接字编程模型当前支持一个“通信域”,该“通信域”使用网际协议组 (Internet
Protocol Suite)。该规范可在 Platform SDK 中获得。
提示: 因为套接字使用网际协议组,所以它们对于支持“信息高速公路”上 Internet 通信的应用程序是首选方式。
套接字的定义
套接字是一个通信终结点,它是 Windows Sockets
应用程序用来在网络上发送或接收数据包的对象。套接字具有类型,与正在运行的进程相关联,并且可以有名称。目前,套接字一般只与使用网际协议组的同一“通信域”中的其他套接字交换数据。
这两种套接字都是双向的,是可以同时在两个方向上(全双工)进行通信的数据流。
可用的套接字类型有以下两种:
流式套接字提供没有记录边界的数据流,即字节流。字节流能确保以正确的顺序无重复地被送达。
数据文报套接字支持面向记录的数据流,但不能确保能被送达,也无法确保按照发送顺序或不重复。
“有序”指数据包按发送的顺序送达。“不重复”指一个特定的数据包只能获取一次。
注意: 在某些网络协议下(如 XNS),流可以面向记录,即作为记录流而非字节流。但在更常用的 TCP/IP
协议下,流为字节流。Windows Sockets 提供与基础协议无关的抽象化级别。
有关上述类型以及各种套接字适用情形的信息,请参见 Windows Sockets:流式套接字和 Windows Sockets:数据文报套接字。
SOCKET 数据类型
每一个 MFC 套接字对象封装一个 Windows Sockets 对象的句柄。该句柄的数据类型为
SOCKET。SOCKET 句柄类似于窗口的 HWND。MFC 套接字类提供对封装句柄的操作。
Platform SDK 中详细描述了 SOCKET 数据类型。
套接字的用途
套接字的作用非常大,至少在下面三种通信上下文中如此:
提示: 最适合使用 MFC 套接字的情况是当同时编写通信的两端时:在两端都使用 MFC。有关该主题(包括如何管理与非
MFC 应用程序通信的情况)的更多信息,请参见 Windows Sockets:字节排序
。
本文描述流式套接字,它是两种可用的 Windows Sockets 类型中的一种。(另一种类型是数据文报套接字 。)
流式套接字提供没有记录边界的数据流:可以是双向的字节流(应用程序是全双工:可以通过套接字同时传输和接收)。可依赖流传递有序的、不重复的数据。(“有序”指数据包按发送顺序送达。“不重复”指一个特定的数据包只能获取一次。)这能确保收到流消息,而流非常适合处理大量数据。
网络传输层可将数据拆分为或分组为若干个大小适当的数据包。 CSocket
类将为您处理打包和解包。
流基于显式连接:套接字 A 请求与套接字 B 建立连接;套接字 B 接受或拒绝此连接请求。
打电话的情况与流非常相似:正常情况下,接听方听到您的话和您讲话时的顺序一样,没有重复和遗漏。流套接字适合文件传输协议
(FTP) 这类实现,此协议有利于传输任意大小的 ASCII 或二进制文件。
如果必须保证数据送达而且数据大小很大时,流式套接字优于数据文报套接字。有关流式套接字的更多信息,请参见
Windows Sockets 规范。该规范可在 Platform SDK 中获得。
MFC 示例 CHATTER 和 CHATSRVR
都使用流式套接字。这些示例可能已经设计为使用数据文报套接字向网络上的所有接收套接字广播。而目前的设计更好,这是因为:
注意: 如果使用 CSocket 类,则必须使用流。如果将套接字类型指定为 SOCK_DGRAM ,则 MFC
断言失败。
本文描述数据文报套接字,它是两种可用的 Windows Sockets 类型中的一种。(另一种类型是
流式套接字 。)
数据文报套接字支持双向数据流,此数据留不能保证按顺序和不重复送达。数据文报也不保证是可靠的;它们可能无法到达目的地。数据文报可能不按顺序到达并且可能会重复,但只要记录的大小没有超过接收端的内部大小限制,就会保持数据中的记录边界。您负责管理顺序和可靠性。(可靠性在局域网
[LAN] 上往往很好,但在广域网 [WAN] 如 Internet 上却不太好。)
数据文报为“无连接”的,也就是不建立显式连接。可将数据文报消息发送到指定的套接字,然后从指定的套接字接收消息。
数据文报套接字的一个示例是使网络上的系统时钟保持同步的应用程序。这阐释了数据文报套接字的一个附加功能,即至少在某些设置中,向大量的网络地址广播消息。
在面向记录的数据方面,数据文报套接字优于流式套接字。有关数据文报套接字的更多信息,请参见
Platform SDK 中的 Windows Sockets 规范。
本文以及另外两篇相关文章解释 Windows Sockets 编程方面的一些问题。本文介绍字节排序。其他问题在文章 Windows Sockets:阻塞和 Windows Sockets:转换字符串中介绍。
如果使用 CAsyncSocket 类或从其派生,则您需要自己管理这些问题。如果您使用 CSocket 类或从其派生,则由 MFC
管理它们。
不同的计算机结构有时使用不同的字节顺序存储数据。例如,基于 Intel 的计算机存储数据的顺序与 Macintosh
(Motorola) 计算机相反。Intel 字节顺序称为“Little-Endian”,与网络标准“Big-Endian”顺序也相反。下表解
释这些术语。
Big-Endian 和 Little-Endian 字节排序
字节排序 | 含义 |
---|---|
Big-Endian | 最重要的字节在词的左端。 |
Little-Endian | 最重要的字节在词的右端。 |
通常,您不必为在网络上发送和接收的数据的字节顺序转换担心,但在有些情况下,您必须转换字节顺序。
在下列情况中需要转换字节顺序:
必须为网络理解。
排序,则需要字节顺序转换。
在下列情况下可以免去转换字节顺序的工作:
使用 CAsyncSocket 时,您必须自己管理任何必需的字节顺序转换。Windows Sockets 将“Big-Endian”字节顺
序模型标准化,并提供在该顺序和其他顺序之间转换的函数。然而,与 CSocket 一起使用的 CArchive 使用相
反的顺序(“Little-Endian”),但 CArchive 为您管理字节顺序转换的细节。通过在应用程序中使用这种标准
排序,或通过使用 Windows Sockets 字节顺序转换函数,可增强代码的可移植性。
最适合使用 MFC 套接字的情况是当编写通信的两端时:在两端都使用 MFC。如果正在编写将与非 MFC 应用程序
通信的应用程序(如 FTP 服务器),则在向存档对象传递数据前,您可能需要使用 Windows Sockets 转换例程
ntohs 、 ntohl 、 htons 和 htonl
自己管理字节交换。本文稍后将给出这些用于与非 MFC 应用程序通信的函数
示例。
注意 当通信的另一端不是 MFC 应用程序时,也必须避免将从 CObject 派生的 C++ 对象以流的形式输入存
档,因为接收端无法处理它们。请参见 Windows
Sockets:使用带存档的套接字中的说明。
有关字节顺序的更多信息,请参见 Platform SDK 中的 Windows Sockets 规范。
下面的示例显示使用存档的 CSocket 对象的一个序列化函数。它还阐释了在 Windows Sockets API 中如何使用
字节顺序转换函数。
该示例显示这样的情形:您正在编写与非 MFC 服务器应用程序通信的客户程序,而您没有访问该服务器应用程序
源代码的权限。在这种情况下,必须假设非 MFC 服务器使用标准的网络字节顺序。相反,MFC 客户端应用程序对
CSocket 对象使用 CArchive 对象,而 CArchive
使用与网络标准相反的“Little-Endian”字节顺序。
假设要与之通信的非 MFC 服务器具有如下已建立的消息包协议:
struct Message { long MagicNumber; unsigned short Command; short Param1; long Param2; };
上述内容用 MFC 术语表示则为:
struct Message { long m_lMagicNumber; short m_nCommand; short m_nParam1; long m_lParam2; void Serialize
( CArchive& ar ); };
在 C++ 中, struct 和类在本质上是一回事。 Message
结构可以有成员函数,如以上声明的 Serialize
成员函数。
Serialize
成员函数可能为如下形式: void Message::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar < < (DWORD)htonl(m_lMagicNumber);
ar < <
(WORD)htons(m_nCommand);
ar < < (WORD)htons(m_nParam1);
ar <
< (DWORD)htonl(m_lParam2);
}
else
{
WORD w;
DWORD dw;
ar > > dw;
m_lMagicNumber = ntohl((long)dw);
ar > > w ;
m_nCommand = ntohs((short)w);
ar > > w;
m_nParam1 =
ntohs((short)w);
ar > > dw;
m_lParam2 = ntohl((long)dw);
}
}
该示例要求进行数据字节顺序转换,因为一端的非 MFC 服务器应用程序的字节排序与另一端在 MFC 客户端应用程序中使用的 CArchive
明显不匹配。该示例阐释了 Windows Sockets 提供的几个字节顺序转换函数。下表描述了这些函数。
Windows Sockets 字节顺序转换函数
函数 | 作用 |
---|---|
ntohs | 将 16 位数量从网络字节顺序转换为主机字节顺序(从 Big-Endian 转换为 Little-Endian)。 |
ntohl | 将 32 位数量从网络字节顺序转换为主机字节顺序(从 Big-Endian 转换为 Little-Endian)。 |
htons | 将 16 位数量从主机字节顺序转换为网络字节顺序(从 Little-Endian 转换为 Big-Endian)。 |
htonl | 将 32 位数量从主机字节顺序转换为网络字节顺序(从 Little-Endian 转换为 Big-Endian)。 |
此示例的另一个要点是,当通信另一端的套接字应用程序为非 MFC 应用程序时,必须避免出现如下列语句的操作:
ar pMsg;
这里的 pMsg
是指向从 CObject 类派生的 C++
对象的指针。这将发送多余的与对象关联的 MFC 信息,而服务器并不理解这些信息,因为只有服务器是 MFC 应用程序时才理解。
有关更多信息,请参见: