簡體   English   中英

C# .NET 套接字 tcp begin/endreceive 客戶端:endreceive 讀取所有字節,但在 beginreceive 中,緩沖區大小自行更改

[英]C# .NET socket tcp begin/endreceive client : endreceive reads all bytes, but in beginreceive, the buffer size changes on its own

所以我有一個使用 Socket 類(在 tcp 上)的服務器和客戶端。

我一直在為這個問題苦苦掙扎了幾個星期,我搜索了很多,但我不明白這個問題是怎么可能的。 我一直在觀察調試器,觀察變量的值,但看不出問題出在哪里。

例如,客戶端發送 100 個字節,服務器接收它們,處理它們並返回 150 個字節。 但是,在客戶端,每次客戶端收到消息時緩沖區都會改變大小(我不知道為什么,可能是因為 beginreceive)。

因此,當服務器發送這 150 個字節時,客戶端開始接收,但我看到緩沖區大小為 100(與客戶端發送的前一條消息的大小相同)。

客戶端執行“接收”,並讀取 150 個字節(服務器發送的正確字節數)。

當我想獲取字符串時,我使用編碼函數、包含消息的緩沖區以及接收到的字節數。

所以,我最終有一個大小為 100 字節(錯誤大小)的緩沖區,讀取的字節數為 150 字節(正確數量)。

我已經多次修改我的代碼,試圖定義“新字節[bufferSize]”,但問題保持不變。

有時,客戶端甚至會收到服務器從未發送過的消息! 這很奇怪。 客戶端有時似乎從“服務器緩沖區”接收字節(我還沒有找到如何檢查它)

       public void InitializeReceive(Socket soc)
        {
             //bufferSize is fixed to 256
            MyBuffer = new byte[bufferSize];

            soc.BeginReceive(MyBuffer , 0, MyBuffer .Length,
                SocketFlags.None, new AsyncCallback(Receiving), soc);

        }
        public void Receiving(IAsyncResult iar)
        {

            Socket tmp = (Socket)iar.AsyncState;

            int received= tmp.EndReceive(iar);

            if (received> 0)
            {
                 //problem is here. MyBuffer has a size of 100 bytes, received is 150 bytes
                string msg = Encoding.Unicode.GetString(MyBuffer, 0, received);



                //next message
                InitializeReceive(tmp);
            }

        }

我試過檢查堆堆棧,類似這樣的東西,但我真的不知道如何診斷它。

客戶端和服務器之間的第一條消息沒問題。 但是幾次交換后就變成這樣了,我修復的緩沖區根本就沒有滿。 第一條消息的大小約為 36 字節,超過 100 字節,依此類推。 這里好像不是大小問題

我什至不明白這怎么可能。 希望有大神給個解釋謝謝!

編輯 :

所以,正如所問,我試圖制作一個最小的可重現代碼。

我自己測試過,是的,問題仍然存在。 我在客戶端收到異常“System.ArgumentOutOfRangeException”。

服務器代碼:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
//
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace BugServeurSocket1
{
    public partial class Form1 : Form
    {
        private Socket MonServeur, MonClient;


        int bufferSize = 256;
        private byte[] MonBuffer;

        int portServeur = 8000;

        string tAdversaire = "1";

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            MonBuffer = new byte[bufferSize];

            #region mise en place serveur
  
            IPAddress IPServeur = CorrectAddress(Dns.GetHostName());

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

            MonServeur.Bind(new IPEndPoint(IPServeur, portServeur));

            MonServeur.Listen(10);

            MonServeur.BeginAccept(new AsyncCallback(SurDemandeConnexion), MonServeur);
            #endregion
        }

        //réseau
        #region fct perso
        #region verif adr ipv4
        private IPAddress CorrectAddress(string nPC)
        {
            IPAddress ipReponse = null;

            if (nPC.Length > 0)
            {
                IPAddress[] IPserveur = Dns.GetHostEntry(nPC).AddressList;

                for (int i = 0; i < IPserveur.Length; i++)
                {
                    Ping pingServeur = new Ping();
                    //envoyer ping
                    PingReply pingResponse = pingServeur.Send(IPserveur[i]);

                    if (pingResponse.Status == IPStatus.Success)

                        if (IPserveur[i].AddressFamily == AddressFamily.InterNetwork)
                        {

                            ipReponse = IPserveur[i];
                            break;
                        }
                }//for
            }

            return ipReponse;
        }
        #endregion
        #region reception connexion client
        public void SurDemandeConnexion(IAsyncResult iar)
        {
            Socket tmp = (Socket)iar.AsyncState;

            MonServeur.BeginAccept(new AsyncCallback(SurDemandeConnexion), MonServeur);//accepter nouvelles connexions

            MonClient = tmp.EndAccept(iar);

            //MonBuffer = new byte[256];//test
            MonBuffer = Encoding.Unicode.GetBytes("Connexion acceptée");
            MonClient.Send(MonBuffer, 0, MonBuffer.Length, SocketFlags.None);

            //ver7
            InitializeReceive(MonClient);
        }
        #endregion
        public void InitializeReceive(Socket soc)
        {
            MonBuffer = new byte[256];//empty buffer//test

            soc.BeginReceive(MonBuffer, 0, MonBuffer.Length,
                SocketFlags.None, new AsyncCallback(ReceiveMsg), soc);
        }
        public void ReceiveMsg(IAsyncResult iar)
        {
            Socket tmp = (Socket)iar.AsyncState;

            int received = tmp.EndReceive(iar);

            if (received > 0)
            {
                string msg = Encoding.Unicode.GetString(MonBuffer, 0, received);

                string[] msgSplit = msg.Split(':');

                //pseudo
                string msgKey = msgSplit[0];

                #region recherche adversaire
                if (msgKey == "JEU_CHERCHER_ADVERSAIRE")
                {
                        MonBuffer = Encoding.Unicode.GetBytes("TESTJEU_CHERCHER_ADVERSAIRE:" + tAdversaire + ":"
+ tAdversaire + "=adversaire_RECHERCHE;joueur_RECHERCHANT");

                       MonClient.Send(MonBuffer, 0, MonBuffer.Length, SocketFlags.None);
                }
                #endregion

                //next msg
                InitializeReceive(tmp);
            }
        }
        #endregion
    }
}

