1. 程式人生 > >通過埠來判斷Minecraft伺服器的延遲

通過埠來判斷Minecraft伺服器的延遲

最近加入了一個開源的mc啟動器專案開發組,另外加上本人是一mc的服主,所以想到了在啟動器中加入此功能。

眾所周知,幾乎所有提供服務的伺服器為了系統安全禁止了ping,這樣多線線路(伺服器有多個IP)的情況下,線路的選優全部交給了第三方的DNS系統

而本人頗不喜這種被動選優的方式,於是便想通過遊戲的啟動器來自動判斷伺服器多個地址的延遲並選擇最好的線路。

這裡因為Minecraft伺服器走的是tcp埠,就只介紹tcp埠的方法,其實udp也差不多。

該專案只是一個在校學習學生的頗為基礎的研究,請勿嘲笑。

思路:

開啟埠,傳送資料,開始計時然後等待服務端返回資料,一旦接受到資料就返回執行時間。

這樣其實程式的流程已經大體明白了,不多說,上菜

  
        static NetworkStream stream = null;
        static string sendstring = "2045";//傳送內容 
        static byte[] senddata = <span style="font-family: 'Courier New'; line-height: 18px; white-space: pre-wrap;">Encoding.Default.GetBytes</span>(sendstring);//轉換為網路傳輸用位元組陣列
        static Stopwatch time = new Stopwatch();//例項化計時
        private static TcpClient t_client = null;//例項化tcp連線        
        /// <summary>
        /// 獲取TCP埠延遲(同步)
        /// <param name="hostname">目標IP地址或主機名</param>
        /// <param name="port">目標埠</param>
        /// </summary>
        public static long tcp(string hostname, string port)
        {

          t_client = new TcpClient();//例項化tcp

            try
            {
                t_client.Connect(hostname, Convert.ToInt32(port));//開啟連線
                stream = t_client.GetStream();//獲取網路流
                stream.Write(senddata, 0, senddata.Length);//同步傳送資料
                time.Restart();//重置計時器並開始計時
                byte[] result = new byte[t_client.Available];
                try
                {
                    /* stream.BeginRead(result, 0, result.Length, new AsyncCallback(ReadCallback), stream);*///非同步接受伺服器回報的字串
                    stream.Read(result, 0, result.Length);
                }
                catch { }
                string strResponse = Encoding.ASCII.GetString(result).Trim();//從伺服器接受到的字串
                t_client.Close();//關閉連線
                return time.ElapsedMilliseconds;//返回執行時間

            }
            catch (System.Exception ex)
            {
                return 999999;//打不開埠 發不了資料 均返回99999
            }
        }
這裡使用到了官方提供的Stopwatch 來進行計時
程式碼非常簡單 在此就不贅述了

那麼現在問題來了 主要有三種情況

1.提供域名和埠要是打不開會導致執行緒卡住

2.打開了資料發不過去也會卡住

3.資料發過去了,目標服務如果不回覆資料的會卡住

這裡就需要一種“超時“的功能,簡單點講就是當執行某個操作,一定時間得不到反饋,就彈出

那麼首先第一種情況,我們使用的是TcpClient開啟的tcp埠,這個類其實是自帶超時返還的,我們直接利用try的系統錯誤程式碼就行

第二種情況 我是沒想好怎麼解決,也對此概念不是很清楚,還是覺得自己懂的很少,先放棄了。

第三種情況 這裡就需要我們自己寫一個超時方法了

這裡特別說明下,比如我在tcp的25565埠開啟了一個mc伺服器的服務,不是往這個埠發什麼過去都會返回資料的,往往很多服務,只有你傳送特定的字串或者16進位制串過去,服務才會返回內容。

