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

Bap*_*ptX 2 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());
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但我对此有一些疑问:

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

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

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

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

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

Ste*_*ary 7

首先,尽量不要学习这一点.如果您可以使用SignalR服务器,那么请执行此操作.有没有这样的事情是在TCP/IP级"简单"套接字服务器.

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

我有一个可能对您有帮助的TCP/IP常见问题,包括backlog参数说明.这是操作系统在您的代码接受它们之前代表您接受的连接数,并且它只是一个提示.

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

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

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

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

那只是在简短的阅读之后.可能会有更多.