如何解决C#异步套接字编程Client/Server问题?

How to solve C# Asynchronous Socket Programming Client/Server problem?

我找到了一篇关于此程序的文章。 (https://www.gokhan-gokalp.com/en/c-ile-asenkron-socket-programlama/)

但我有问题,它没有将信息服务器发送到客户端。 我必须在客户端编写接收方法,我必须在服务器端正确发送方法。他们失踪了。 我该如何实施?

客户端:主程序

using ExampleClient.Sockets;
using ExampleDataTransferObjects;
using System;
using System.Net;
using System.Linq;

namespace ExampleClient
{
    class Program
    {
        static void Main(string[] args)
        {
            int port = 5555;
            Console.WriteLine(string.Format("Client Başlatıldı. Port: {0}", port));
            Console.WriteLine("-----------------------------");

            ExampleSocket exampleSocket = new ExampleSocket(new IPEndPoint(IPAddress.Parse("127.0.0.1"), port));
            exampleSocket.Start();

            Console.WriteLine("Göndermek için \"G\", basınız...");

            int count = 1;
            while (Console.ReadLine().ToUpper() == "G")
            {
                ExampleDTO exampleDTO = new ExampleDTO()
                {
                    Status = string.Format("{0}. Alındı", count),
                    Message = string.Format("{0} ip numaralı client üzerinden geliyorum!", GetLocalIPAddress())
                };

                exampleSocket.SendData(exampleDTO);
                count++;
            }

            Console.ReadLine();
        }

        static string GetLocalIPAddress()
        {
            string localIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList.Where(a => a.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).FirstOrDefault().ToString();

            return localIP;
        }
    }
}

Client.cs: 此客户端将向服务器传递数据。

using ExampleDataTransferObjects;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;

namespace ExampleClient.Sockets
{
    public class ExampleSocket
    {
        #region Variables
        Socket _Socket;
        IPEndPoint _IPEndPoint;

        // Socket işlemleri sırasında oluşabilecek errorları bu enum ile handle edebiliriz.
        SocketError socketError;
        byte[] tempBuffer = new byte[1024];
        #endregion

        #region Constructor
        public ExampleSocket(IPEndPoint ipEndPoint)
        {
            _IPEndPoint = ipEndPoint;

            // Socket'i tanımlıyoruz IPv4, socket tipimiz stream olacak ve TCP Protokolü ile haberleşeceğiz. 
            // TCP Protokolünde server belirlenen portu dinler ve gelen istekleri karşılar oysaki UDP Protokolünde tek bir socket üzerinden birden çok client'a ulaşmak mümkündür.
            _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        #endregion

        #region Public Methods
        public void Start()
        {
            // BeginConnect ile asenkron olarak bir bağlantı başlatıyoruz.
            _Socket.BeginConnect(_IPEndPoint, OnBeginConnect, null);
        }

        public void SendData(ExampleDTO exampleDTO)
        {
            using (var ms = new MemoryStream())
            {
                // İlgili object'imizi binary'e serialize ediyoruz.
                new BinaryFormatter().Serialize(ms, exampleDTO);
                IList<ArraySegment<byte>> data = new List<ArraySegment<byte>>();

                data.Add(new ArraySegment<byte>(ms.ToArray()));

                // Gönderme işlemine başlıyoruz.
                _Socket.BeginSend(data, SocketFlags.None, out socketError, (asyncResult) =>
                {
                    // Gönderme işlemini bitiriyoruz.
                    int length = _Socket.EndSend(asyncResult, out socketError);

                    if (length <= 0 || socketError != SocketError.Success)
                    {
                        Console.WriteLine("Server bağlantısı koptu!");
                        return;
                    }
                }, null);

                if (socketError != SocketError.Success)
                    Console.WriteLine("Server bağlantısı koptu!");
            }
        }
        #endregion

        #region Private Methods
        void OnBeginConnect(IAsyncResult asyncResult)
        {
            try
            {
                // Bağlanma işlemini bitiriyoruz.
                _Socket.EndConnect(asyncResult);

                // Bağlandığımız socket üzerinden datayı dinlemeye başlıyoruz.
                _Socket.BeginReceive(tempBuffer, 0, tempBuffer.Length, SocketFlags.None, OnBeginReceive, null);
            }
            catch (SocketException)
            {
                // Servera bağlanamama durumlarında bize SocketException fırlatıcaktır. Hataları burada handle edebilirsiniz.
                Console.WriteLine("Servera bağlanılamıyor!");
            }
        }

        void OnBeginReceive(IAsyncResult asyncResult)
        {
            // Almayı bitiriyoruz ve geriye gelen byte array'in boyutunu vermektedir.
            int receivedDataLength = _Socket.EndReceive(asyncResult, out socketError);

            if (receivedDataLength <= 0 || socketError != SocketError.Success)
            {
                // Gelen byte array verisi boş ise bağlantı kopmuş demektir. Burayı istediğiniz gibi handle edebilirsiniz.
                Console.WriteLine("Server bağlantısı koptu!");
                return;
            }

            // Tekrardan socket üzerinden datayı dinlemeye başlıyoruz.
            _Socket.BeginReceive(tempBuffer, 0, tempBuffer.Length, SocketFlags.None, OnBeginReceive, null);
        }
        #endregion
    }
}

ExampleDataTransferObjects.dll: 这个dll将数据客户端传输到服务器

using System;

namespace ExampleDataTransferObjects
{
    /// <summary>
    /// Serialize edebilmek için Serializable attributü ile işaretliyoruz.
    /// </summary>
    [Serializable]
    public class ExampleDTO
    {
        public string Status { get; set; }
        public string Message { get; set; }
    }
}

服务器端:

Client.cs: 它将接收数据并发送给监听器

using System;
using System.IO;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;

namespace ExampleServer.Sockets
{
    public delegate void OnExampleDTOReceived(ExampleDTO eDTO);

    public class Client
    {
        #region Variables
        public OnExampleDTOReceived _OnExampleDTOReceived;
        Socket _Socket;

        // Socket işlemleri sırasında oluşabilecek errorları bu enum ile handle edebiliriz.
        SocketError socketError;
        byte[] tempBuffer = new byte[1024]; // 1024 boyutunda temp bir buffer, gelen verinin boyutu kadarıyla bunu receive kısmında handle edeceğiz.
        #endregion

        #region Constructor
        public Client(Socket socket)
        {
            _Socket = socket;
        }
        #endregion

        #region Public Methods
        public void Start()
        {
            // Socket üzerinden data dinlemeye başlıyoruz.
            _Socket.BeginReceive(tempBuffer, 0, tempBuffer.Length, SocketFlags.None, OnBeginReceiveCallback, null);
        }
        #endregion

        #region Private Methods
        void OnBeginReceiveCallback(IAsyncResult asyncResult)
        {
            // Almayı bitiriyoruz ve gelen byte array'in boyutunu vermektedir.
            int receivedDataLength = _Socket.EndReceive(asyncResult, out socketError);

            if (receivedDataLength <= 0 && socketError != SocketError.Success)
            {
                // Gelen byte array verisi boş ise bağlantı kopmuş demektir. Burayı istediğiniz gibi handle edebilirsiniz.
                return;
            }

            // Gelen byte array boyutunda yeni bir byte array oluşturuyoruz.
            byte[] resizedBuffer = new byte[receivedDataLength];

            Array.Copy(tempBuffer, 0, resizedBuffer, 0, resizedBuffer.Length);

            // Gelen datayı burada ele alacağız.
            HandleReceivedData(resizedBuffer);

            // Tekrardan socket üzerinden data dinlemeye başlıyoruz.
            // Start();

            // Socket üzerinden data dinlemeye başlıyoruz.
            _Socket.BeginReceive(tempBuffer, 0, tempBuffer.Length, SocketFlags.None, OnBeginReceiveCallback, null);
        }

        /// <summary>
        /// Gelen datayı handle edeceğimiz nokta.
        /// </summary>
        /// <param name="resizedBuffer"></param>
        void HandleReceivedData(byte[] resizedBuffer)
        {
            if (_OnExampleDTOReceived != null)
            {
                using (var ms = new MemoryStream(resizedBuffer))
                {
                    // BinaryFormatter aracılığı ile object tipimize geri deserialize işlemi gerçekleştiriyoruz ve ilgili delegate'e parametre olarak geçiyoruz.
                    ExampleDTO exampleDTO = new BinaryFormatter().Deserialize(ms) as ExampleDTO;

                    _OnExampleDTOReceived(exampleDTO);
                }
            }
        }
        #endregion
    }
}

监听器:异步监听数据

using ExampleDataTransferObjects;
using System;
using System.Net;
using System.Net.Sockets;

namespace ExampleServer.Sockets
{
    public class Listener
    {
        #region Variables
        Socket _Socket;
        int _Port;
        int _MaxConnectionQueue;
        #endregion

        #region Constructor
        public Listener(int port, int maxConnectionQueue)
        {
            _Port = port;
            _MaxConnectionQueue = maxConnectionQueue;

            // Socket'i tanımlıyoruz IPv4, socket tipimiz stream olacak ve TCP Protokolü ile haberleşeceğiz. 
            // TCP Protokolünde server belirlenen portu dinler ve gelen istekleri karşılar oysaki UDP Protokolünde tek bir socket üzerinden birden çok client'a ulaşmak mümkündür.
            _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        #endregion

        #region Public Methods
        public void Start()
        {
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, _Port);

            // Socket'e herhangi bir yerden ve belirttiğimiz porttan gelecek olan bağlantıları belirtmeliyiz.
            _Socket.Bind(ipEndPoint);

            // Socketten gelecek olan bağlantıları dinlemeye başlıyoruz ve maksimum dinleyeceği bağlantıyı belirtiyoruz.
            _Socket.Listen(_MaxConnectionQueue);

            // BeginAccept ile asenkron olarak gelen bağlantıları kabul ediyoruz.
            _Socket.BeginAccept(OnBeginAccept, _Socket);
        }
        #endregion

        #region Private Methods
        void OnBeginAccept(IAsyncResult asyncResult)
        {
            Socket socket = _Socket.EndAccept(asyncResult);
            Client client = new Client(socket);

            // Client tarafından gönderilen datamızı işleyeceğimiz kısım.
            client._OnExampleDTOReceived += new Sockets.OnExampleDTOReceived(OnExampleDTOReceived);
            client.Start();

            // Tekrardan dinlemeye devam diyoruz.
            _Socket.BeginAccept(OnBeginAccept, null);
        }

        void OnExampleDTOReceived(ExampleDTO exampleDTO)
        {
            // Client tarafından gelen data, istediğiniz gibi burada handle edebilirsiniz senaryonuza göre.
            Console.WriteLine(string.Format("Status: {0}", exampleDTO.Status));
            Console.WriteLine(string.Format("Message: {0}", exampleDTO.Message));
        }
        #endregion
    }
}

服务器主程序:

using ExampleServer.Sockets;
using System;

namespace ExampleServer
{
    class Program
    {
        static void Main(string[] args)
        {
            int port = 5555;
            Console.WriteLine(string.Format("Server Başlatıldı. Port: {0}", port));
            Console.WriteLine("-----------------------------");

            Listener listener = new Listener(port, 50);

            listener.Start();

            Console.ReadLine();
        }
    }
}

我建议您使用 signalR 在客户端和服务器之间进行全双工通信。 https://docs.microsoft.com/tr-tr/aspnet/signalr/overview/getting-started/introduction-to-signalr

另外,我修改了你的ServerToClient通信代码,你可以在下面找到。

在ExampleSocket.cs中定义两个新变量

    #region Variables
    Socket _Socket;
    IPEndPoint _IPEndPoint;
    //new
    SocketError socketError;
    byte[] tempBuffer = new byte[1024];
    //new
    #endregion

在 ExampleSocket.cs 中,将下面一行添加到您的 Start() 函数中

  public void Start()
    {
        // BeginConnect ile asenkron olarak bir bağlantı başlatıyoruz.
        _Socket.BeginConnect(_IPEndPoint, OnBeginConnect, null);
        //new 
        _Socket.BeginReceive(tempBuffer, 0, tempBuffer.Length, SocketFlags.None, OnBeginReceiveCallback, null);
        //new
    }

在ExampleSocket.cs中添加如下新函数

    //new
    void OnBeginReceiveCallback(IAsyncResult asyncResult)
    {
        int receivedDataLength = _Socket.EndReceive(asyncResult, out socketError);

        if (receivedDataLength <= 0 && socketError != SocketError.Success)
            return;


        byte[] resizedBuffer = new byte[receivedDataLength];

        Array.Copy(tempBuffer, 0, resizedBuffer, 0, resizedBuffer.Length);

        using (var ms = new MemoryStream(resizedBuffer))
        {
            ms.Position = 0;
            string myStr = new StreamReader(ms).ReadToEnd();
            Console.WriteLine(string.Format("From server : {0}", myStr));
        }
        _Socket.BeginReceive(tempBuffer, 0, tempBuffer.Length, SocketFlags.None, OnBeginReceiveCallback, null);
    }
    //new

在Listener.cs中添加如下新函数

   //new
    public void Send(string s)
    {
        client.Send(s);
    }
    //new 

在Client.cs中添加如下新函数

     //new
    public void Send(string s)
    {

        if (_Socket.Connected)
        {
            Byte[] byteData = Encoding.ASCII.GetBytes(s.ToCharArray());
            _Socket.Send(byteData, byteData.Length, 0);
        }

    }
    //new

在 ExampleServer.Program 中,修改您的 Main(string[] args) 函数

static void Main(string[] args)
    {
        int port = 5555;
        Console.WriteLine(string.Format("Server Başlatıldı. Port: {0}", port));
        Console.WriteLine("-----------------------------");

        Listener listener = new Listener(port, 50);

        listener.Start();

        //new
        bool dewamke = true;

        while (dewamke)
        {
            Console.WriteLine("Write exit to close or write anything for sending data.");
            var cmd = Console.ReadLine();
            if (cmd == "exit")
                dewamke = false;
            else
                listener.Send(cmd);
        }

        Console.WriteLine("closing server.");
        Console.ReadLine();
        //new
    }

我用这段代码解决了 client.cs 中的问题:

 void HandleReceivedData(byte[] resizedBuffer)
        {
            if (_OnExampleDTOReceived != null)
            {
                //using (var ms = new MemoryStream(resizedBuffer))
                //{
                //    // BinaryFormatter aracılığı ile object tipimize geri deserialize işlemi gerçekleştiriyoruz ve ilgili delegate'e parametre olarak geçiyoruz.
                //    string exampleDTO = new BinaryFormatter().Deserialize(ms) as string;

                //    if (exampleDTO == "POSALIVE")
                //        SendData("ACK");

                //    _OnExampleDTOReceived(exampleDTO);
                //}

                using (MemoryStream stream = new MemoryStream(resizedBuffer))
                {
                    stream.Position = 0;
                    var sr = new StreamReader(stream);
                    string myStr = sr.ReadToEnd();

                    if (myStr.Contains("POSALIVE"))
                        SendData("ACK");
                    _OnExampleDTOReceived(myStr);
                }
            }
        }

        public void SendData(string message)
        {
            using (var ms = new MemoryStream())
            {
                // İlgili object'imizi binary'e serialize ediyoruz.
                new BinaryFormatter().Serialize(ms, message);
                IList<ArraySegment<byte>> data = new List<ArraySegment<byte>>();

                data.Add(new ArraySegment<byte>(ms.ToArray()));

                // Gönderme işlemine başlıyoruz.
                _Socket.BeginSend(data, SocketFlags.None, out socketError, (asyncResult) =>
                {
                    // Gönderme işlemini bitiriyoruz.
                    int length = _Socket.EndSend(asyncResult, out socketError);

                    if (length <= 0 || socketError != SocketError.Success)
                    {
                        log.append("Server bağlantısı koptu!", "");
                        return;
                    }
                }, null);

                if (socketError != SocketError.Success)
                    log.append("Server bağlantısı koptu!", "");
            }
        }

我在client.cs

中添加了发送数据方法