Java-Quellcode-Analyse und Interview-Fragen - Socket-Quellcode und Interview-Fragen

Diese Reihe von verwandten Blog, Mu Klasse Referenzspalte Java - Quellcode und Systemhersteller Interviewer kurz und bündig Zhenti
unterhalb dieser Spalte ist GitHub - Adresse:
Quelle aufgelöst: https://github.com/luanqiu/java8
Artikel Demo: https: // GitHub. com / luanqiu / java8_demo
Klassenkameraden können es bei Bedarf ansehen)

Die
chinesische Übersetzung der Einführungssprache Socket heißt Socket. Viele Studenten, die vier oder fünf Jahre gearbeitet haben, haben diese API nicht verwendet. Solange diese API verwendet wird, muss sie den Kerncode eines wichtigen Projekts bilden.

Normalerweise verwendet jeder verschiedene Open-Source-RPC-Frameworks wie Dubbo, gRPC, Spring Cloud usw., die selten Netzwerkanrufe schreiben müssen. Die folgenden drei Abschnitte können Ihnen dabei helfen, diesen Inhalt zu ergänzen, wenn Sie ihn wirklich benötigen. Kann als Beispiel im Handbuch verwendet werden.

Dieser Artikel und der Artikel "ServerSocket-Quellcode und Interviewfragen" befassen sich hauptsächlich mit dem Quellcode von Socket und ServerSocket. Das Kapitel "Arbeiten in der Praxis: Socket kombiniert mit der Verwendung des Thread-Pools" befasst sich hauptsächlich mit der Implementierung der beiden APIs in der tatsächlichen Arbeit.

1 Sockel Gesamtstruktur

Die Struktur von Socket ist sehr einfach. Socket ist wie eine Shell. Es umfasst verschiedene Vorgänge wie Socket-Initialisierung und Verbindungserstellung. Die zugrunde liegende Implementierung wird von SocketImpl implementiert. Die Geschäftslogik von Socket selbst ist sehr einfach.

Es gibt nicht viele Attribute von Socket, es gibt Socket-Status, SocketImpl, Lese- und Schreibstatus usw. Der Quellcode lautet wie folgt: Die
Fügen Sie hier eine Bildbeschreibung ein
Änderungen des Socket-Status entsprechen der Operationsmethode, z. B. dem neuen Socket (createImpl-Methode). , Der Status ändert sich in created = true. Nach dem Verbinden (connect) ändert sich der Status in linked = true und so weiter.

2 Initialisierung

Es gibt viele Konstruktoren von Socket, die in zwei Kategorien unterteilt werden können:

  1. Geben Sie einen Proxy-Typ (Proxy) an, um eine Reihe von Knoten zu erstellen. Es gibt drei Typen: DIRECT (direkte Verbindung), HTTP (Proxy für erweiterte HTTP- und FTP-Protokolle), SOCKS (SOCKS-Proxy) und die drei verschiedenen Codemethoden entsprechen unterschiedlichen SocketImpl Jeweils: PlainSocketImpl, HttpConnectSocketImpl, SocksSocketImpl Zusätzlich zum Typ gibt Proxy auch die Adresse und den Port an.
  2. Das Standard-SocksSocketImpl wird erstellt, und die Adresse und der Port müssen im Konstruktor übergeben werden. Der Quellcode lautet wie folgt:
// address 代表IP地址,port 表示套接字的端口
// address 我们一般使用 InetSocketAddress,InetSocketAddress 有 ip+port、域名+port、InetAddress 等初始化方式
public Socket(InetAddress address, int port) throws IOException {
    this(address != null ? new InetSocketAddress(address, port) : null,
         (SocketAddress) null, true);
}

Die Adresse hier kann eine IP-Adresse oder ein Domainname sein, z. B. 127.0.0.1 oder www.wenhe.com.

Werfen wir einen Blick auf den Quellcode dieses Konstruktors auf niedriger Ebene, der von diesem Konstruktor aufgerufen wird:

// stream 为 true 时,表示为stream socket 流套接字,使用 TCP 协议,比较稳定可靠,但占用资源多
// stream 为 false 时,表示为datagram socket 数据报套接字,使用 UDP 协议,不稳定,但占用资源少
private Socket(SocketAddress address, SocketAddress localAddr,
               boolean stream) throws IOException {
    setImpl();
 
    // backward compatibility
    if (address == null)
        throw new NullPointerException();
 
    try {
        // 创建 socket
        createImpl(stream);
        // 如果 ip 地址不为空,绑定地址
        if (localAddr != null)
            // create、bind、connect 也是 native 方法
            bind(localAddr);
        connect(address);
    } catch (IOException | IllegalArgumentException | SecurityException e) {
        try {
            close();
        } catch (IOException ce) {
            e.addSuppressed(ce);
        }
        throw e;
    }
}

Es ist aus dem Quellcode ersichtlich:

  1. Beim Erstellen von Socket können Sie TCP oder UDP auswählen. Der Standardwert ist TCP.
  2. Wenn die Adresse und der Port beim Erstellen eines Sockets übergeben werden, wird beim Erstellen versucht, einen Socket an dieser Adresse und diesem Port zu erstellen.
  3. Der parameterlose Konstruktor von Socket initialisiert nur SocksSocketImpl und bindet nicht an den aktuellen Adressport. Wir müssen die Verbindungsmethode manuell aufrufen, um die aktuelle Adresse und den aktuellen Port zu verwenden.
  4. Socket kann als Abstraktion der Netzwerkkommunikation auf Sprachebene verstanden werden. Das Erstellen, Verbinden und Schließen des zugrunde liegenden Netzwerks ist immer noch der vom TCP- oder UDP-Netzwerkprotokoll selbst festgelegte Standard. Socket verwendet nur die Java-Sprache, um eine Kapselungsebene zu erstellen, was uns komfortabler macht. Verwenden Sie.

3 Stellen Sie eine Verbindung zum Server her

Die Verbindungsmethode wird hauptsächlich verwendet, um den Socket-Client mit dem Server zu verbinden. Wenn die unterste Schicht das TCP-Schichtprotokoll ist, wird eine Verbindung mit dem Server über einen Drei-Wege-Handshake hergestellt, um die Kommunikation zwischen dem Client und dem Server vorzubereiten. Der untere Quellcode lautet wie folgt:

public void connect(SocketAddress endpoint, int timeout) throws IOException {
}

Die Verbindungsmethode erfordert zwei Eingabeparameter. Der erste Eingabeparameter ist SocketAddress, der die Adresse des Servers darstellt. Wir können InetSocketAddress für die Initialisierung verwenden, z. B.: New InetSocketAddress ("www.wenhe.com", 2000).

Der zweite Eingabeparameter ist die Bedeutung der Zeitüberschreitungszeit (in Millisekunden), die die maximale Wartezeit angibt, die der Client benötigt, um eine Verbindung zum Server herzustellen. Wenn die aktuelle Wartezeit überschritten wird, wird die Verbindung immer noch nicht erfolgreich hergestellt und eine SocketTimeoutException-Ausnahme wird ausgelöst. Wenn sie 0 ist, bedeutet dies unendliche Wartezeit.

4 Sockel häufig verwendete Einstellparameter

Die allgemeinen Einstellungsparameter von Socket finden Sie in der SocketOptions-Klasse. Als Nächstes analysieren wir sie nacheinander. Die meisten der folgenden Erkenntnisse stammen aus Klassenanmerkungen und Netzwerken.

4.1 setTcpNoDelay

Diese Methode wird zum Festlegen des Attributs TCP_NODELAY verwendet . Der Kommentar des Attributs lautet wie folgt: Diese Einstellung ist nur für TCP wirksam, hauptsächlich um die Verwendung des Nagle-Algorithmus zu verbieten. True bedeutet verboten, false bedeutet verwendet und der Standardwert ist false.

Für den Nagle-Algorithmus zitieren wir die Erklärung auf Wikipedia:

Der Nag-Algorithmus soll die Leistung des [TCP / IP] -Netzwerks verbessern, indem die Anzahl der gesendeten Pakete verringert wird. Er wurde von John Nag benannt, als er bei Ford Aerospace war.
Nags Dokument beschreibt, was er als "Problem mit kleinen Paketen" bezeichnet - eine Anwendung sendet ständig kleine Dateneinheiten und einige belegen oft nur 1 Byte. Da das TCP-Paket 40 Byte Header-Informationen enthält (TCP und IPv4 belegen jeweils 20 Byte), führt dies zu einem 41-Byte-Paket mit nur 1 Byte verfügbarer Informationen, was zu einer enormen Verschwendung führt. Diese Situation tritt häufig während der Telnet-Arbeitsphase auf. Die meisten Tastaturoperationen generieren 1 Byte Daten und senden diese sofort. Erschwerend kommt hinzu, dass bei einer langsamen Netzwerkverbindung eine große Anzahl dieser Datenpakete gleichzeitig übertragen wird, was zu Überlastung und Kollision führt.
Der Nag-Algorithmus vereint eine bestimmte Menge von Ausgabedaten und sendet sie einmal. Insbesondere solange es gesendete Datenpakete gibt, die noch nicht bestätigt wurden, puffert der Absender die Datenpakete weiter, bis eine bestimmte Datenmenge vor dem Senden akkumuliert ist.

Fassen Sie die Szene zusammen, in der der Algorithmus ein- und ausgeschaltet wird:

  1. Wenn der Nagle-Algorithmus für kleine Datenpakete wie Mausbewegungen und Klicks deaktiviert ist, interagiert der Client sofort mit dem Server. Die Echtzeitantwort ist sehr hoch, aber häufige Kommunikation verbraucht viele Netzwerkressourcen.
  2. Wenn der Nagle-Algorithmus aktiviert ist, führt der Algorithmus automatisch kleine Datenpakete zusammen und wartet, bis er eine bestimmte Größe (MSS) erreicht, bevor er mit dem Server interagiert. Der Vorteil besteht darin, dass die Anzahl der Kommunikationen verringert wird und der Nachteil darin besteht, dass die Echtzeitantwort geringer ist.

Wenn der Socket erstellt wird, ist der Nagle-Algorithmus standardmäßig aktiviert. Sie können auswählen, ob der Nagle-Algorithmus gemäß den Echtzeitanforderungen deaktiviert werden soll.

4.2 setSoLinger

Die setSoLinger-Methode wird hauptsächlich zum Festlegen des SO_LINGER- Attributwerts verwendet.

Der Kommentar bedeutet wahrscheinlich Folgendes: Wenn wir die close-Methode aufrufen, wird standardmäßig direkt zurückgegeben. Wenn der Wert jedoch SO_LINGER zugewiesen wird, wird die close-Methode blockiert. Warten Sie innerhalb der SO_LINGER-Zeit, bis die Kommunikationspartner Daten gesendet haben. Wenn die Zeit abgelaufen ist, ist dies noch nicht geschehen Am Ende wird TCP RST gesendet, um das Schließen von TCP zu erzwingen.

Werfen wir einen Blick auf den setSoLinger-Quellcode:

// on 为 false,表示不启用延时关闭,true 的话表示启用延时关闭
// linger 为延时的时间,单位秒
public void setSoLinger(boolean on, int linger) throws SocketException {
    // 检查是否已经关闭
    if (isClosed())
        throw new SocketException("Socket is closed");
    // 不启用延时关闭
    if (!on) {
        getImpl().setOption(SocketOptions.SO_LINGER, new Boolean(on));
    // 启用延时关闭,如果 linger 为 0,那么会立即关闭
    // linger 最大为 65535 秒,约 18 小时
    } else {
        if (linger < 0) {
            throw new IllegalArgumentException("invalid value for SO_LINGER");
        }
        if (linger > 65535)
            linger = 65535;
        getImpl().setOption(SocketOptions.SO_LINGER, new Integer(linger));
    }
}

4.3 setOOBInline

Die setOOBInline-Methode wird hauptsächlich zum Festlegen des SO_OOBINLINE- Attributs verwendet.

Der Hinweis lautet: Wenn Sie dringende TCP-Daten (TCP-Notfalldaten) akzeptieren möchten, können Sie diese Option aktivieren. Standardmäßig ist diese Option deaktiviert. Wir können Notfalldaten über die Socket # sendUrgentData-Methode senden.

Nach dem Abfragen vieler Informationen wird empfohlen, diesen Wert nicht so weit wie möglich festzulegen und die Verwendung von TCP-Notfalldaten zu verbieten.

4.4 setSoTimeout

Die setSoTimeout-Methode wird hauptsächlich zum Festlegen des SO_TIMEOUT- Attributs verwendet.

Der Hinweis lautet: Dient zum Festlegen der Zeitüberschreitungszeit für Blockierungsvorgänge. Die Blockierungsvorgänge umfassen hauptsächlich:

  1. ServerSocket.accept () Der Server wartet auf die Verbindung des Clients.
  2. SocketInputStream.read () Zeitlimit für Client- oder Server-Leseeingabe;
  3. DatagramSocket.receive ()。

Wir müssen diese Option vor dem Blockierungsvorgang festlegen . Wenn die Zeit abgelaufen ist, blockiert der Vorgang immer noch und eine InterruptedIOException wird ausgelöst (Socket löst eine SocketTimeoutException-Ausnahme aus, verschiedene Sockets können unterschiedliche Ausnahmen auslösen).