那麼幹貨

 /// <summary>
        ///  獲取TCP埠延遲(非同步)
        /// </summary>
        /// <param name="hostname">目標IP地址或主機名</param>
        /// <param name="port">目標埠</param>
        /// <returns></returns>
        public static long Begintcp(string hostname, string port)
        {
            t_client = new TcpClient();

            try
            {
                t_client.Connect(hostname, Convert.ToInt32(port));//開啟連線
                stream = t_client.GetStream();
                stream.Write(senddata, 0, senddata.Length);//同步傳送資料
                i = 0;
                time.Restart();//重置計時器並開始計時
                byte[] result = new byte[t_client.Available];
                try
                {
                    stream.BeginRead(result, 0, result.Length, new AsyncCallback(ReadCallback), stream);//非同步接受伺服器回報的字串

                }
                catch { }
                BackgroundWorker backWorker = new BackgroundWorker();
                backWorker.WorkerReportsProgress = true;
                backWorker.DoWork += new DoWorkEventHandler(bw_DoWork); //開啟一個執行緒
                if (backWorker.IsBusy != true)
                {
                    backWorker.RunWorkerAsync();
                }
                else
                { }
                while(true)
                {
                    if (i == 1)
                    {
                        time.Stop();
                        break;
                    }
                    else if (i == 2)
                    {
                        return 99999;
                    }
                }
                string strResponse = Encoding.ASCII.GetString(result).Trim();//從伺服器接受到的字串

                t_client.Close();//關閉連線
                return time.ElapsedMilliseconds;


            }
            catch (System.Exception ex)
            {
                return 999999;
            }
        }
        /// <summary>
        /// 讀取非同步委託
        /// </summary>
        /// <param name="ar"></param>
        private static void ReadCallback(IAsyncResult ar)
        {
            NetworkStream s = (NetworkStream)ar.AsyncState;
            try
            {
                
                    i = 1;
                    s.EndRead(ar);
              
            }
            catch
            { }

        }
 /// <summary>
        /// 超時非同步委託
        /// </summary>
        private static void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(5000);//設定超時五秒
            i = 2;
        }

以上,本人用的方法其實很蠢,也很簡單粗暴

設定了一個靜態變數 i初始值1。

然後非同步接收網路流的資料,這裡就開了個執行緒A就專門接受資料,一旦接受到資料就執行委託,委託就是把i設定成1;

在開非同步接收資料的同時,再開一個執行緒B,執行緒主要作用就是等待五秒(thread.sleep方法) 然後設定i=2;

主執行緒在開完兩個執行緒之後,立刻進入死迴圈,不停判斷i的值。等於1了 就返回時間,如果5秒鐘過後,還沒接受到資料,執行緒B就會將i設定成2 就會返回99999的延遲資料

當然肯定有更好的做法,以後更。

接下來就是針對MC服務端的研究了,如上面所說,你不是隨隨便便發一個數據過去,mc的服務端就會返回資料的,事實是我發了個”2048“過去,毛都沒有返回一個。

然後我就非常機智的從網上下了個tcp伺服器,然後用mc嘗試連線了下

結果如下圖:


然後服務端就接到了這些莫名其妙的16進位制資料

然後我再開啟了這個小工具的客戶端模式,把這個資料發了試一下 然後:


你會發現伺服器返還了以下資料

2016-7-8 23:55:52
*-----------------------TCP CLIENT---------------------*
CLIENT->SERVER  1200050C31302E3132322E362E3136345C0D010100
SERVER->CLIENT  .59:23565 xDD
SERVER->CLIENT  .59:23565 10.99.99"},{"modid":"Forge","version":"10.13.4.1481"},{"modid":"kimagine","version":"0.1"},{"modid":"iChunUtil","version":"4.1.2"},{"modid":"SC0_SpaceCore","version":"0.7.14"},{"modid":"recipehandler","version":"1.7.10"},{"modid":"IC2","version":"2.2.804-eѽ
SERVER->CLIENT  .59:23565 xperimental"},{"modid":"nevermine","version":"2.4.B-漢化:§6Remilia"},{"modid":"Morph","version":"0.9.1"},{"modid":"BambooMod","version":"[email protected][email protected] [email protected]@"},{"modid":"shipwrecks_winslow","version":"1.6.2"},{"modid":"SC0_UsefulPets","verѽ
SERVER->CLIENT  .59:23565 sion":"1.3"},{"modid":"ropebridge","version":"1.3"},{"modid":"FLabsBF","version":"4.3"},{"modid":"levels","version":"r2.3.0"},{"modid":"Oceancraft","version":"1.4.1"},{"modid":"heart","version":"2.0.0"},{"modid":"grapplemod","version":"1.0"},{"modid":"Redsѽ
SERVER->CLIENT  .59:23565 tonePasteMod","version":"1.6.2"},{"modid":"fairylights","version":"1.4.0"},{"modid":"lootablebodies","version":"1.3.4"},{"modid":"harvestcraft","version":"1.7.10j"},{"modid":"AdvancedSolarPanel","version":"1.7.10-3.5.1"}]}}

