tft每日頭條

 > 科技

 > c語言長連接用到的函數

c語言長連接用到的函數

科技 更新时间:2024-11-19 18:40:01

看到這個标題,如果你想說誰會這麼蛋疼,有網不好好上,那麼說明你還是一個純潔的少年。自動重撥的需求所在多有,主要是為了繞過各大網站對相同IP地址的重複請求次數限制等等。具體的我不說了,說多了說我教壞小孩子。我最近研究這個隻是因為我想寫個軟件自動播放和下載某個網站的視頻,but這個網站一天隻讓同個IP免費看五個視頻,我又摳不願意花錢,但是又特别想多看多載幾部。什麼,你要我把網站地址告訴你?還是算了吧,傳播XX(se)OO(qing)是違法的知道不。

按慣例,先聲明:技術知識浩瀚無垠,筆者但求淺嘗辄止便心滿意足,所以,本文并不确保描述的準确性,若有錯誤和不足之處請不吝賜教。

上網方式簡介


趁此機會簡單了解下各類網絡接入方式。以下描述主要來自百度文檔《淺談各種寬帶上網的方法》,有興趣的朋友可以自行百度搜索文庫。

撥号上網:20世紀90年代剛有互聯網的時候,老百姓上網使用最為普便的一種方式是撥号上網。隻要用戶擁有一台個人電腦、一個外置或内置的調制解調器(modem)和一根電話線,再向本地ISP供應商申請自己的賬号,或購買上網卡,擁有自己的用戶名和密碼後,然後通過撥打ISP的接入号連接到Internet上。那個時候,出差的人們常常會問賓館能否撥号上網,然後問撥什麼号,之後以緩慢的速度發送郵件或“暢遊”網絡。撥号方式理論上的最高速率56KBIT/S。除了速度慢外,同時隻能進行一項工作,比如上網了電話就打不進來。

  • LayoutKind,有三個枚舉值:Sequential,對象的成員按照它們出現的順序依次布局,并且同StructLayout的另一個屬性Pack有關,詳細規則可以參考百度的一篇文章<<内存對齊的規則以及作用>>,那篇文章中的#pragma pack()同這裡的Pack作用一樣;Explicit,對象的各個成員的精确位置被顯式控制,每個成員必須使用 System.Runtime.InteropServices.FieldOffsetAttribute指示該字段在類型中的位置,當映射到C/C 的union時,該枚舉值非常有用;Auto,運行時自動對象的成員選擇适當的布局,可能會對字段順序進行調整,使實例占用盡可能少的内存(當然前提是各個字段獨享自己的内存,不同于union)。考慮到CPU讀取效率,一般采用LayoutKind.Sequential。
  • Pack,第1條已述。
  • CharSet,指示字符是單字節or雙字節,這主要是曆史遺留問題。可以将之設為Auto,這樣,在 Windows NT、Windows 2000、Windows XP 和 Windows Server2003 系列上,默認值為 System.Runtime.InteropServices.CharSet.Unicode;在 Windows 98 和 Windows Me 上,默認值為 System.Runtime.InteropServices.CharSet.Ansi。
  • Size,指示類或結構的絕對大小。不常用,但是如果需要在結構末尾分配額外的空間,則可能會用到此屬性。
  • 結構體的ULONG_PTR等表示基元類型指針的字段,隻能使用IntPtr映射;若字段有預定義的若幹值表示有意義的狀态指示等,則可以使用enum映射,如dwfOptions标記RasDial的某些擴展信息,這些标記可以用枚舉值表示。

    [Flags] public enum RDEOPT { None = 0x0, UsePrefixSuffix = 0x1, PausedStates = 0x2, IgnoreModemSpeaker = 0x4, //... }

    RasDial還有個參數值得注意——lpvNotifier——雖然LPVOID類型表示這是個不透明指針,用IntPtr即可,但文檔所述表明這是個回調函數參數,當 Win32 函數需要返回多項數據時,通常都是通過回調機制來實現的,開發人員将函數指針傳遞給函數。.Net中有個類型專門作為方法的引用——Delegate,所以用Delegate映射更精确更方便。

    最終RasDial函數的C#封裝版本如下:

    [DllImport(NativeMethods.RasApi32Dll, CharSet = CharSet.Unicode)] private static extern int RasDial( IntPtr lpRasDialExtensions, string lpszPhonebook, IntPtr lpRasDialParams, RasNotifierType dwNotifierType, Delegate lpvNotifier, out RasHandle lphRasConn);

    可以看到lpRasDialExtensions使用的類型是IntPtr,如前所述,我們要手動為其分配内存(非托管),并寫入相應數據,關鍵代碼如下:

    try { IntPtr lpRasDialExtensions = IntPtr.Zero; var extensions = new RASDIALEXTENSIONS(); //根據StructLayout相關屬性計算内存大小 int extensionsSize = Marshal.SizeOf(typeof(RASDIALEXTENSIONS)); extensions.size = extensionsSize; #if (WIN7 || WIN8) extensions.devSpecificInfo.size = Marshal.SizeOf(typeof(RASDEVSPECIFICINFO)); #endif lpRasDialExtensions = Marshal.AllocHGlobal(extensionsSize); Marshal.StructureToPtr(extensions, lpRasDialExtensions, true); } catch (Exception) { //... } finally { if (lpRasDialExtensions != IntPtr.Zero) { Marshal.FreeHGlobal(lpRasDialExtensions); } }

    代碼并不複雜,其中Marshal.StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld)方法的第三個參數說明如下:

    假設 ptr 指向非托管内存塊。此内存塊的布局由相應的托管類 structure 描述。StructureToPtr将字段值從結構封送到指針。假設 ptr 塊包含引用字段,該字段指向當前包含“abc”的字符串緩沖區。假設托管端上相應的字段是包含“vwxyz”的字符串。如果不另行通知它,StructureToPtr将分配一個新的非托管緩沖區來保存“vwxyz”,并将它挂鈎到ptr 塊。這将丢棄舊緩沖區“abc”使之漂移而不将其釋放回非托管堆。最後,您将得到一個孤立的緩沖區,它表示在代碼中存在内存洩漏。如果将 fDeleteOld 參數設置為真,則StructureToPtr 在繼續為“vwxyz”分配新緩沖區之前釋放保存“abc”的緩沖區。

    調用結束後記住要使用Marshal.FreeHGlobal釋放非托管内存。

    DotRas


    以上代碼來自于一個開源項目DotRas,雖然我并不提倡重複造輪子,但大概知道輪子怎麼造總沒有壞處。由于筆者家裡條件不允許——光纖入戶——so,我借用朋友的虛拟機(ADSL)進行DotRas的調用測試,主要代碼如下:

    //斷開 private void btnHangUp_Click(object sender, RoutedEventArgs e) { if (_dataContext.SelectedRasConnection != null) { var conns = RasConnection.GetActiveConnections();//獲取當前所有活動連接 var conn = conns.First(o => o.EntryId == _dataContext.SelectedRasConnection.EntryId); if (conn != null) { RasIPInfo ipAddresses = (RasIPInfo)conn.GetProjectionInfo(RasProjectionType.IP); tbTestInfo.Text = "_前_" ipAddresses.IPAddress.ToString(); conn.HangUp();//斷開,斷開後RasConnection.GetActiveConnections()返回值裡就沒它了 System.Threading.Thread.Sleep(10000); DialUp(_dataContext.SelectedRasConnection.EntryName); } } } //撥号連接 private void DialUp(string entryname) { RasDialer dialer = new RasDialer(); dialer.EntryName = entryname; dialer.PhoneNumber = " "; dialer.AllowUseStoredCredentials = true; dialer.PhoneBookPath = RasPhoneBook.GetPhoneBookPath(RasPhoneBookType.AllUsers); dialer.Timeout = 1000; dialer.Dial(); if (_dataContext.SelectedRasConnection != null) { var conns = RasConnection.GetActiveConnections(); var conn = conns.First(o => o.EntryId == _dataContext.SelectedRasConnection.EntryId); if (conn != null) { RasIPInfo ipAddresses = (RasIPInfo)conn.GetProjectionInfo(RasProjectionType.IP); tbTestInfo.Text = "_後_" ipAddresses.IPAddress.ToString(); } } }

    界面如圖:

    c語言長連接用到的函數(用代碼控制網絡斷開與重連)1

    點斷開後,果然遠程桌面斷開了:

    c語言長連接用到的函數(用代碼控制網絡斷開與重連)2

    10秒鐘後,虛拟機重撥連接,再等待一段時間後(這個時間比較長有1到3分鐘,遠遠沒達到實用的标準,可能是花生殼域名重新解析的緣故;經朋友在本地測試,速度杠杠的),界面重新展現:

    c語言長連接用到的函數(用代碼控制網絡斷開與重連)3

    可以看到前後的IP是不一樣的。

    ,

    更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

    查看全部

    相关科技资讯推荐

    热门科技资讯推荐

    网友关注

    Copyright 2023-2024 - www.tftnews.com All Rights Reserved