为何要捕捉IOException异常的原因在标号17中,在此是为了处理客户端在关闭套接字之前的过早结束。
客户端套接字
现在,让我们来看一下客户端程序(参见例2)。在连接到服务端之后,客户端将发送一对随机的整数,并且在发送下一对之前等待返回的结果。此处我们所看到的是服务端与客户端的同步通讯,客户端在接收到前一对值的结果之前,是不会发送另一对新值的。
例2:
using namespace System;
using namespace System::IO;
using namespace System::Net;
using namespace System::Net::Sockets;
using namespace System::Threading;
int main(array<String^>^ argv)
{
if (argv->Length != 2)
{
Console::WriteLine("Usage: Client port message-count");
Environment::Exit(1);
}
int port = 0;
try
{
port = Int32::Parse(argv[0]);
}
catch (FormatException^ e)
{
Console::WriteLine("Port number {0} is ill-formed", argv[0]);
Environment::Exit(2);
}
if (port < IPEndPoint::MinPort || port > IPEndPoint::MaxPort)
{
Console::WriteLine("Port number must be in the range {0}-{1}",IPEndPoint::MinPort, IPEndPoint::MaxPort);
Environment::Exit(3);
}
int messageCount = 0;
try
{
messageCount = Int32::Parse(argv[1]);
}
catch (FormatException^ e)
{
Console::WriteLine("Message count {0} is ill-formed", argv[1]);
Environment::Exit(4);
}
IPAddress^ ipAddress = nullptr;
try
{
/*1*/ ipAddress = Dns::GetHostEntry(Dns::GetHostName())->AddressList[0];
/*2*/ IPEndPoint^ ipEndpoint = gcnew IPEndPoint(ipAddress, port);
/*3*/ Socket^ clientSocket = gcnew Socket(AddressFamily::InterNetwork,
SocketType::Stream, ProtocolType::Tcp);
/*4*/ clientSocket->Connect(ipEndpoint);
NetworkStream^ netStream = gcnew NetworkStream(clientSocket);
BinaryReader^ br = gcnew BinaryReader(netStream);
BinaryWriter^ bw = gcnew BinaryWriter(netStream);
int value1, value2;
int result;
Random^ random = gcnew Random;
(int i = 1; i <= messageCount; ++i)
{
/*5*/ value1 = static_cast<int>(random->NextDouble() * 100);
/*6*/ value2 = static_cast<int>(random->NextDouble() * 100);
/*7*/ bw->Write(value1);
/*8*/ bw->Write(value2);
Console::Write("Sent values {0,3} and {1,3}",value1, value2);
/*9*/ result = br->ReadInt32();
Console::WriteLine(", received result {0,3}", result);
/*10*/ Thread::Sleep(3000);
}
/*11*/ clientSocket->Shutdown(SocketShutdown::Both);
Console::WriteLine("Notified server we're shutting down");
/*12*/ clientSocket->Close();
/*13*/ netStream->Close();
Console::WriteLine("Shutting down client");
}
/*14*/ catch (SocketException^ e)
{
Console::WriteLine("Request to connect to {0} on port {1} failed"+ "\nbecause of {2}", ipAddress, port, e);
Environment::Exit(5);
}
}
如同服务端一样,客户端取得一个IP地址,把它与端口号绑定以生成一个IPEndPoint,并连接到服务端,而服务端在此之前一直处于阻塞监听模式。
在每一个发送与接收操作之间,我们有意延迟三秒,以便观察程序的输出。
以下是一个服务端程序使用端口2500时的输出:
Server listener blocking status is True
New connection accepted
Received values 42 and 69, sent result 111
Received values 66 and 71, sent result 137
Received values 7 and 93, sent result 100
Received values 43 and 65, sent result 108
Received values 45 and 3, sent result 48
Shutting down server
而以下是对应的客户端程序,在发送5对值之后的输出:
Sent values 42 and 69, received result 111
Sent values 66 and 71, received result 137
Sent values 7 and 93, received result 100
Sent values 43 and 65, received result 108
Sent values 45 and 3, received result 48
Notified server we're shutting down
Shutting down client 套接字上的串行化
前面所演示的服务端与客户端程序以简单的方式进行数值交换,如int,然而,程序很有可能也会需要发送与接收各种不同的用户自定义的对象类型,这就涉及到串行化。
试想某些金融程序所涉及到的许多事务类型,如存款、转账、取款,每一种都与事务有关。在此,只需简单地设置好适当的串行化与反串行化机制,服务端就能处理多个客户端请求,并可返回这些事务的任意数量与任意组合。
以下有一些练习来加深对此的了解:






