简单的一个线程套接字 - TCP服务器

时间:2014-08-11 17:10:22

标签: c# tcp tcpserver

首先,我不知道Stackoverflow是否是发布此类邮件的最佳网站,但我不知道其他网站是这样的。

为了更好地理解C#中的tcp编程,我决定从头开始做所有可能的方法。这是我想知道的(不是正确的顺序: - 简单的一个线程套接字服务器(本文) - 简单的多线程套接字服务器(我不知道如何,导致线程复杂) - 简单的线程套接字服务器(将客户端管理放在另一个线程中) - 多线程套接字服务器 - 使用tcpListener - 使用async / Await - 使用任务 最终的目标是知道如何做最好的tcp服务器,而不只是复制/粘贴来的一些部分,但要正确理解所有的东西。

所以,这是我的第一部分:单线程tcp服务器。

有我的代码,但我认为没有人会纠正某些内容,因为它只是来自MSDN的副本:http://msdn.microsoft.com/en-us/library/6y0e13d3(v=vs.110).aspx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace SimpleOneThreadSocket
{
    public class ServerSocket
    {
        private int _iPport = -1;
        private static int BUFFER_SIZE = 1024;
        private Socket _listener = null;

        public ServerSocket(int iPort)
        {            
            // Create a TCP/IP socket.
            this._listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // Save the port
            this._iPport = iPort;
        }

        public void Start()
        {
            byte[] buffer = null;
            String sDatasReceived = null;

            // Bind the socket to loopback address
            try
            {
                this._listener.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, _iPport));
                this._listener.Listen(2);
            }
            catch (Exception e)
            {
                System.Console.WriteLine(e.ToString());
            }

            // Listening
            try
            {
                Console.WriteLine("Server listening on 127.0.0.1:" + _iPport);

                while (true)
                {
                    Socket client = this._listener.Accept();
                    Console.WriteLine("Incoming connection from : " + IPAddress.Parse(((IPEndPoint)client.RemoteEndPoint).Address.ToString()) + ":" + ((IPEndPoint)client.RemoteEndPoint).Port.ToString());

                    // An incoming connection needs to be processed.
                    while (true)
                    {
                        buffer = new byte[BUFFER_SIZE];
                        int bytesRec = client.Receive(buffer);
                        sDatasReceived += Encoding.ASCII.GetString(buffer, 0, bytesRec);
                        if (sDatasReceived.IndexOf("<EOF>") > -1)
                        {
                            // Show the data on the console.
                            Console.WriteLine("Text received : {0}", sDatasReceived);

                            // Echo the data back to the client.
                            byte[] msg = Encoding.ASCII.GetBytes(sDatasReceived);

                            client.Send(msg);

                            sDatasReceived = "";
                            buffer = null;
                        }
                        else if (sDatasReceived.IndexOf("exit") > -1)
                        {
                            client.Shutdown(SocketShutdown.Both);
                            client.Close();

                            sDatasReceived = "";
                            buffer = null;

                            break;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}

但我对此有一些疑问:

  • 来自Socket的Listen方法有一个参数:backlog。根据MSDN,积压是可用连接的数量。我不知道为什么,当我把0,我可以通过多个Telnet会话连接到我的服务器。编辑:0&amp; 1都允许2个连接(1个电流,1个待处理),2个允许3个连接(1个电流,2个待处理等)......所以我不太了解MSDN的含义。

  • 您能否确认Accept Method将逐个接受每个连接,这就是为什么我在服务器中看到来自不同Telnet会话的文本?

  • 你能确认一下(我的服务器是一个C#库)我不能杀死我的服务器(使用这种代码)而不会杀死进程吗?它可以用线程,但稍后会出现。

如果我的代码出现问题,请帮助我:)

我将很快回来使用一个简单的多线程套接字服务器,但我不知道如何(我认为在使用线程或async / await之前一步可用)。

1 个答案:

答案 0 :(得分:7)

首先,尽量不要学习这一点。如果您可以使用SignalR服务器,那么请执行此操作。 没有这样的东西就像&#34;简单&#34; TCP / IP级别的套接字服务器。

如果你坚持痛苦的路线(即学习正确的TCP / IP服务器设计),那么还有很多东西值得学习。首先,MSDN示例是众所周知的糟糕起点;它们几乎不能工作,往往不能处理任何类型的错误条件,这在现实世界中在TCP / IP级别工作时是绝对必要的。将它们视为如何调用方法的示例,套接字客户端或服务器的示例。

我有TCP/IP FAQ可以帮助您,包括对backlog parameter的说明。这是操作系统在您的代码接受它们之前代表您接受的连接数,并且它只是一个提示。

回答您的其他问题:对Accept的单次呼叫将接受一个新的套接字连接。写入的代码具有无限循环,因此它将像任何其他无限循环一样工作;它将继续执行,直到遇到异常或其线程中止(在进程关闭时发生)。

  

如果我的代码出现问题,请帮帮我

哦,是的。这段代码有很多问题。毕竟,这是一个MSDN套接字示例。 :)脱离我的头顶:

  1. 缓冲区大小是一个任意值,相当低。我自己会从8K开始,因此可以在一次读取中获得完整的以太网数据包。
  2. Bind显式使用环回地址。我想,可以玩,但请记住在现实世界中将其设置为IPAddress.Any
  3. backlog参数可以进行测试,但在真实服务器上应为int.MaxValue以启用现代服务器操作系统中的动态积压。
  4. 代码将落在第一个catch之后,并在Accept / Bind失败后尝试Listen
  5. 如果发生任何异常(例如,来自ListenReceive),则整个服务器将关闭。请注意,被终止的客户端套接字将导致被记录/忽略的异常,但它会停止此服务器。
  6. 每次循环都会重新分配读缓冲区,即使旧的缓冲区从未再次使用过。
  7. ASCII是一种有损编码。
  8. 如果客户端在没有发送<EOF>的情况下干净地关闭,那么服务器就会进入无限繁忙的循环。
  9. 收到的数据未正确分为信息;回显的消息可能包含所有一条消息和另一条消息的一部分。在这个特定的例子中它并不重要(因为它只是一个echo服务器而且它使用的是ASCII而不是真正的编码),但这个例子隐藏了你需要处理消息帧的事实适用于任何现实世界的应用程序。
  10. 解码应在消息成帧后完成。这对于ASCII(有损编码)来说并不是必需的,但是对于像UTF8这样的任何真实编码都是必需的。
  11. 由于服务器只是在任何时间接收或发送(而不是两者),因此它无法检测或从半开插座情况中恢复。半开的套接字会导致此服务器挂起。
  12. 服务器一次只能连接一个。
  13. 这是在简短的阅读之后。可能会有更多。