| using namespace System; using namespace System::IO; using namespace System::Net; using namespace System::Net::Sockets; int main(array<String^>^ argv) { if (argv->Length != 1) { Console::WriteLine("Usage: Server port"); 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); } /*1*/ 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); } /*2*/ IPAddress^ ipAddress = Dns::GetHostEntry(Dns::GetHostName())->AddressList[0]; /*3*/ IPEndPoint^ ipEndpoint = gcnew IPEndPoint(ipAddress, port); /*4*/ Socket^ listenerSocket = gcnew Socket(AddressFamily::InterNetwork, SocketType::Stream, ProtocolType::Tcp); /*5*/ listenerSocket->Bind(ipEndpoint); /*6*/ listenerSocket->Listen(1); /*7*/ Console::WriteLine("Server listener blocking status is {0}", listenerSocket->Blocking); /*8*/ Socket^ serverSocket = listenerSocket->Accept(); Console::WriteLine("New connection accepted"); /*9*/ listenerSocket->Close(); /*10*/ NetworkStream^ netStream = gcnew NetworkStream(serverSocket); /*11*/ BinaryReader^ br = gcnew BinaryReader(netStream); /*12*/ BinaryWriter^ bw = gcnew BinaryWriter(netStream); try { int value1, value2; int result; while (true) { /*13*/ value1 = br->ReadInt32(); /*14*/ value2 = br->ReadInt32(); Console::Write("Received values {0,3} and {1,3}", value1, value2); result = value1 + value2; /*15*/ bw->Write(result); Console::WriteLine(", sent result {0,3}", result); } } /*16*/ catch (EndOfStreamException^ e) { } /*17*/ catch (IOException^ e) { Console::WriteLine("IOException {0}", e); } /*18*/ serverSocket->Shutdown(SocketShutdown::Both); /*19*/ serverSocket->Close(); /*20*/ netStream->Close(); Console::WriteLine("Shutting down server"); } |
此处与套接字相关的功能由命名空间System::Net和System::Net::Sockets提供,并且需要在生成期间引用System.dll程序集。另外,因为通过套接字的通讯涉及到流,所以还要用到System:IO机制。
当程序执行时,服务端需要知道其用来监听客户端连接请求的端口号,在此,这个整数值通过命令行参数提供。一般来说,端口号在0-65535范围内,而0-1023保留给特定的用途,因此,服务端可用的端口号就为1024-65535。
在标号1中,通过IPEndPoint类中的MinPort和MaxPort这两个公共静态字段,就可得到特定系统上可用的端口范围。
而在标号2中,可得到我们自己的主机名,并解析到一个IpHostEntry,可从中取得本机的IP地址。接下来在标号3中,用IP地址和端口号创建了一个IPEndPoint对象,其可为某个连接提供某种服务。
在标号4中,创建了一个Internet传输服务托管版本的套接字,一旦它被创建,就应通过Bind函数(标号5)绑定到一个特定的端点。接下来,套接字声明其已经开始服务,并监听连接请求(标号6)。传递给Listen的参数表明了请求队列中连接挂起的长度,因为我们只有一个客户端,所以在此1就足够了。
套接字默认以阻塞模式创建,如标号7中所示,这意味着,它会一直等待连接请求。
当从客户端接收到连接请求时,阻塞的套接字就会被唤醒,通过调用Accept(如标号8),接受请求并创建另一个套接字,并通过此套接字来与客户端通讯。我们看到,此时的服务端有两个套接字:一个用于监听客户连接请求,而另一个用于与连接的客户端通讯。当然,一个服务端在同一时间,可与多个客户端进行连接,且每个客户端都有一个套接字。
在这个简单的例子中,我们只关心请求连接的第一个客户端,一旦连接上了,便可关闭此监听连接请求的套接字(参见标号9)。
在标号10-12中,我们用最近连接的套接字,建立了一个NetworkStream,连同两个读写函数一起,便可以从套接字接收请求,并返回结果。
服务端在此无限循环,读入一对整数,计算它们的和,并把结果返回给客户端。当服务端探测到输入流中的文件结束标志时(由客户端关闭了套接字),会抛出EndOfStreamException异常,并关闭I/O流和套接字,服务结束。
标号18中的Socket::ShutDown调用将同时关闭套接字上的接收和发送功能,因为我们的服务端只需告之一个客户端它的关闭,所以此函数调用有点多余,但是,在服务端要过早地结束的某些情况下,这种做法还是有用的。
为何要捕捉IOException异常的原因在标号17中,在此是为了处理客户端在关闭套接字之前的过早结束。
http://dev.xuezhishi.net/program/VC/2007-06-21/18449.html