以上字符采用UTF-8 無bom編碼 其他會亂碼哦

這裡就很明顯了 只要發過去”1200050C31302E3132322E362E3136345C0D010100“這個16進位制字串

伺服器就會返回當前的mod列表

已經在1.6.4 1.7.1 1.8.4 1.9 版本的伺服器上測試過了

完整類庫在  https://code.csdn.net/u012735616/minecraft_tcp/tree/master/   

一個學生的研究日誌,感謝csdn上眾多的文章

相關推薦

通過判斷Minecraft伺服器延遲

最近加入了一個開源的mc啟動器專案開發組,另外加上本人是一mc的服主,所以想到了在啟動器中加入此功能。 眾所周知,幾乎所有提供服務的伺服器為了系統安全禁止了ping,這樣多線線路(伺服器有多個IP)的情況下,線路的選優全部交給了第三方的DNS系統 而本人頗不喜這種被動選優的

開發亞馬遜 MWS中feed上傳修改商品資訊 通過GetFeedSubmissionResult判斷上傳資料是否成功

GetFeedSubmissionResultSample.php 中的方法如下 $config = array ( 'ServiceURL' => $serviceUrl, 'ProxyHost' => null, 'ProxyPort' => -1, 'MaxErrorRetry' =

nginx通過區分不同的虛擬主機

nginx安裝啟動步驟https://blog.csdn.net/kxj19980524/article/details/85013683 如果想通過埠來區分不同虛擬主機需要修改它的配置檔案 安裝好之後,進入到nginx目錄下,進入到conf下面有個nginx.conf,然後編輯它進行修改

Docker如何通過連線一個容器

Docker容器的連線 1.網路埠對映 我們建立一個python應用的容器 # docker run -d -P training/webapp python app.py 我們使用 -P 引數建立一個容器,使用 docker ps 來看到埠5

Java中通過jsch連線遠端伺服器執行linux命令

有時候你可能需要通過程式碼來控制執行linux命令實現某些功能。 針對這類問題可以使用JSCH來實現,具體程式碼如下: public class CogradientImgFileManager{ private static final Logg

shell通過程序判斷多個redis服務是否都啟動

動指令碼後,不知redis服務是否全部啟動成功,查詢了shell中許多判斷自啟動是否執行成功的方法,經測試都失敗。最終發現多種方法只能判斷shell語句指令碼是否執行成功,並不能來判斷redis服務。我開始思考,先去執行自啟指令碼,然後去判斷程序是否存在這個程序,這樣不就可以

通過ALPN協議判斷伺服器端是否支援http2協議

由於當前主流瀏覽器,都只支援基於 HTTPS 部署的 HTTP/2,因為瀏覽器是基於ALPN協議來判斷伺服器是否支援HTTP2協議。瀏覽器在進行SSL連線,第一次傳送ClientHello包時,用過SS

通過js中的useragrent判斷設備是pc端還是移動端,跳轉不同的地址

lenovo err agent indexof pad ren phi mobile 手機 if(/AppleWebKit.*Mobile/i.test(navigator.userAgent) || (/MIDP|SymbianOS|NOKIA|SAMSUNG|L

ireport報表制作, 通過節點、產品類型判斷,當該節點審核通過之後,報表相對應的字段出顯示審核意見及簽名

not body node 代碼 person images bst nature sysdate 1、代碼 (與本內容相關的代碼:7~36) 1 select so.sale_order_no as sale_order_no, 2 (SELECT company_

通過判斷瀏覽器的userAgent,用正則判斷手機是否是ios(蘋果)和Android(安卓)客戶端

A- A+ 通過判斷瀏覽器的userAgent,用正則來判斷手機是否是 ios (蘋果)和 Android (安卓)客戶端。程式碼如下: <script type="text/java

009-通過express框架構建nodejs伺服器

    最近一直在研究nodejs 也是花了不少時間 今天終於把伺服器弄通了玩通了 實在是不容易     //1.載入express模組 var express = require('express'); //2.建立ap

java geometry判斷點線上的那一側,左側或右側,利用向量積,通過經緯度變化判斷目標運動方向,是否過線

線由兩點確定,判斷一個點線上的那一邊,來判斷gis引用中,通過經緯度變化來判斷目標運動方向 利用向量積正負判斷位置 Point2D.Double target = new Point2D.Double(0,0.5); Point2D.Double

C/C++是怎麼通過巨集定義判斷作業系統的

詳見Qt的qglobal.h 我們寫C/C++程式碼時也可以參考這個來確定所在平臺 /* The operating system, must be one of: (Q_OS_x) DARWIN - Darwin OS (synonym for Q_

C#利用反射判斷對象是否包含某個屬性的實現方法

是否 npr nbsp pro bsp str return ram ret 本文實例展示了C#利用反射來判斷對象是否包含某個屬性的實現方法,對於C#程序設計人員來說有一定的學習借鑒價值。 具體實現代碼如下: 1 /// <summary> 2 /// 利

[java]用md5判斷兩個文件是否完全相同

tle name stub https pem puts 試用 多圖 ring 1.前言   由於相比較兩張圖片是否是相同,如果通過像素點比較感覺速度比較慢,當很多圖片進行比較時,效率就低很多了。由於每個文件md5基本上是唯一的,所用用獲取文件的md5來判斷是否相同文件。

Android中通過反射設置Toast的顯示時間

ring margin ner manage etc short 延遲 lln sse 這個Toast的顯示在Android中的用途還是非常大的,同一時候我們也知道toast顯示的時間是不可控的。我們僅僅能改動他的顯示樣式和顯示的位置,盡管他提供了一個顯示時間的設置方法

老男孩教育每日一題-第66天-通過Iptables限定apache每秒鐘連接數為1,峰值為3

防火墻 iptables 每日一題 題目通過Iptables來限定apache每秒鐘連接數為1,峰值為3參考答案iptables -A INPUT -d 172.16.100.1 -p tcp --dport -m limit --limit 1/second --limit-burst -j

win7基礎 環境變量的配置 借助變量,通過拼接添加路徑

alt 中間 可用 步驟 學習資源 blog 字符串變量 images -1 註意事項:博文內容僅供參考,不可用於其他用途。 (我就是想把這個路徑加到環境變量中,以此來簡化java javac的使用步驟) (需要新建一個中間變量)

通過腳本判斷遠程Web服務器狀態碼是否正常

linux shell if read 通過腳本判斷遠程web服務器狀態碼是否正常通過腳本判斷遠程Web服務器狀態碼是否正常說明:(1)生產環境常見的HTTP狀態碼列表,請查看我的博文:http://wutengfei.blog.51cto.com/10942117/1934645(2)實驗中遠程nginx服

通過Executor啟動線程比用Thread的start()更好

對象創建 code splay 大小 延時 ren mar 數量 nds java5為什麽引入Executor線程池框架 1.new Thread()的缺點 (1) 每次new Thread()耗費性能 (2) 調用new Thread()創建的線程缺乏管理,被稱為野線程,