上面服务端、客户端的代码已经建立起了连接,这通过使用“netstat -a”命令,从端口的状态可以看出来,但这是操作系统告诉我们的。那么我们现在需要知道的就是:服务端的程序如何知道已经与一个客户端建立起了连接?
服务器端开始侦听以后,可以在TcpListener实例上调用AcceptTcpClient()来获取与一个客户端的连接,它返回一个 TcpClient类型实例。此时它所包装的是由服务端去往客户端的Socket,而我们在客户端创建的TcpClient则是由客户端去往服务端的。这 个方法是一个同步方法(或者叫阻断方法,block method),意思就是说,当程序调用它以后,它会一直等待某个客户端连接,然后才会返回,否则就会一直等下去。这样的话,在调用它以后,除非得到一个 客户端连接,不然不会执行接下来的代码。一个很好的类比就是Console.ReadLine()方法,它读取输入在控制台中的一行字符串,如果有输入, 就继续执行下面代码;如果没有输入,就会一直等待下去。
- Console.WriteLine("Server is running ..");
- IPAddress ip = new IPAddress(new byte[]{127,0,0,1});
- TcpListener listener = new TcpListener(ip, 801);
- listener.Start();//开始侦听
- Console.WriteLine("Start Listening ..");
- //获取连接
- TcpClient remoteClient = listener.AcceptTcpClient();
- //打印客户端信息
- Console.WriteLine("Client Connectioned!{0}-------->{1}", remoteClient.Client.LocalEndPoint, remoteClient.Client.RemoteEndPoint);
运行这段代码,会发现服务端运行到listener.AcceptTcpClient()时便停止了,并不会执行下面的 Console.WriteLine()方法。为了让它继续执行下去,必须有一个客户端连接到它,所以我们现在运行客户端,与它进行连接。简单起见,我们 只在客户端开启一个端口与之连接:
- Console.WriteLine("Client Running ..");
- TcpClient client = new TcpClient();
- try
- {
- client.Connect("localhost", 801);
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- return;
- }
- //打印连接到服务端信息
- Console.WriteLine("Server Connnected!{0}---->{1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint);
现在我们再接着考虑,如果有多个客户端发动对服务器端的连接会怎么样,我们先看下客户端的关键代码:
- Console.WriteLine("Client Running ..");
- TcpClient client;
- for (int i = 0; i < 5; i++)
- {
- try
- {
- client = new TcpClient();
- client.Connect("localhost", 801);
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- return;
- }
- //打印连接到服务端信息
- Console.WriteLine("Server Connnected!{0}---->{1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint);
- }
如果服务端代码不变,我们先运行服务端,再运行客户端,那么接下来会看到这样的输出:
就又回到了“多个客户端与服务端连接”中的处境:尽管有三个客户端连接到了服务端,但是服务端程序只接收到了一个。这是因为服务端只调用了一次listener.AcceptTcpClient(),而它只对应一个连往客户端的Socket。但是操作系统是知道连接已经建立了的,只是我们程序中没有处理到,所以我们当我们输入“netstat -a”时,仍然会看到3对连接都已经建立成功。
为了能够接收到三个客户端的连接,我们只要对服务端稍稍进行一下修改,将AcceptTcpClient方法放入一个do/while循环中就可以了:
- Console.WriteLine("Server is running ..");
- IPAddress ip = new IPAddress(new byte[]{127,0,0,1});
- TcpListener listener = new TcpListener(ip, 801);
- listener.Start();//开始侦听
- Console.WriteLine("Start Listening ..");
- while (true)
- {
- //获取连接
- TcpClient remoteClient = listener.AcceptTcpClient();
- //打印客户端信息
- Console.WriteLine("Client Connectioned!{0}-------->{1}", remoteClient.Client.LocalEndPoint, remoteClient.Client.RemoteEndPoint);
- }
这样看上去是一个死循环,但是并不会让你的机器系统资源迅速耗尽。因为前面已经说过了,AcceptTcpClient()再没有收到客户端的连接之前, 是不会继续执行的,它的大部分时间都在等待。另外,服务端几乎总是要保持在运行状态,所以这样做并无不可,还可以省去“按Q退出”那段代码。此时再运行代 码,会看到服务端可以收到3个客户端的连接了。