套接字,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。Socket原意是 “插座”。通过将这3个参数结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
Socket可以看成在两个程序进行通讯连接中的一个端点,是连接应用程序和网络驱动程序的桥梁,Socket在应用程序中创建,通过绑定与网络驱动建立关系。此后,应用程序送给Socket的数据,由Socket交给网络驱动程序向网络上发送出去。计算机从网络上收到与该Socket绑定IP地址和端口号相关的数据后,由网络驱动程序交给Socket,应用程序便可从该Socket中提取接收到的数据,网络应用程序就是这样通过Socket进行数据的发送与接收的。
Host A上的程序A将一段信息写入Socket中,Socket的内容被Host A的网络管理软件访问,并将这段信息通过Host A的网络接口卡发送到Host B,Host B的网络接口卡接收到这段信息后,传送给Host B的网络管理软件,网络管理软件将这段信息保存在Host B的Socket中,然后程序B才能在Socket中阅读这段信息。
假设在网络中添加第三个主机Host C,那么Host A怎么知道信息被正确传送到Host B而不是被传送到Host C中了呢?基于TCP/IP网络中的每一个主机均被赋予了一个唯一的IP地址,IP地址是一个32位的无符号整数,由于没有转变成二进制,因此通常以小数点分隔,如:198.163.227.6,正如所见IP地址均由四个部分组成,每个部分的范围都是0-255,以表示8位地址。
值得注意的是IP地址都是32位地址,这是IP协议版本4(简称Ipv4)规定的,目前由于IPv4地址已近耗尽,所以IPv6地址正逐渐代替Ipv4地址,Ipv6地址则是128位无符号整数。
假设第二个程序被加入的网络的Host B中,那么由Host A传来的信息如何能被正确的传给程序B而不是传给新加入的程序呢?这是因为每一个基于TCP/IP网络通讯的程序都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留Socket中的输入/输出信息,端口号是一个16位无符号整数,范围是0-65535,以区别主机上的每一个程序(端口号就像房屋中的房间号),低于256的端口号保留给标准应用程序,比如pop3的端口号就是110,每一个套接字都组合进了IP地址、端口、端口号,这样形成的整体就可以区别每一个套接字。
Sockets:
流式套接字
本文描述流式套接字,它是两种可用的 Windows Sockets 类型中的一种。(另一种类型是数据文报套接字 。)
流式套接字提供没有记录边界的数据流:可以是双向的字节流(应用程序是全双工:可以通过套接字同时传输和接收)。可依赖流传递有序的、不重复的数据。(“有序”指数据包按发送顺序送达。“不重复”指一个特定的数据包只能获取一次。)这能确保收到流消息,而流非常适合处理大量数据。
网络传输层可将数据拆分为分组或若干个大小适当的数据包。 CSocket 类将为您处理打包和解包。
流基于显式连接:套接字 A 请求与套接字 B 建立连接;套接字 B 接受或拒绝此连接请求。
打电话的情况与流非常相似:正常情况下,接听方听到您的话和您讲话时的顺序一样,没有重复和遗漏。流套接字适合文件传输协议(FTP) 这类实现,此协议有利于传输任意大小的 ASCII 或二进制文件。
如果必须保证数据送达而且数据大小很大时,流式套接字优于数据文报套接字。有关流式套接字的更多信息,请参见 Windows Sockets 规范。该规范可在 Platform SDK 中获得。
MFC 示例 CHATTER 和 CHATSRVR 都使用流式套接字。这些示例可能已经设计为使用数据文报套接字向网络上的所有接收套接字广播。而目前的设计更好,这是因为:
广播模型受制于网络“洪水”(或“风暴”)问题。
后来采用的客户端-服务器模型更有效。
流式模型提供可靠的数据传输,数据文报模型则未提供。
最终模型利用在 CArchive 类借给 CSocket 类的 Unicode 和 ANSI 套接字应用程序之间通信的能力。
如果使用 CSocket 类,则必须使用流。如果将套接字类型指定为 SOCK_DGRAM ,则 MFC 断言失败
Windows Sockets 示例列表
下列 MFC 示例程序阐释了 Windows Sockets 功能:
CHATTER
CHATTER 是一个 Windows 套接字客户端示例应用程序。它是一个具有拆分窗口的单文档界面 (SDI) 应用程序,允许用户将消息发送到讨论服务器 (CHATSRVR),讨论服务器然后将消息同时发送给其他多个 CHATTER 用户。
通过使 CHATTER 应用程序向服务器发送广播数据文报包而不是消息流,可以在不使用客户端/服务器模型的情况下编写 CHATTER 和 CHATSRVR。然而,与流式套接字不同,数据文报套接字不能保证一定会被传送;因此,一些消息可能不会到达讨论中的所有其他用户。
运行实例:
生成并运行 CHATTER 示例
打开解决方案 chatter.sln。
在“生成”菜单上单击“生成”。
在“调试”菜单上单击“开始执行(不调试)”。
运行 CHATTER 时,有一个“Setup”对话框请求输入以下内容:
Handle
用来寻址所有消息的名称。例如,可以选择“”。发送的所有消息的前面都会自动加上名称“”。
Server
运行 CHATSVR 示例的计算机的 IP 地址。
Channel
标识要加入的讨论的数字(一台计算机可以运行多个讨论服务器)。
提供了所有这些信息并单击“OK”后,主应用程序窗口随即出现。若要发送消息,请在下部窗格中键入消息。按 ENTER 键发送消息。若要发送多行消息,请按 CTRL+ENTER 键。关键字
示例:
此示例说明了以下关键字:
AfxGetApp、AfxMessageBox、CArchive::Flush、CArchive::IsStoring、CControlBar::EnableDocking、CControlBar::GetBarStyle、CControlBar::SetBarStyle、CDialog::DoModal、CDocument::DeleteContents、CDocument::GetFirstViewPosition、CDocument::GetNextView、CDocument::OnNewDocument、CEditView::GetEditCtrl、CEditView::SerializeRaw、CFrameWnd::DockControlBar、CFrameWnd::EnableDocking、CFrameWnd::OnCreateClient、CFrameWnd::SetActiveView、CObject::AssertValid、CObject::Dump、CObject::IsKindOf、CObject::Serialize、CRect::Size、CSplitterWnd::CreateView、CSplitterWnd::GetPane、CStatusBar::Create、CStatusBar::SetIndicators、CString::GetBuffer、CString::GetLength、CString::IsEmpty、CString::LoadString、CString::ReleaseBuffer、CToolBar::Create、CToolBar::LoadBitmap、CToolBar::SetButtons、CView::GetDocument、CView::OnDraw、CWinApp::AddDocTemPlate、CWinApp::InitInstance、CWinApp::LoadStdProfileSettings、CWinApp::OnFileNew、CWnd::DestroyWindow、CWnd::DoDataExchange、CWnd::GetClientRect、CWnd::GetWindowText、CWnd::GetWindowTextLength、CWnd::KillTimer、CWnd::OnChar、CWnd::OnCreate、CWnd::OnTimer、CWnd::PreCreateWindow、CWnd::SetTimer、CWnd::SetWindowText、SetWindowText、rand、wsprintf
注意一些示例(如此示例)尚未经过修改以反映Visual C++向导、库和编译器的变化,但仍说明了如何完成所需的任务。
请参见
MFC 示例
CHATSRVR
CHATSRVR 是 Windows 套接字服务器示例应用程序,它是一个单文档界面 (SDI) 应用程序,用于为 CHATTER 示例的客户端实现讨论服务器。
通过使 CHATTER 应用程序向服务器发送广播数据文报包而不是消息流,可以在不使用客户端/服务器模型的情况下编写 CHATTER和 CHATSRVR。然而,与流式套接字不同,数据文报套接字不能保证一定会被传送;因此,一些消息可能不会到达讨
论中的所有其他用户。生成并运行示例
生成并运行 CHATSRVR 示例
打开解决方案 chatsrvr.sln。
在“生成”菜单上单击“生成”。
在“调试”菜单上单击“开始执行(不调试)”。
运行 CHATSRVR 时会显示一个请求输入“Channel”的“Discussion”对话框。“Channel”是标识要支持的讨论的数字(一台计算机可以运行多个讨论服务器)。提供了此信息并单击“OK”后,主应用程序窗口随即出现。关键字
关键字:
AfxMessageBox、CArchive::Flush、CArchive::IsStoring、CCmdUI::Enable、CCmdUI::SetText、CControlBar::EnableDocking、CControlBar::GetBarStyle、CControlBar::SetBarStyle、CDialog::DoModal、CDocument::DeleteContents、CDocument::OnNewDocument、CEditView::GetEditCtrl、CFrameWnd::DockControlBar、CFrameWnd::EnableDocking、CObject::AssertValid、CObject::Dump、CObject::Serialize、CStatusBar::Create、CStatusBar::SetIndicators、CString::GetBuffer、CString::LoadString、CString::ReleaseBuffer、CToolBar::Create、CToolBar::LoadBitmap、CToolBar::SetButtons、CView::GetDocument、CView::OnDraw、CWinApp::AddDocTemplate、CWinApp::ExitInstance、CWinApp::InitInstance、CWinApp::LoadStdProfileSettings、CWinApp::OnFileNew、CWnd::DoDataExchange、CWnd::GetWindowTextLength、CWnd::OnCreate、SetWindowText、wsprintf
注意 一些示例(如此示例)尚未经过修改以反映 Visual C++ 向导、库和编译器的变化,但仍说明了如何完成所需的任务。
通信:
要通过Internet进行通信,至少需要一对套接字,其中一个运行在客户端,称之为ClientSocket,另一个运行于服务器端面,称为ServerSocket。根据连接启动的方式以及本地要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听、客户端请求、连接确认。
服务器监听是指服务端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
客户端请求是由客户端的套接字提出连接请求,要连接的目标是服务器端套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器套接字的地址和端口号,然后再向服务器端套接字提出连接请求。
连接确认是当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的信息发送给客户端,一旦客户端确认了此连接,连接即可建立。而服务器端继续处于监听状态,继续接收其他客户端的连接请求。
使用套接字进行数据处理有两种基本模式:同步和异步。
同步模式:
同步模式的特点是在通过Socket进行连接、接收、发送数据时,客户机和服务器在接收到对方响应前会处于阻塞状态,即一直等到收到对方请求进才继续执行下面的语句。可见,同步模式只适用于数据处理不太多的场合。当程序执行的任务很多时,长时间的等待可能会让用户无法忍受。
异步模式:
异步模式的特点是在通过Socket进行连接、接收、发送操作时,客户机或服务器不会处于阻塞方式,而是利用callback机制进行连接、接收、发送处理,这样就可以在调用发送或接收的方法后直接返回,并继续执行下面的程序。可见,异步套接字特别适用于进行大量数据处理的场合。
使用同步套接字进行编程比较简单,而异步套接字编程则比较复杂。