簡體   English   中英

在C#中對使用UDP協議的套接字感到困惑

[英]Confused about Sockets with UDP Protocol in C#

我剛開始通過各種谷歌搜索來學習套接字,但是我在解決如何在C#中正確使用套接字時遇到了一些問題,我需要一些幫助。

我有一個測試應用程序(Windows窗體)和一個不同的類(實際上是它自己的.dll,但這是無關緊要的)我有我的套接字代碼的所有服務器/客戶端代碼。

問題1)

在我的測試應用程序中,在服務器部分,用戶可以單擊“開始監聽”按鈕,我的套接字應用程序的服務器部分應該開始監聽指定地址和端口上的連接,到目前為止一切順利。

但是,應用程序將被阻止,除非有人連接到服務器,否則我無法執行任何操作。 如果沒有人連接怎么辦? 我該怎么處理? 我可以指定接收超時但是呢? 拋出異常,我該怎么辦? 我想要的是在主應用程序上進行某種活動,以便用戶知道應用程序沒有凍結並等待連接。 但是如果沒有連接,它應該超時並關閉所有內容。

也許我應該使用異步調用來發送/接收方法,但它們似乎令人困惑,我無法使它工作,只有同步工作(我將在下面發布我當前的代碼)。

問題2)

當某些發送/接收呼叫超時時,是否需要關閉任何內容。 正如你在我當前的代碼中看到的那樣,我在套接字上有一堆關閉,但這種方式感覺不對。 但是當操作超時且我沒有關閉套接字時,它也感覺不對。

在我的兩個問題的結論....我想一個不阻止的應用程序,所以用戶知道服務器正在等待連接(例如有一個小的選框動畫)。 如果在一段時間后從未建立連接,我想關閉應該關閉的所有內容。 建立連接或在一段時間后沒有發生連接時,我想告知結果的主要應用。

這是我的一些代碼,其余的類似。 Packet類是一個自定義類,代表我的自定義數據單元,它只是現在基於enums的一堆屬性,有方法將它們轉換為字節並返回屬性。

開始偵聽連接的函數是這樣的:

public void StartListening(string address, int port) {
    try {
        byte[] bufferBytes = new byte[32];

        if(address.Equals("0.0.0.0")) {
            udpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
        } else {
            udpSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
        }

        remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);

        int numBytesReceived = udpSocket.ReceiveFrom(bufferBytes, ref remoteEndPoint);

        if(numBytesReceived == 0) {
            udpSocket.Close();
            return;
        }

        Packet syncPacket = new Packet(bufferBytes);

        if(syncPacket.PacketType != PacketType.Control) {
            udpSocket.Close();
            return;
        }
    } catch {
        if(udpSocket != null) {
            udpSocket.Close();
        }
    }
}

我確信我有一堆不必要的代碼,但我是新手,我不知道該做什么,修復我的代碼以及如何解決上述問題的任何幫助都非常感謝。

編輯:

我應該說我的要求是使用UDP並在應用層自己實現這些。 您可以將此視為家庭作業,但我沒有這樣標記,因為代碼無關緊要,不會成為我的成績的一部分而我的問題(我的問題)是“如何編碼”,因為我的套接字體驗很少而且不是授課。

但是我必須說我現在解決了我的問題我認為......我在演示應用程序上使用了線程,它給了我一些問題,現在我在協議連接中使用它,更有意義,我可以輕松改變我的自定義協議類屬性,並從演示應用程序中讀取它們。

我指定了一個超時,如果達到超時則拋出一個SocketException。 只要捕獲到這樣的異常,就會關閉套接字連接。 我只是談論連接握手,僅此而已。 如果沒有捕獲異常,代碼可能會順利進行並建立連接。

請相應調整您的答案。 現在,對我來說,將任何一個標記為已接受的答案是沒有意義的,希望你理解。

你的東西有點不對勁。

首先,UDP是無連接的。 您沒有連接或斷開連接。 您所做的就是發送和接收(每次都必須指定目的地)。 您還應該知道UDP承諾的唯一事情就是每次讀取都會收到完整的消息。 UDP不保證您的郵件以正確的順序到達或者它們到達所有郵件。

