winforms TCP服务器/客户端,在客户端尝试连接之前,服务器是否接受是否重要?

c2e8gylq  于 2022-11-17  发布在  其他
关注(0)|答案(1)|浏览(264)

我正在建立一个TCP服务器/客户端通信,由于某种原因,只有当服务器接受,然后客户端尝试连接时,它才能正常工作。如果我在服务器接受之前启动客户端,它不会做任何事情。
服务器不是异步的(clientHandle = serverHandle.Accept()),也许这就是问题所在,客户端是(s_ClientHandle.BeginnConnect(ipEndpoint, new AsyncCallback(ConnectCallback), s_ClientHandle))。服务器没有计划取回的数据,它只是打算将数据发送到客户端。
服务器:我有一个Windows窗体,使用一个单例类调用StartServer(),这里的服务器类是一个简化的版本:

我知道这是一个很大的代码,客户端不包括在内。服务器代码只包括在内,因为它可能更好地理解我的问题

如果我使用一个异步服务器(写在下面),它连接得很好,就像它应该的那样,但是它从来没有发送过数据,因为服务器总是在监听(StartServer()中的while(true))。有没有办法添加一个安全线程?或者其他什么?这样它就可以在不断监听的同时发送数据?或者AcceptBeginAccept之间有什么我不知道的规则?

namespace Server
{
    public partial class NewForm : Form
    {
        string version = "null";

        public NewForm()
        {
            InitializeComponent();
        }

        private void btnTState_Click(object sender, EventArgs e)
        {
            Singleton Single = Singleton.GetInstance();
            Single.SendMsg(txtMsg.text);
        }
    }
}

单例类:

namespace Server{
    public sealed class Singleton
    {

        private static Singleton instance = null;

        public static Singleton GetInstance()            
        {
            if (instance == null)                        
            {
                instance = new Singleton();
            }
            return instance;
        }

        private Singleton()                               
        {
            Server.StartServer();
        }

        public void SendState(string msg)
        {
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            byte[] data = new byte[4];
            Array.Copy(enc.GetBytes(msg), 0, data, 0, 4);

            Server.SendToClients(data);

        }
   }
}

伺服器

namespace Server
{
    internal class Server
    {
        private static Socket serverHandle;

        private static Socket clientHandle = null;

        private static ArrayList clientList = new ArrayList();


        public static void StartServer()
        {
            IPEndPoint ipep = null;
            ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 59999);  

            // Create a TCP/IP socket.
            serverHandle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            serverHandle.Bind(ipep);
            serverHandle.Listen(20);

            clientHandle = serverHandle.Accept();
            clientList.Add(clientHandle);

        }

        public static void SendToClients(byte[] data)
        {
            ArrayList clients = GetClients();
            foreach (Socket client in clients)
            {
                client.Send(data, 0, data.Length, 0);

            }

        }

        private static ArrayList GetClients()
        {
            ArrayList returnValue = new ArrayList();

            lock (clientList)
            {
                foreach (Socket client in clientList)
                {
                    returnValue.Add(client);
                }
            }
            return returnValue;
        }
    }
}

异步:

public static void StartServer()
        {
            IPEndPoint ipep = null;
            try
            {
                ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 59999);

                serverHandle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                serverHandle.Bind(ipep);
                serverHandle.Listen(61);

                while (true)
                {
                    allEventsDone.Reset();
                    serverHandle.BeginAccept(new AsyncCallback(AcceptCallback), serverHandle);

                    allEventsDone.WaitOne();
                }

            }
            catch (SocketException e)
            {
                
            }
            catch (Exception e)
            {            }
        }

private static void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                Socket server = (Socket)ar.AsyncState;
                Socket clientHandle = server.EndAccept(ar);

                allEventsDone.Set();

                Read(clientHandle);     // reads data, calls readcallback etc
                lock (clientList)
                {
                    clientList.Add(client);
            }
            catch (Exception e)
            {
            }
        }

客户端是异步的(但它确实工作)

1aaf6o9v

1aaf6o9v1#

仅当服务器正在侦听时,连接才起作用。
如果要处理多个连接,使用async和Tasks会更容易,而且无论如何都应该将代码转换为这种更现代的样式。

  • 确保传递从CancellationTokenSource创建的CancellationToken,然后可以通过取消它来关闭服务器。
  • 不要使用原始的Socket,只使用TcpListener
  • 不要使用127.0.0.1,只使用IPAddress.Any
  • ArrayList已过时,您应该改用List<T>。在这种情况下,您需要使用ConcurrentBag<T>而不是手动锁定。
internal class VTrainSServer
{
    private static ConcurrentBag<TcpClient> clientList = new List<TcpClient>();

    public static async Task StartServer(CancellationToken cancel)
    {
        var listener = new TcpListener(IPAddress.Any, 59999));
        try
        {
            listener.Start(20);
            while(!cancel.IsCancellationRequested)
            {
                var client = await listener.AcceptAsync(cancel);
                clientList.Add(client);
            }
        }
        catch (OperationCanceledException)
        { //
        }
        finally
        {
            foreach (var client in clientList)
                client.Dispose();

            if (listener.Active)
                listener.Stop();
        }
    }

    public static async Task SendToClients(byte[] data, CancellationToken cancel)
    {
        foreach (var client in clients.ToArray())
        {
            cancel.ThrowIfCancellationRequested();
            var stream = client.GetStream();  // do not dispose or it will shut down the socket
            await stream.WriteAsync(data, 0, data.Length, cancel);
        }
    }

    private static List<TcpClient> GetClients()
    {
        return clientList.ToArray();
    }
}

您可以使用CancellationToken来调用此函数,如下所示

static CancellationTokenSource _cancel;

public async void btnStartServer_Click(object sender, EventArgs e)
{
    _cancel = new CancellationTokenSource();
    await VTrainSServer.StartServer(_cancel.Token);
}

public async void btnTState_Click(object sender, EventArgs e)
{
    await VTrainSServer.SendToClients(Encoding.UTF8.GetBytes(txtMsg.text), _cancel.Token);
}

public void btnStopServer_Click(object sender, EventArgs e)
{
    _cancel.Cancel()
}

相关问题