zoukankan      html  css  js  c++  java
  • .NET Socket开发之同步Socket实现两例

    来源:中国IT实验室
      今天,我们来讲一下在。NET 网络应用程序开发中同步Socket的应用,很多人认为在网络应用的服务端Socket不应该使用同步Socket.是的,在大多数情况下是这样的,但是也有一些场景下我们使用同步Socket可能会得到更的结果。如在下面的两种场景下我们便可以考虑使用同步的Socket.

      一、客户端数量比较少:

      数量比较少是指会同时连接到服务器的客户端数量一般在50人以下。这种情况下我们可以考虑使用同步Socket+Thread来实现我们的服务端。这样会让我们编写逻辑更清晰的代码而性能不会下降太多。

      二、客户端数量较多但都是短连接:

      短连接是指客户端的连接在处理完一次收发之后就产即断开的场景,比如说HTTP协议就是一种短连接。HTTP在客户端发出请求时建立一个Socket连接,并通过Socket发出一个URL请求,服务端在处理完这个请求并回发相应的页面后便会断开这个连接。那么在这种场景下我们也可以使用同步Socket来实现我们的需求。

      那么应该如果实现我上面提到的两种需求呢。对于这两种需求,我将采用不同的方案来实现它们。

      首先我们来看看第一种需求,这里我采用Socket+Thread来实现,基本的流程如下:

      首先创建一个Socket,并且给它绑定一个EndPoint后开始监听。接下来我们创建一个线程,在这个线程中我们用一个无限循环来接收来自客户端的连接请求。在接收到一个请求后,为这个客户端创建一个新的线程,并且在这个线程中也使用一个无限循环接收来自这个客户端的数据。下面让我们来看看代码:
      首先我们创建一个Socket用来侦听客户端的连接:
    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    IPEndPoint locEP= new IPEndPoint(IPAddress.Any, 2000);
    listener.Bind(locEP);
    listener.Listen(100);

      然后创建一个线程来处理客户端的连接请求:
    Thread acceptThread = new Thread(new ThreadStart(AcceptWorkThread));
    acceptThread.Start();
    private void AcceptWorkThread()
    ...{
    Thread.CurrentThread.IsBackground = true;
    while (true)
    ...{
    Socket accept = listener.Accept();
    IPEndPoint remoEP = (IPEndPoint)accept.RemoteEndPoint;
    string recString = "接收到来自" + remoEP.Address.ToString() + "的连接。";
    this.Invoke(new AddListItemHandler(this.AddListItem), new string[] ...{ recString });
    Thread receiveThread = new Thread(new ParameterizedThreadStart(ReceiveWorkThread));
    receiveThread.Start(accept);
    }
    }
      最后我们来看看如何接收数据:
    private void ReceiveWorkThread(object obj)
    {
    Thread.CurrentThread.IsBackground = true;
    Socket socket = (Socket)obj;
    byte[] buffer = new byte[1024];
    while (true)
    {
    int receiveCount = socket.Receive(buffer);
    if (receiveCount > 0)
    {
    IPEndPoint remoEP = (IPEndPoint)socket.RemoteEndPoint;
    string recString = "来自客户端" + remoEP.Address.ToString() + "的消息:" + Encoding.Default.GetString(buffer, 0, receiveCount);
    this.Invoke(new AddListItemHandler(this.AddListItem), new string[] ...{ recString });
    socket.Send(buffer, receiveCount, SocketFlags.None);
    }
    else
    {
    socket.Close();
    break;
    }
    }
    }
      好了,整个实现就完成了。
      现在让我们来看看第二个需求:
      这个方案我们将采用另外一个方法来实现,为什么不采用上一个方法来实现呢?让我们来分析一下。我们知道,在上一个实现中,每接入一个客户端就要创建一个线程,如果有大量的客户端接入的话,就会创建过多的线程。但是如果线程过多的话,Windows就需要更多的CPU时间来切换线程的上下文(这也是上一个实现不能接入很多客户端的原因)。

      我们知道,在这个方案中每一个连接都是短连接。而且顺序都是固定的。都是:接入->接收->发送这样的顺序,那么我们就可以在一个方法中完成整个处理。这样,我们就可以利用线程池来实现我们所需要的。好了,让我们用代码来说话吧:

      首先我们创建一个Socket用来侦听客户端的连接:
    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    IPEndPoint locEP= new IPEndPoint(IPAddress.Any, 2000);
    listener.Bind(locEP);
    listener.Listen(100);

      接下来我们要创建一个线程池:

    Thread[] ClientThreadList = new Thread[30];
    foreach (Thread th in ClientThreadList)
    ...{
    th = new Thread(new ThreadStart(ClientWorkThread));
    th.Start();
    }
      最后让我们看看线程都要做些什么:
    private void ClientWorkThread()
    ...{
    byte[] buffer = new byte[1024];
    while (true)
    {
    Socket socket = listener.Accept();
    string recString = "接收到来自" + remoEP.Address.ToString() + "的连接。";
    this.Invoke(new AddListItemHandler(this.AddListItem), new string[] ...{ recString });
    int receCount = socket.Receive(buffer);
    if (receCount>0)
    {
    string recString = "来自客户端" + remoEP.Address.ToString() + "的消息:" + Encoding.Default.GetString(buffer, 0, receiveCount);
    this.Invoke(new AddListItemHandler(this.AddListItem), new string[] ...{ recString });
    socket.Send(buffer, receCount, SocketFlags.None);
    }
    socket.Shutdown(SocketShutdown.Both);
    socket.Close();
    }
    }
      为什么我们要这样做呢?

      首先我们创建了一个Socket用于侦听客户端的连接请求,接下我们创建了一个拥有30个线程的线程池。并在每个线程中实现了Accept、Receive、Send和Close(),以完成连接、接收、发送、关闭的操作。

      现在我们假设有一个客户连接到服务器了,这时会有一个线程Accept到这个请求,并开始接收客户端发送过来的数据,接收到数据之后处理完发送给客户端,然后关闭这个连接,再次进入等待连接状态。而其它29个线程由于没有Accept到这个请求,仍然处理等待接入状态。
  • 相关阅读:
    我爱java系列之---【微服务间的认证—Feign拦截器】
    我爱java系列之---【设置权限的三种解决方案】
    581. Shortest Unsorted Continuous Subarray
    129. Sum Root to Leaf Numbers
    513. Find Bottom Left Tree Value
    515. Find Largest Value in Each Tree Row
    155. Min Stack max stack Maxpop O(1) 操作
    painting house
    Minimum Adjustment Cost
    k Sum
  • 原文地址:https://www.cnblogs.com/godwar/p/942043.html
Copyright © 2011-2022 走看看