客戶端代碼:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO; 
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
//
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace BugClientSocket1
{
    public partial class Form1 : Form
    {
        private Socket MonClient;

        //buffer
        int bufferSize = 256;
        private byte[] MonBuffer;

        int portServeur = 8000;

        string pseudoID = "1:";
        string tAdversaire = "1";

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            tPseudo.Text = pseudoID;

            MonBuffer = new byte[bufferSize];
        }

        private IPAddress CorrectAddress(string nPC)
        {
            IPAddress ipReponse = null;

            try
            {
                if (nPC.Length > 0)
                {
                    IPAddress[] IPserveur = Dns.GetHostEntry(nPC).AddressList; 
                    for (int i = 0; i < IPserveur.Length; i++)
                    {
                        Ping pingServeur = new Ping();
                        PingReply pingResponse = pingServeur.Send(IPserveur[i]);

                        if (pingResponse.Status == IPStatus.Success)
                            if (IPserveur[i].AddressFamily == AddressFamily.InterNetwork)
                            {
                                ipReponse = IPserveur[i];
                                break;
                            }
                    }//for
                }

                return ipReponse;
            }
            catch (SocketException e)
            {
                return null;
            }
        }

        private void BConnecter_Click(object sender, EventArgs e)
        {
            MonClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            MonClient.Blocking = false;


            //tserveur.txt -> replace with your PC name
            IPAddress IPserveur = CorrectAddress(tServeur.Text);

            if (IPserveur != null)
                MonClient.BeginConnect(new IPEndPoint(IPserveur, portServeur), new AsyncCallback(OnConnection), MonClient);

        }
        #region Se connecter au serveur
        public void OnConnection(IAsyncResult iar)
        {
            Socket tmp = (Socket)iar.AsyncState;

            if (tmp.Connected)
            {
                byte[] msg = Encoding.Unicode.GetBytes("PSEUDO:" + tPseudo.Text);

                //send pseudo
                MonClient.Send(msg, 0, msg.Length, SocketFlags.None);

                //next msg
                InitializeReceive(tmp);

            }
            else
            {
                MessageBox.Show("Serveur inaccessible");
            }

        }
        #endregion
        #region Debut reception msg + suite
        //cfr beginaccept-> beginreceive ici
        public void InitializeReceive(Socket soc)
        {
            MonBuffer = new byte[bufferSize];

            soc.BeginReceive(MonBuffer, 0, MonBuffer.Length,
                SocketFlags.None, new AsyncCallback(ReceiveMsg), soc);

        }
        public void ReceiveMsg(IAsyncResult iar)
        {
            Socket tmp = (Socket)iar.AsyncState;

            int received = tmp.EndReceive(iar);

            if (received > 0)
            {
                string msg = Encoding.Unicode.GetString(MonBuffer, 0, received);

                //next msg
                InitializeReceive(tmp);
            }

        }
        #endregion

        private void BChercher_Click(object sender, EventArgs e)
        {
            if (MonClient != null && tPseudoAdversaire.Text != "")
            {
                //MonBuffer = new byte[bufferSize];
                MonBuffer = Encoding.Unicode.GetBytes("JEU_CHERCHER_ADVERSAIRE:" + tAdversaire + ":"
                    + pseudoID + "=adversaire_RECHERCHE;joueur_RECHERCHANT");

                //send pseudo
                MonClient.Send(MonBuffer, 0, MonBuffer.Length, SocketFlags.None);

            }

        }
    }
}


我確實故意讓文本(例如 JEU_CHERCHER ...)得到完全相同的問題。

我的應用程序是Visual Studio 2019 中winform

要重現該錯誤,請執行以下步驟:

  1. 在方法“beginreceive”和“end receive”和變量“received”中放置斷點; 查看字節和字符串的數量。 到達這些斷點時還要檢查 MonBuffer 的大小
  2. 單擊“連接”按鈕連接到服務器
  3. 服務器接收“偽”並接受客戶端
  4. 服務器發送消息說“好的,您已連接”
  5. 單擊“chercher”(搜索)向服務器發送新消息
  6. 服務器收到消息,繼續處理,然后用一條新消息回答它
  7. 客戶端接收消息,這里是錯誤緩沖區大小小於接收字節數
  8. 發生異常

如果您有什么不明白的地方(因為我沒有全部翻譯成英文),請告訴我。 謝謝

總結一下,解決方法是:

  • 盡可能避免將全局變量用作緩沖區。 但是在這里,我需要全局變量作為接收數據的緩沖區
  • 永遠不要使用相同的緩沖區來接收和發送消息(字節)
  • 至少使用局部變量發送,這里我使用全局變量緩沖區來接收

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM