Java 網路編程 —— Socket 詳解

来源:https://www.cnblogs.com/Yee-Q/archive/2023/04/30/17365331.html
-Advertisement-
Play Games

構造 Socket 在【客戶端/服務端】的通信模式中,客戶端需要主動構造與伺服器連接的 Socket,構造方法有以下幾種重載形式: Socket() Socket(InetAddress address, int port) throws UnknownHostException,IOExcepti ...


構造 Socket

在【客戶端/服務端】的通信模式中,客戶端需要主動構造與伺服器連接的 Socket,構造方法有以下幾種重載形式:

Socket()
Socket(InetAddress address, int port) throws UnknownHostException,IOException
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException
Socket(String host, int port) throws UnknownHostException,IOException
Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException
Socket(Proxy proxy)

除了第一個不帶參數的構造方法,其他構造方法都會試圖建立與伺服器的連接,一旦連接成功,就返回 Socket 對象,否則拋出異常

1. 設定等待建立連接的超時時間

當客戶端的 Socket 構造方法請求與伺服器連接時,可能要等待一段時間。在預設情況下,Socket 構造方法會一直等待下去,直到連接成功,或者出現異常。Socket 構造方法請求連接時,受底層網路的傳輸速度的影響,可能會處於長時間的等待狀態。如果希望限定等待連接的時間,就需要使用第一個不帶參數的構造方法

Socket socket = new Socket();
SocketAddress remoteAddr = new InetSocketAddress("1ocalhostn", 8000);
// 參數endpoint指定伺服器的地址,參數timeout設定的超時時間(ms)
// 如果參數timeout被設為0則表示永遠不會超時
socket.connect(remoteAddr, 60000);

以上代碼用於連接到本地機器上的監聽 8000 埠的伺服器程式,等待連接的最長時間為一分鐘。如果在一分鐘內連接成功,則 connect() 方法順利返回,如果在一分鐘內出現某種異常則拋出該異常,如果在一分鐘後既沒有連接成功,也沒有出現異常,那麼會拋出 SocketTimeoutException

2. 設定伺服器的地址

除了不帶參數的構造方法,其他構造方法都需要在參數中設定伺服器的地城,包括伺服器的 IP 或主機名,以及埠

// address表示主機的IP地址
Socket(InetAddress address, int port)
// address表示主機的名字
Socket(String host, int port)

InetAddress 類表示主機的P地址,提供了一系列靜態工廠方法用於構造自身實例

// 返回本地主機的IP地址、
InetAddress addr1 = inetAddress.getLocalHost();
// 返回代表 "222.34.57” 的 IPv4 地址
InetAddress addr2 = InetAddress.getByName("222.34.5.7");
// 返同代表 ”2001:DB8:2DE::E13" 的 IPv6 地址
InetAddress addr3 = InetAddress.getByName("2001:DB8:2DE::E13");
// 返回主機名為 "www.javathinker.net" 的 IP 地址
InetAddress addr4 = InetAddress.getByName ("www.javathinker.net");

3. 設定客戶端的地址

在一個 Socket 對象中既包含遠程伺服器的 IP 地址和埠信息,也包含本地客戶端的 IP 地址和埠信息。在預設情況下,客戶端的 IP 地址來自客戶程式所在的主機,客戶端的埠則由操作系統隨機分配。Socket 類還有兩個構造方法允許顯式地設置客戶端的 IP 地址和埠

Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException
Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException

如果一個主機同時屬於兩個以上的網路,它就可能擁有兩個以上 IP 地址,例如一個主機在 Internet 網路中的 IP 地址為 “222.67,1.34”,在一個區域網中的 IP 地址為 “1125.4.3",假定這個主機上的客戶程式希望和同一個局城網上的一個地址為 “112.5.4.4:8000” 的伺服器程式通信,客戶端可按照如下方式構造 Socket 對象

InetAddress remoteAddr = InetAddress.getByName("112.5,4.45");
InetAddress localAddr = InetAddress.getByName("112.5.4.3");
//客戶端使用口2345
Socket socket = new Socket(remoteAddr, 8000, localAddr, 2345);

4. 客戶連接伺服器時可能拋出的異常

當 Socket 的構造方法請求連接伺服器時,可能會拋出以下異常:

  • UnknownHostException:無法識別主機的名字或 IP 地址
  • ConnectException:沒有伺服器進程監聽指定的埠,或者伺服器進程拒絕連接
  • SocketTimeoutException:等待連接超時
  • BindException:無法把Socket 對象與指定的本地 IP 地址或埠綁定

5. 使用代理伺服器

在實際應用中,有的客戶程式會通過代理伺服器來訪問遠程伺服器。代理伺服器有許多功能,比如能作為防火牆進行安全防範,或者提高訪問速度,或者具有訪問特定遠程伺服器的許可權

String proxyIP = "myproxy.abc.oom"; // 代理伺服器地址
int proxyPort = 1080; // 代理伺服器埠
// 創建代理對象
Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(proxyIP, proxyPort));
Socket socket  new Socket(proxy);
//連接到遠程伺服器
socket.connect(new InetSocketAddress("www.javathinker.net", 80));

ProxyType 類表示代理伺服器的類型,有以下可選值:

  • Proxy.Type.SOCKS:在分層的網路結構中,SOCKS 是位於會話層的代理類型
  • Proxy.Type.HTTP:在分層的網路結構中,HTTP 是位於應用層的代理類型
  • Proxy.Type.DIRECT:不使用代理,直接連接遠程伺服器

6. InetAddress 地址類的用法

InetAddress 類表示主機的IP 地址,InetAddress 類的靜態工廠方法給 getByName() 用於構造自身的實例

// 返回代表 "222.34.5.7" 的 IPv4 地址
InetAddress addr2 = InetAddress,getByName("222.34.5.7");
// 返回主機名為 "www.javathinker.net" 的 IP 地址
InetAddress addr4 = InetAddress.getByName("www.javathinker.net");

InetAddress 還提供了獲取相應的主機名的兩種方法:

  • getHostname():首先從 DNS 緩存中查找與 IP 地址匹配的主機名,如果不存在,再通過 DNS 伺服器查找,如果找到,則返回主機名,否則返回 IP 地址
  • getCanonicalHostName():通過 DNS 伺服器查找與 IP 地址匹配的主機名,如果找到則返回主機名,否則返問 IP 地址

以上兩種方法的區別在於 getHostname() 會先查找 DNS 緩存,減少查找 DNS 伺服器的概率,提高查找性能。而 getCanonicalHostName() 總是查找 DNS 伺服器,確保獲得當前最新版本的主機名

InetAddress 類還提供了兩個測試能否從本地主機連接到特定主機的方法:

public boolean isReachable(int timeout) throws IOException
public boolean isReachable(NefworkInterface interface, int ttl, int timeout) throws IOException

如果遠程主機在參數 timeout(ms)指定的時間內做出回應,以上方法返回true,否則返回 false,如果出現網路錯誤則拋出 IOException。第二種方法還允許從參數指定的本地網路介面建立連接,以及 TTL(IP 數據包被丟棄前允許存在的時間)

7. NetworkInterface 類的用法

NetworkInterfiace 類表示物理上的網路介面,它有兩種構造自身實例的靜態工廠方法,這兩種方法都聲明拋出 SocketException

// 參數 name 指定網路介面的名字,如果不存在與名字對應的網路介面,就返回 null
getByName(String name)
// 參數 address 指定網路介面的 IP 地址,如果不存在與 IP 地址對應的網路介面,就返回 null
getByInetAddress(InetAddress address)

NetworkInterface 類的以下方法用於獲取網路介面的信息

// 返回網路介面的名字
public String getName()
// 返回和網路介面綁定的所有 IP 地址,返回值為 Enumeration 類型,裡面存放了表示 IP 地址的 InetAddress 對象
public Enumeration getInetAddresses()

獲取 Socket 的信息

在一個 Socket 對象中同時包含了遠程伺服器的 IP 地址和埠信息,以及客戶本地的 IP 地址和埠信息。此外,從 Socket 對象中還可以獲得輸出流和輸入流,分別用於向伺服器發送數據,以及接收從伺服器端發來的數據

以下方法用於獲取 Socket 的有關信息

// 獲得遠程被連接進程的IP地址
getInetAddress()
// 獲得遠程被連接進程的埠
getPort()
// 獲得本地的IP地址
getLocalAddress()
// 獲得本地的埠
getLocalPort()
// 獲得輸入流,如果Socket還沒有連接,或者已經關團,或者已經通過shutdownInput()方法關閉輸入流,那麼此方法會拋出IOException
getInputStream()
// 獲得輸出流,如果Socket還沒有連接,或者已經關閉,或者已經通過shutdownOutput()方法關閉輸出流,那麼此方法會拋出 IOException
getOutputStream()

關閉 Socket

當客戶與伺服器的通信結束時,應該及時關閉 Socket,以釋放 Socket 占用的包括埠在內的各種資源。Socket 的 close() 方法負責關閉 Socket,如果一個 socket 對象被關閉,就不能再通過它的輸入流和輸出流進行 IO 操作,否則會導致 IOException

Socket 類提供了三個狀態測試方法

// 如果Socket沒有關閉,則返回false,否則返回true
isClosed()
// 如果Socket曾經連接到遠程主機,不管當前是否已經關閉,都返回true。如果Socket從未連接到遠程主機,就返回false
isConnected()
// 如果Socket已經與一個本地埠綁定,則返回true,否則返回false
isBound()

如果要判斷一個 Socket 對象當前是否處於連接狀態,可採用以下方式

String isConnected = socket.isConnected() && !socket.isClosed();

半關閉 Socket

進程 A 與進程 B 通過 Socket 通信,假定進程 A 輸出數據,進程 B 讀入數據,進程 A 如何告訴進程 B 所有數據已經輸出完畢呢?有幾種處理辦法:

  • 如果進程 A 與進程 B 交換的是字元流,並且都一行一行地讀寫數據,那麼可以事先約定以一個特殊的標誌作為結束標誌,例如以字元串 “bye” 作為結束標誌,當進程 A 向進程 B 發送一行字元串 “bye”,進程 B 讀到這一行數據後,就停止讀取數據

  • 進程 A 先發送一個消息,告訴進程 B 所發送的正文的長度,然後發送正文。進程 B 先獲知進程 A 將發送的正文的長度,接下來只要讀取該長度的字元或者位元組,就停止讀取數據

  • 進程 A 發完所有數據後,關閉 Socket,當進程 B 讀入了進程 A 發送的所有數據後,再次執行輸入流的 read() 方法時,該方法返回 “-1”,如果執行 BufferedReader 的 readLine() 方法,那麼該方法返回 null

    ByteArrayOutputstream bufferenew = ByteArrayOutputstream();
    byte[] buff = new byte[1024);
    int len = -1;
    while((len = socketIn.read(buff)) != -1) {
        buffer.write(buff, 0, len);   
    }
    
  • 當調用 Socke t的 close() 方法關閉 Socket 後,它的輸出流和輸入流也都被關閉。有的時候,可能僅僅希望關閉輸出流或輸入流之一,此時可以採用 Socket 類提供的半關閉方法

    shutdownInput() // 關閉輸入流
    shutdownOutput() // 關團輸出流
    

    假定進程 A 執行以下代碼,先向進程 B 發送一個字元串,等到進程 B 接收到這個字元串後,進程 A 再調用 Socket 的 shutdownOutput() 方法關閉輸出流,接下來進程 A 不允許再輸出數據,但是仍可以通過輸入流讀入數據

    // 發出請求信息
    String data = ...;
    OutputStream socketOut = socket.getOutputStream();
    socketOut.write(data.getBytes());
    socketOut.flush();
    // 讀取響應
    InputStream socketIn = socket.getInputStream();
    if(伺服器端返回提示信息,表明已經接收到客戶端的所有請求數據)
        socket.shutdownOutput(); //關閉輸出流
    //繼續通過socketIn讀取數據
    ...
    

    值得註意的是,先後調用 Socket 的 shutdownInput() 和 shutdownOutput() 方法,僅僅關閉了輸入流和輸出流,並不等價於調用 Socket 的 close() 方法。在通信結束後,仍然要調用 Socket 的 close() 方法,因為只有該方法才會釋放 Socket 占用的資源,比如占用的本地埠等

    Socket 類還提供了兩種狀態測試方法,用來判斷輸入流和輸出流是否關閉

    public boolean isInputShutdown() // 如果輸入流關閉,則返回true,否則返回false
    public boolean isOutputShutdown() // 如果輸出流關閉,則返回true,否則返回false
    

設置 Socket 的選項

1. TCP_NODELAY

表示立即發送數據。在預設情況,下發送數據採用 Negale 演算法,發送方發送的數據不會立刻被髮出,而是先放在緩衝區內,等緩衝區滿了再發出。發送完一批數據後,會等待接收方對這批數據的回應,然後發送下一批數據。此演算法法適用於發送方需要發送大批量數據並且接收方會及時做出回應的場合,這種演算法通過減少傳輸數據的次數來提高通信效率

如巢發送方持續地發送小批量的數據。並且接收方不一定會立即發送響應數據,那麼 Negale 演算法會使發送方運行得很慢,對於GU程式,比如網路游戲程式(伺服器需要實時跟蹤客戶端滑鼠的移動),這個問題尤其突出

TCP_NODEALY 的預設值為 false,表示採用 Negale 演算法,如果調用 setTcpNoDelay(true) 方法,就會關閉 Socket 的緩衝,確保數據被及時發送

if(!socket.getTcpNoDelay())
    socket.setTcpNoDelay(true);

2. SO_RESUSEADDR

表示是否允許重用 Socket 所綁定的本地地址。當接收方通過 Socket 的 close() 方法關閉 Socket 時,如果網路上還有發送到這個 Socket 的數據,那麼底層的 Socket 不會立刻釋放本地埠,而是會等待一段時間,確保接收到了網路上發送過來的延遲數據,再釋放埠。Socket 接收到延遲數據後,不會對這些數據做任何處理。Socket 接收延遲數據的目的是,確保這些數據不會被其他碰巧綁定到同樣埠的新進程接收到

客戶程式一般採用隨機埠,因此出現兩個客戶程式綁定到同樣埠的可能性不大。許多伺服器程式都使用固定的埠。當伺服器程式被關閉後,有可能它的埠還會被占用一段時間,如果此時立刻在同一臺主機上重啟伺服器程式,由於埠已經被占用,使得服務感程式無法綁定到該埠,導致啟動失敗

為了確保當一個進程關閉了 Socket 後,即便它還沒釋放埠,同一臺主機上的其他進要也可以立刻重用該埠,可以調用 Socke 的 setResuseAddress(ture) 方法

if(!socket.getResuseAddress())
    socket.setResuseAddress(true);

值得註意的是 socket.setResuseAddress(true) 方法必須在 Socket 還沒有被綁定到一個本地埠之前調用,否則執行無效

3. SO_TIMEOUT

表示接收數據時的等待超時時間。當通過 Socket 的輸入流讀數據時,如果還沒有數據,就會等待。Socket 類的SO_TIMEOUT 選項用於設定接收數據的等待超時時間,單位為 ms,它的預設值為0,表示會無限等待,永遠不會超時

以下代碼把接收數據的等待超時時間設為三分鐘

if(socket.getTimeout() == 0)
    socket.setTimeout(60000 * 3);

Socket 的 setTimeout() 方法必須在接收數據之前執行才有效

4. SO_LINGER

表示與執行 Socket 的 close() 方法時,是否立即關閉底層的 Socket。在預設情況下執行 Socket 的 close() 方法,該方法會立即返回,但底層的 Socket 實際上並不立即關閉,它會延遲一段時間,直到發送完所有剩餘的數據,才會真正關閉 Socket

如果執行以下方法

socket.setSoLinger(true,0);

那麼執行 Socket 的 close() 方法,該方法也會立即返回而且底層的 Socket 也會立即關閉,所有未發送完的數據被丟棄

如果執行以下方法

socket.setSoLinger(true,3600);

那麼執行 Socket 的 close() 方法,該方法不會立即返回,而是進入阻塞狀態,同時,底層的 Socket 會嘗試發送剩餘的數據。只有滿足以下兩個條件之一,close() 方法才返回:

  • 底層的 Socket 已經發送完所有的剩餘數據
  • 儘管底層的 Socket 還沒有發送完所有的剩餘數據,但己經阻塞了 3600s,此時 close() 也會返回,未發送的數據被丟棄

5. SO_RCVBUF

表示接收數據的緩衝區的大小。一般說來,傳輸大的連續的數據塊,比如基於 HTTP 或 FTP 的通信,可以使用較大的緩衝區,這可以減少傳輸數據的次數,提高傳輸數據的效率。而對於互動式的通信方式,比如 Telnet 和網路游戲,則應該採用小的緩衝區,確保小批量的數據能及時發送給對方

6. SO_SNDBUF

表示發送數據的緩衝區的大小

7. SO_KEEPALIVE

表示對於長時間處於空閑狀態的 Socket,是否要自動把它關團。當 SO_KEEPALIVE 選項為 tue 時,表示底層的 TCP 實現會監視該連接是否有效連接處於空閑狀態,即連接的兩端沒有互相傳送數據超過了 2 小時,本地的 TCP 實現發送一個數據包給遠程的 Socket,如果遠程 Socke 沒有返迴響應,TCP 實現就會持續嘗試發送 11 分鐘,直到接收到響應為止。如果在 12 分鐘內未收到響應,TCP 實現就會自動關閉本地 Socket,斷開連接

SO_KEEPALIVE 選項的預設值為 false,表示 TCP 不會監視連接是否有效,不活動的客戶端可能會永久存在下去,而不會註意到伺服器已經崩潰

8. IP 服務類型選項

當用戶通過郵局發送普通信、掛號信或者快件時,實際上選擇了郵局提供的不同的服務。發送普通信的價格最低,但發送速度慢,並且可靠性沒有保證。發送掛號信的價格稚高,但可靠性有保證。發送快件的價格最高,發送速度最快,並且可靠性有保證

在 Internet 上傳輸數據也分為不同的服務類型,它們有不同的定價。用戶可以根據自己的需求,選擇不同的服務類型。例如發送視頻需要較高的帶寬,快速到達目的地,以保證接收方看到連續的畫面,而發送電子郵件可以使用較低的帶寬,延遲幾個小時到達目的地也沒關係

IP 規定了一些服務類型,用來定性地描述服務的質量,舉例如下:

  • 低成本:發送成本低
  • 高可靠性:保證把數據可靠地送達目的地
  • 最高吞吐量:一次可以接收或發送大批量的數據
  • 最小延遲:傳輸數據的速度快,把數據快速送達目的地

這些服務類型還可以進行組合,例如,可以同時要求獲得高可靠性和最小延遲。服務類型存儲在 IP 數據包頭部的名為 IP_TOS 的 8 位欄位中,Socket 類中提供了設置和讀取服務類型的方法

// 設置服務類型
public void setTrafficClass(int trafficClass) throws SocketException
// 讀取服務類型
public int getTrafficClass() throws SocketException

服務類型用 1 位元組來表示,取值範圍是 0 到 255 之間的整數。這個服務類型數據也會被覆制到 TCP 數據包頭部的 8 位欄位中。,在目前的網路協議中,對這個表示服務類型的位元組又做了進一步的細分:

  • 高六位:表示 DSCP 值,即表示不同的服務類型代碼號。DSCP 允許最多有 64 種服務類型
  • 低兩位:表示 ECN 值,即顯式擁塞通知信息

64 個 DSCP 值到底表示什麼含義,這是由具體的網路和路由器決定的。下麵是比較常見的 DCSP 值:

  • 預設服務類型:000000

  • 加速轉發類型:101110,特點是低損耗、低延遲、低抖動

  • 保證轉發類型:共 12 個取值,保證以指定速率傳送,見下表

    類型 第1類(最低轉發優先) 第2類 第3類 第4類(最高轉發優先)
    低丟包率 001010 010010 011010 100010
    中丟包率 001100 010100 011100 100100
    高丟包率 001110 010110 011110 100110

    其中第 1 類有最低轉發優先順序,第 4 類有最高轉發優先順序。也就是說,當網路出現阻塞時,第 4 類的數據包被優先轉發。每一類又包含了 3 個取值,其中低丟包率的服務類型丟棄數據包的概率小,而高丟包率的服務類型丟棄數據包的概率大

加速轉發類型比其他服務類型有更高的優先順序,例如以下代碼使得 Socket 採用加速轉發類型來收發數據:

Socket socket = new Socket("www.javathinker.net", 80);
// 0xB8 對應二進位數據 10111000
// 低兩位表示顯式擁塞通知,取值為 00
socket.setTrafficClass(0xB8);