Wenn für Socket die Zeitüberschreitungszeit auf 0 gesetzt ist, bedeutet dies, dass keine Zeitüberschreitungszeit vorhanden ist und beim Blockieren auf unbestimmte Zeit gewartet wird.

4.5 setSendBufferSize

Die setSendBufferSize-Methode wird hauptsächlich zum Festlegen des SO_SNDBUF- Attributs verwendet. Der Eingabeparameter ist vom Typ int. Dies bedeutet, dass die Größe des Puffers am sendenden Ende (Ausgabeende) in Byte festgelegt wird.

Die Eingabeparametergröße muss größer als 0 sein, andernfalls wird eine IllegalArgumentException ausgelöst.

Im Allgemeinen nehmen wir die Standardeinstellung an. Wenn der Wert zu klein eingestellt ist, ist die Netzwerkinteraktion wahrscheinlich zu häufig. Wenn der Wert zu groß eingestellt ist, ist die Interaktion geringer und die Echtzeitleistung geringer.

4.6 setReceiveBufferSize

Die setReceiveBufferSize-Methode wird hauptsächlich zum Festlegen des SO_RCVBUF- Attributs verwendet. Der Eingabeparameter ist vom Typ int. Dies bedeutet, dass die Größe des Puffers am empfangenden Ende in Byte festgelegt wird.

Die Eingabeparametergröße muss größer als 0 sein, andernfalls wird eine IllegalArgumentException ausgelöst.

Im Allgemeinen können wir nach dem Einrichten des Sockets die Fenstergröße nach Belieben ändern. Wenn die Fenstergröße jedoch größer als 64 KB ist, müssen wir Folgendes beachten:
1. Der Pufferwert muss festgelegt werden , bevor der Socket eine Verbindung zum Client herstellt .
2. Er muss an den ServerSocket gebunden sein Stellen Sie den Pufferwert vor der lokalen Adresse ein .

4.7 setKeepAlive

Die setKeepAlive-Methode wird hauptsächlich zum Festlegen des SO_KEEPALIVE- Attributs verwendet. Sie wird hauptsächlich verwendet, um festzustellen, ob der Socket auf dem Server noch aktiv ist. Die Standardeinstellung ist false, wodurch diese Funktion nicht ausgelöst wird.

Wenn SO_KEEPALIVE aktiviert ist, löst TCP automatisch die Funktion aus: Wenn innerhalb von zwei Stunden keine Kommunikation zwischen dem Client und dem Server-Socket besteht, sendet TCP automatisch eine Keepalive-Prüfung an die andere Partei, und die andere Partei muss auf diese Prüfung antworten (vorausgesetzt, der Client sendet an Server) gibt es drei Vorhersagen:

  1. Der Server verwendet die erwartete ACK-Antwort und zeigt damit an, dass alles normal ist.
  2. Der Server antwortet mit RST und zeigt an, dass sich der Server in einem Absturz- oder Neustartzustand befindet, und beendet die Verbindung.
  3. Keine Antwort vom Server (wird mehrmals versucht), was darauf hinweist, dass der Socket geschlossen wurde.

4.8 setReuseAddress

Die setReuseAddress-Methode wird hauptsächlich zum Festlegen des SO_REUSEADDR- Attributs verwendet. Der Eingabeparameter ist ein boolescher Wert und der Standardwert ist false.

Nachdem der Socket geschlossen wurde, wartet er einige Zeit, bevor er tatsächlich geschlossen wird. Wenn zu diesem Zeitpunkt ein neuer Socket dieselbe Adresse und denselben Port bindet und setReuseAddress true ist, kann die Bindung erfolgreich sein. Andernfalls schlägt die Bindung fehl.

5 Zusammenfassung

Wenn Sie immer Business-Code verwenden, wird Socket möglicherweise nur sehr wenig verwendet. Wenn Sie jedoch ein Interview über das Netzwerkprotokoll führen oder die Möglichkeit haben, in Zukunft Middleware zu verwenden, besteht eine hohe Wahrscheinlichkeit, dass Sie sich an Socket wenden. Es ist auch gut als Wissensreserve.

Veröffentlicht 40 Originalarbeiten · erntete Lob 1 · Ansichten 5354

Ich denke du magst

Origin blog.csdn.net/aha_jasper/article/details/105609521
Empfohlen
Rangfolge