另一方面,TCP是基於連接的。 您連接,發送/接收,最后斷開連接。 TCP是基於流的(而UDP是基於消息的),這意味着您可以在第一次讀取時獲得一半消息,在第二次讀取時獲得另一半消息。 TCP承諾,一切都會以正確的順序到達(或者會死於嘗試;)。 因此,使用TCP意味着您應該擁有某種邏輯來了解完整消息何時到達以及用於構建完整消息的緩沖區。

下一個重要問題是關於阻止。 由於您是新手,我建議您使用Threads來處理套接字。 將偵聽器套接字放在一個線程中,並將每個連接套接字放在一個單獨的線程中(5個連接的客戶端= 5個線程)。

我還建議您使用TCP,因為它比構建消息和構建事務系統更容易構建完整的消息(如果您想確保所有消息到達/來自客戶端,則需要它)。

更新

你仍然有UDP錯誤。 除了清理系統資源之外,Close不會執行任何操作。 你應該做這樣的事情:

public void MySimpleServer(string address, int port) 
{
    try 
    {
        byte[] bufferBytes = new byte[32];

        if(address.Equals("0.0.0.0")) {
            udpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
        } else {
            udpSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
        }

        remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
        while (serverCanRun)
        {
            int numBytesReceived = udpSocket.ReceiveFrom(bufferBytes, ref remoteEndPoint);

            // just means that one of the clients closed the connection using Shutdown.
            // doesnt mean that we cant continue to receive.
            if(numBytesReceived == 0)
                continue; 

            // same here, loop to receive from another client.
            Packet syncPacket = new Packet(bufferBytes);
            if (syncPacket.PacketType != PacketType.Control)
                continue; 

            HandlePacket(packet, endPoint);
        }
    } catch {
        if(udpSocket != null) {
            udpSocket.Close();
        }
    }
}

看到? 因為沒有連接,所以只是浪費時間來關閉UDP套接字以開始從另一個套接字收聽。 同一個套接字可以從知道正確端口和地址的所有udp客戶端接收。 這就是remoteEndPoint的用途。 它告訴發送消息的客戶端。

更新2

小更新,以總結我的所有評論。

UDP是無連接的。 您永遠無法檢測連接是已建立還是已斷開連接。 UDP套接字上的Close方法只釋放系統資源。 client.Close()調用不會通知服務器套接字(就像TCP一樣)。

檢查連接是否打開的最佳方法是創建ping / pong樣式的數據包。 即客戶端發送PING消息,服務器以PONG響應。 請記住,如果消息未到達,UDP將不會嘗試重新發送消息。 因此,在假定服務器已關閉(如果您沒有收到PONG)之前,您需要重新發送PING幾次。

對於關閉客戶端,您需要將自己的消息發送到服務器,告知客戶端將停止與服務器通信。 為了可靠性,同樣的事情在這里,繼續重新發送BYE消息,直到您收到回復。

如果你想要可靠性,你必須為UDP實現一個事務系統。 SIP(google rfc3261)是使用UDP上的事務的協議示例。

根據您的描述,我覺得您應該使用TCP套接字而不是UDP。 不同的是

TCP - 您在特定IP上等待連接:某些用戶可以連接到它的端口,直到套接字關閉可以通過發送和接收信息進行通信。 這就像打電話給某人。

UDP - 您在某個IP上等待消息:端口。 想要通信的用戶只是通過UDP發送消息。 您將通過UDP接收消息。 交貨順序無法保證。 這更像是向某人發送蝸牛郵件。 沒有建立專用的通信渠道。

現在來解決你的問題

服務器

  1. 使用TCP系列創建一個Socket。
  2. 創建一個線程並接受該線程中的連接或使用Socket的BeginAccept apis。
  3. 在主線程中,您仍然可以顯示自動收報機或您想要做的任何事情。

客戶

  1. 連接到服務器。
  2. 通過發送和接收數據進行通信。

暫無
暫無

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

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