值得註意的是,DCSP 值僅僅為底層的網路實現提供一個參考,有些底層 Socket 實現會忽略 DCSP 值,對它不進行任何處理

9. 設定連接時間、延遲和帶寬的相對重要性

從 JDK1.5 開始,為 Socket 類提供了一個 setPerformancePreferences() 方法

public vold setPerformancePreferences (int connectionTime, int latency, int bandwidth)

以上方法的 3 個參數表示網路傳輸數據的 3 項指標:

  • connectionTime:表示用最少時間建立連接
  • latency:表示最小廷遲
  • bandwidth:表示最高帶寬

setPerformancePreferences() 方法被用來設定這 3 項指標之間的相對要性。可以為這些參數賦予任意的整數。這些整數之間的相對大小就決定了相應參數的相對重要性。例如,如果參數 connectionTime 為 2,參數 latency 為 1,而參數 bandwidth 為 3,就表示最高帶寬最重要,其次是最少連接時間,最後是最小延遲

值得註意的是 setPerformancePreferences() 方法所做的設置僅僅為底層的網路實現提供一個參考,有些底層 Socket 實現會忽略這一設置,對它不進行任何處理


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • (Linux安裝軟體) 前言 這期呢主要說一說Linux中包軟體管理相關命令,這一期的命令雖然只有兩個。但 軟體包的安裝和卸載都是我們平常最常用的,需要熟練掌握。 rpm和yum 是CentOS 主要的包軟體管理。 兩個命令各有用處,①yum需要互聯網,yum會去網上的yum源獲取所需的軟體包 ② ...
  • 前言 本篇文章主要介紹的關於本人在使用MySql記錄筆記的一些使用方法和經驗,溫馨提示,本文有點長,約1.5w字,幾十張圖片,建議收藏查看。 一、MySql安裝 下載地址:https://dev.mysql.com/downloads/ 在安裝MySql之前,查看是否以及安裝過MySql,如果已經安 ...
  • CSS知識點總結 文章內容可能較多且雜亂,可以查看頁面右方的目錄,以及使用Ctrl+F搜索頁面內容進行內容定位。 常用屬性 推薦搭配文檔使用,可以複製屬性名,到文檔查看該屬性對應的可選值。 👉MDN Web Docs 盒模型 寬度:width 高度:height 邊框:border 圓角:bord ...
  • 1、四層結構 viewer --> datasources(DataSourceCollection類型) --> datasource --> entities(EntityCollection類型) --> entity 需要學習的方向是:只需要註意每個層與層之間的關係和entity實例如何創建 ...
  • 一、前言 第二次在博客園上發佈面向對象程式設計題目集的總結博客。經過幾周的學習,面向對象的理念更加深入。雖然已經學了些面向對象程式設計,學好這部分內容還是有較大難度。 關於知識點 本次的題目集所體現的知識點已經不僅限於Java的語法知識,還需要考慮設計問題,不能看到題目就開始進行代碼編寫,需要考慮類 ...
  • B/S 結構系統的 緩存機制(Cookie) 以及基於 cookie 機制實現 oa 十天免登錄的功能 @ 每博一文案 嘿,大風揚起的沉沙中,每一粒都有它的必然性,而每個人的命運都有自己的因果, 為自己的選擇負責承擔或好或壞的結果。是成年人的必修課。 有人請教索羅斯投資的指導,我的父親一直追隨你炒股 ...
  • JSP 的本質原理解析:"編寫的時候是JSP,心裡想解讀的是 java 源碼" @ 每博一文案 活明白的人,一生只做好了這兩件事: 每個瞬間都充滿了選擇和承擔,就算面前是一座獨木橋,也必須選擇是前進後退,亦或是留在原地此時此刻你所經歷的一切。 這是過往無數個選擇後的結果,哪些小的選擇匯聚在了一起,最 ...
  • 一前言 題目集四主要考察的是對LocalDate,ArrayList,HashSet等Java自帶類的使用 題目集五主要考察的是對正則表達式的使用,以及對其題目集三的時間題目的進行類結構的改變 題目集六隻有一道題,主要是對題目集四的第一題進行加大難度 總的來說這幾次的題目量比前面幾次都要少,但是題目 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...