Strategie zur Optimierung von App-Online-Netzwerkproblemen

In unserem App-Entwicklungsprozess ist das Netzwerk unverzichtbar. Es ist fast schwierig, sich Apps vorzustellen, die keine Netzwerkübertragung erfordern. Daher sind Netzwerkprobleme im Allgemeinen schwer offline zu reproduzieren. Sobald sie in den Händen der Benutzer sind, werden sie auf viele stoßen Es gibt schwierige und komplizierte Krankheiten, daher ist die Netzwerküberwachung unerlässlich. Für häufige Benutzerprobleme müssen wir auch Optimierungsstrategien in tatsächliche Projekte integrieren.

1 Grundlegende Optimierung des Netzwerks

Für einige gängige Netzwerkanforderungs-Frameworks wie OkHttp, Retrofit usw. kapseln sie tatsächlich das HTTP-Protokoll. Am häufigsten verwenden wir POST- oder GET-Anfragen. Wenn wir Client-Entwicklung durchführen, ist es möglich, diese grundlegenden Inhalte zu kennen Schreiben Sie auch Code, aber wenn Sie wirklich auf Online-Netzwerkprobleme stoßen, können Sie es nicht herausfinden. Tatsächlich ist das größte Problem das mangelnde Wissen über das Netzwerk. Beginnen wir also am Anfang des Artikels mit einigen grundlegenden Netzwerken Wissen.

1.1 Arten von Netzwerkverbindungen

Tatsächlich initiieren wir bei Netzwerkverbindungen normalerweise eine Anfrage an den Server und der Server gibt eine entsprechende Antwort zurück. Gleichzeitig kann die Datenübertragung jedoch nur in eine Richtung erfolgen. Diese Verbindungsmethode wird als Halbduplex-Kommunikation bezeichnet .

Typ beschreiben Beispiel
Simplex Bei der Kommunikation können Daten nur von einer Partei zur anderen gesendet werden Zu den gebräuchlichsten gehören das UDP-Protokoll und Android-Broadcast
Halbduplex Während des Kommunikationsprozesses können Daten von einer Partei A an die andere Partei B oder von einer Partei B an die andere Partei A gesendet werden, es kann jedoch gleichzeitig nur eine Datenübertragung von einer Partei stattfinden. Gängige Protokolle wie HTTP
Vollduplex Es findet jederzeit eine bidirektionale Datenübertragung von A nach B und von B nach A statt. Zu den gebräuchlichsten gehören das Socket-Protokoll und der lange Verbindungskanal

Daher ist das Http1.0-Protokoll immer noch ein Halbduplex-Protokoll, da lange Verbindungen standardmäßig deaktiviert sind. Wenn Sie lange Verbindungen unterstützen müssen, müssen Sie dem http-Header ein Feld hinzufügen: „Verbindung: Keep-Alive“. ; Im Http 1.1-Protokoll sind lange Verbindungen standardmäßig aktiviert. Wenn Sie lange Verbindungen schließen müssen, müssen Sie das HTTP-Anforderungsheaderfeld hinzufügen: „Connection:close“.

Wann oder in welchem ​​Szenario müssen wir also lange Verbindungen verwenden? Tatsächlich ist es ganz einfach. Denken Sie nur an eines: Wenn in Geschäftsszenarien Anforderungen an die Unmittelbarkeit von Nachrichten bestehen, müssen Sie eine lange Verbindung mit dem Server herstellen, z. B. IM-Chat, Videoanrufe und andere Szenarien.

1.2 DNS-Auflösung

Wenn Partner im Projekt Trace-Protokolle zum Netzwerk hinzugefügt haben, sollten sie neben Timeout-Fehlern wie Net Timeout auch Ausnahmen wie UnknowHostException gesehen haben. Dies liegt daran, dass die DNS-Auflösung fehlgeschlagen ist und die IP-Adresse des Servers nicht abgerufen wurde durch Auflösung.

Wenn wir zu Hause sind, stellen unsere Mobiltelefone oder Computer eine Verbindung zum WLAN des Routers her und der Router kann die DNS-Serveradresse festlegen.

Wenn die Einstellungen jedoch falsch sind oder durch Angriffe manipuliert werden, schlägt die DNS-Auflösung fehl und die Netzwerkanforderungen unserer App sind abnormal. Daher müssen wir in diesem Fall unsere eigene DNS-Auflösungsstrategie hinzufügen.

Schauen wir uns zunächst ein Beispiel an. Angenommen, wir möchten einen Baidu-Domänennamen anfordern, um Daten zu erhalten, zum Beispiel:

object HttpUtil {

    private const val BASE_URL = "https://www.baidu.comxx"

    fun initHttp() {
        val client = OkHttpClient.Builder()
            .build()
        Request.Builder()
            .url(BASE_URL)
            .build().also {

                kotlin.runCatching {
                    client.newCall(it).execute()
                }.onFailure {
                    Log.e("OkHttp", "initHttp: error $it ")
                }

            }
    }
}

Offensichtlich ist der Domänenname von Baidu falsch, daher wird beim Ausführen einer Netzwerkanfrage ein Fehler gemeldet:

java.net.UnknownHostException: Unable to resolve host "www.baidu.comxx": No address associated with hostname

Sobald also unser Domainname gekapert und geändert wird, fällt der gesamte Dienst aus und die Benutzererfahrung wird schlecht. Daher können wir eine kleine Optimierung über den von OkHttp bereitgestellten benutzerdefinierten DNS-Resolver vornehmen.

public interface Dns {
  /**
   * A DNS that uses {@link InetAddress#getAllByName} to ask the underlying operating system to
   * lookup IP addresses. Most custom {@link Dns} implementations should delegate to this instance.
   */
  Dns SYSTEM = hostname -> {
    if (hostname == null) throw new UnknownHostException("hostname == null");
    try {
      return Arrays.asList(InetAddress.getAllByName(hostname));
    } catch (NullPointerException e) {
      UnknownHostException unknownHostException =
          new UnknownHostException("Broken system behaviour for dns lookup of " + hostname);
      unknownHostException.initCause(e);
      throw unknownHostException;
    }
  };

  /**
   * Returns the IP addresses of {@code hostname}, in the order they will be attempted by OkHttp. If
   * a connection to an address fails, OkHttp will retry the connection with the next address until
   * either a connection is made, the set of IP addresses is exhausted, or a limit is exceeded.
   */
  List<InetAddress> lookup(String hostname) throws UnknownHostException;
}

Werfen wir einen Blick auf den Quellcode. Die Suchmethode entspricht der DNS-Adressierung. Sobald eine Ausnahme auftritt, wird eine UnknownHostException-Ausnahme ausgelöst. Gleichzeitig wird intern auch eine SYSTEM-Methode definiert, in der die Routing-Adressierung durchgeführt wird über die vom System bereitgestellte InetAddress-Klasse. Auch wenn die DNS-Auflösung fehlschlägt, wird eine UnknownHostException ausgelöst.

Deshalb unternehmen wir zwei Schritte: Zuerst nutzen wir die Systemfunktionen für das Routing. Wenn dies fehlschlägt, verwenden wir eine benutzerdefinierte Strategie.

class MyDNS : Dns {


    override fun lookup(hostname: String): MutableList<InetAddress> {

        val result = mutableListOf<InetAddress>()
        var systemAddressList: MutableList<InetAddress>? = null
        //通过系统DNS解析
        kotlin.runCatching {
            systemAddressList = Dns.SYSTEM.lookup(hostname)
        }.onFailure {
            Log.e("MyDNS", "lookup: $it")
        }

        if (systemAddressList != null && systemAddressList!!.isNotEmpty()) {
            result.addAll(systemAddressList!!)
        } else {
            //系统DNS解析失败,走自定义路由
            result.add(InetAddress.getByName("www.baidu.com"))
        }

        return result
    }
}

Auf diese Weise wird nach dem Scheitern der Auflösung von www.baidu.comxx der Domänenname www.baidu.com als Ersatz verwendet, wodurch das Problem eines Netzwerkanforderungsfehlers vermieden wird.

1.3 Strategie zur Anpassung der Schnittstellendaten

Ich glaube, dass viele Partner beim Debuggen von Schnittstellen mit dem Server häufig auf diese Situation stoßen: Das Schnittstellendokument gibt an, dass dieses Feld vom Typ int ist und das Ergebnis die Zeichenfolge „“ ist. In einigen Fällen muss der Server ein leeres Array zurückgeben , aber null wird zurückgegeben. In diesem Fall schlägt das Parsen fehl, wenn wir die Daten analysieren, unabhängig davon, ob wir Gson oder Moshi verwenden. Bei unsachgemäßer Handhabung führt dies zu einem ernsthaften Absturz.

Um dieses Problem der Nichtübereinstimmung des Datenformats zu lösen, können wir einfach einige Anpassungen an Gson vornehmen, wie zum Beispiel den Listentyp:

class ListTypeAdapter : JsonDeserializer<List<*>> {
    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?
    ): List<*> {
        return try {
            if (json?.isJsonArray == true) {
                Gson().fromJson(json, typeOfT)
            } else {
                Collections.EMPTY_LIST
            }
        } catch (e: Exception) {
            //
            Collections.EMPTY_LIST
        }
    }
}

Wenn es sich bei json um Daten vom Typ „Listen-Array“ handelt, werden sie normalerweise in ein Listen-Array konvertiert. Wenn nicht, werden sie in ein leeres Array geparst.

class StringTypeAdapter : JsonDeserializer<String> {

    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?
    ): String {
        return try {
            if (json?.isJsonPrimitive == true) {
                Gson().fromJson(json, typeOfT)
            } else {
                ""
            }
        } catch (e: Exception) {
            ""
        }
    }
}

Bei Feldern vom Typ String wird zunächst beurteilt, ob es sich um einen Basistyp (String, Number, Boolean) handelt. Wenn es sich um einen Basistyp handelt, kann er normal konvertiert werden.

GsonBuilder()
    .registerTypeAdapter(Int::class.java, IntTypeAdapter())
    .registerTypeAdapter(String::class.java, StringTypeAdapter())
    .registerTypeAdapter(List::class.java, ListTypeAdapter())
    .create().also {
        GsonConverterFactory.create(it)
    }

Auf diese Weise können Sie beim Erstellen von GsonConverterFactory unsere Strategie zur Datenanpassung verwenden. In der Testumgebung empfehlen wir jedoch nicht, dies zu verwenden, da Probleme auf dem Server nicht gefunden werden können. Mit dieser Strategie können Sie Online-Probleme nach dem Gehen vermeiden online.

2 HTTPS-Protokoll

Der Unterschied zwischen dem http-Protokoll und dem https-Protokoll besteht darin, dass es ein zusätzliches „s“ gibt. Unterschätzen Sie dieses „s“ nicht. Es kann die Zuverlässigkeit der HTTP-Datenübertragung gewährleisten. Was ist also dieses „s“? Es ist das SSL/ TLS-Protokoll. .

Aus dem Bild oben geht hervor, dass das SSL/TLS-Protokoll verwendet wird, bevor das TCP-Protokoll eingegeben wird.

2.1 Symmetrische Verschlüsselung und asymmetrische Verschlüsselung

Da HTTPS die Zuverlässigkeit der Übertragung gewährleisten kann, bedeutet dies, dass es die Daten verschlüsselt. In der Vergangenheit wurden HTTP-Protokolldaten im Klartext übertragen und die Daten konnten leicht gestohlen und verfälscht werden. Daher wurden die Daten bei der anschließenden Optimierung verschlüsselt und übertragen. Das HTTPS-Protokoll war geboren.

Es gibt zwei gängige Verschlüsselungsmethoden: symmetrische Verschlüsselung und asymmetrische Verschlüsselung.

2.1.1 Symmetrische Verschlüsselung

Zunächst einmal handelt es sich um die symmetrische Verschlüsselung. Sie können das spezifische Prinzip anhand des Namens erkennen. Schauen Sie sich das Bild unten an:

Der Schlüssel für die symmetrische Ver- und Entschlüsselung ist ein Schlüssel, der von beiden Parteien vereinbart werden muss. Der Sender verschlüsselt die Daten mit dem geheimen Schlüssel, und der Empfänger verwendet denselben geheimen Schlüssel, um die übertragenen Daten zu entschlüsseln und zu erhalten.

Daher ist die Verwendung der symmetrischen Verschlüsselung sehr einfach und analysiert Daten schnell, die Sicherheit ist jedoch relativ gering, da sich beide Parteien auf denselben Schlüssel einigen müssen. Bei der Übertragung des Schlüssels besteht die Gefahr einer Entführung, und auch die einheitliche Speicherung ist gefährdet angegriffen werden.

Als Reaktion auf diese Situation entstand daher die asymmetrische Verschlüsselung.

2.1.2 Asymmetrische Verschlüsselung

Bei der asymmetrischen Verschlüsselung gibt es zwei Schlüssel: privaten Schlüssel + öffentlichen Schlüssel. Jeder kann den öffentlichen Schlüssel kennen. Der Sender kann den öffentlichen Schlüssel zum Verschlüsseln von Daten verwenden, und der Empfänger kann den privaten Schlüssel, den nur er kennt, zum Entschlüsseln und Erhalten der Daten verwenden.

Da also jeder den öffentlichen Schlüssel kennt, kann der private Schlüssel direkt vom öffentlichen Schlüssel abgeleitet werden? Die Antwort ist, dass es derzeit unmöglich ist, aber es könnte in der Zukunft möglich sein. Es hängt davon ab, ob Kryptografiemeister oder Hacker auf der ganzen Welt dieses Problem lösen können.

Fassen Sie die Vor- und Nachteile der beiden Verschlüsselungsmethoden zusammen:

Verschlüsselungstyp Vorteil Mangel
Symmetrische Verschlüsselung Der Vorgang ist einfach und die Entschlüsselungsgeschwindigkeit ist hoch Nicht sicher, die Schlüsselverwaltung ist riskant
asymmetrische Verschlüsselung Nur Sie kennen den privaten Schlüssel Der Vorgang ist umständlich und die Entschlüsselungsgeschwindigkeit ist langsam

2.2 Sicherheitsgarantie öffentlicher Schlüssel

Durch die Einführung der asymmetrischen Verschlüsselung in Abschnitt 2.1 scheint die Übertragung öffentlicher Schlüssel zwar sicherer zu sein, ist aber etwas zu ideal. Schauen wir uns das folgende Szenario an.

Wenn der öffentliche Schlüssel während der Übertragung gekapert wird, erhält der Absender den öffentlichen Schlüssel des Hackers und alle nachfolgenden Datenübertragungen werden gekapert. Es stellt sich also die Frage, wie sichergestellt werden kann, dass der vom Absender erhaltene öffentliche Schlüssel der öffentliche Schlüssel des Empfängers sein muss ?quadrat?

Nehmen wir ein einfaches Beispiel: Wir holen uns unterwegs eine Bankkarte und wollen darin das Geld abheben. Dann ist der Bankschalter eigentlich der Empfänger, und die Bankkarte ist der öffentliche Schlüssel. Dann gibt uns die Bank das Geld direkt ? ? Definitiv nicht, wir benötigen entweder einen Ausweis oder ein Passwort, um zu beweisen, dass die Bankkarte uns gehört, daher ist die Sicherheitsgarantie des öffentlichen Schlüssels das CA-Zertifikat (das als unser Ausweis verstanden werden kann).

Anschließend muss der Empfänger zunächst einen Personalausweis beantragen und über die CA-Agentur eine digitale Signatur generieren. Die spezifischen Generierungsregeln lauten wie folgt:

Was dann schließlich an den Empfänger gesendet wird, ist das folgende digitale Zertifikat, das enthält: digitale Signatur + öffentlicher Schlüssel + persönliche Informationen des Empfängers usw.

Nachdem der Absender das digitale Zertifikat erhalten hat, prüft er, ob das digitale Zertifikat legal ist. Die Erkennungsmethode ist wie folgt:

Wenn es sich nicht um ein gefälschtes Zertifikat handelt, ist die Wahrscheinlichkeit nahezu 0, da es einfach unmöglich ist, eine digitale Signatur eines Domänennamens zu fälschen, und die CA-Organisation ist nicht einfach umsonst . Solange das Zertifikat authentifiziert ist, ist es öffentlich Schlüssel kann Sicherheit garantiert werden.

2.3 HTTPS-Übertragungsprozess

Tatsächlich enthält eine HTTP-Anfrage zwei HTTP-Übertragungen. Wenn wir eine Anfrage stellen, www.baidu.comist der spezifische Prozess wie folgt:

(1) Der Client initiiert eine Anfrage an den Server, um auf Baidu zuzugreifen, und stellt dann zu diesem Zeitpunkt eine Verbindung mit dem Server von Baidu her.

(2) Zu diesem Zeitpunkt verfügt der Server über einen öffentlichen Schlüssel und einen privaten Schlüssel. Der öffentliche Schlüssel kann an den Client gesendet werden, und dann wird ein SSL-Zertifikat an den Client gesendet, das Folgendes enthält: CA-Signatur, öffentlicher Schlüssel usw Informationen von Baidu. Einzelheiten finden Sie in Abschnitt 2.2, letztes Bild;

(3) Nach Erhalt des SSL-Zertifikats entschlüsselt der Client die CA-Signatur, um festzustellen, ob das Zertifikat legal ist. Wenn es nicht legal ist, trennen Sie die Verbindung. Wenn es legal ist, generieren Sie eine Zufallszahl als Schlüssel für die symmetrische Verschlüsselung von Daten . Schlüssel, der durch Verschlüsselung mit einem öffentlichen Schlüssel an den Server gesendet wird.

(4) Nachdem der Server die verschlüsselten Daten des Clients empfangen hat, entschlüsselt er sie mithilfe des privaten Schlüssels und erhält den symmetrischen Verschlüsselungsschlüssel. Anschließend verschlüsselt er Baidu-bezogene Daten mithilfe des symmetrischen Verschlüsselungsschlüssels und sendet sie an den Client.

(5) Der Client erhält die Daten durch Entschlüsselung vom Server und die Anforderung endet.

Tatsächlich handelt es sich bei HTTPS-Anfragen nicht um eine vollständig asymmetrische Verschlüsselung, sondern um eine Kombination der Stärken jedes Unternehmens. Da die Übertragung symmetrischer Verschlüsselungsschlüssel riskant ist, werden die symmetrischen Verschlüsselungsschlüssel in der Frühphase durch asymmetrische Verschlüsselung übertragen, und die anschließende Datenübertragung erfolgt durch symmetrische Verschlüsselung Verschlüsselung, Verbesserung der Effizienz der Datenanalyse.

Was wir jedoch verstehen müssen, ist, dass HTTPS nur die Sicherheit beider Parteien in der Kommunikation schützt. Man-in-the-Middle-Angriffe wie die Paketerfassung durch Testpartner durch Charles bergen aufgrund gefälschter Zertifikate weiterhin das Risiko von Datenlecks oder nicht vertrauenswürdige Zertifizierungsstellen können dies erreichen.

Android-Studiennotizen

Hinweise zur OkHttp-Quellcode-Analyse: https://qr18.cn/Cw0pBD
Android-Leistungsoptimierung: https://qr18.cn/FVlo89
Android-Fahrzeugversion: https://qr18.cn/F05ZCM
Hinweise zur Android-Reverse-Security-Studie: https://qr18.cn/CQ5TcL
Grundprinzipien des Android-Frameworks: https://qr18.cn/AQpN4J
Android-Audio und -Video: https://qr18.cn/Ei3VPD
Jetpack-Familien-Bucket (einschließlich Compose): https://qr18.cn/A0gajp
Kotlin: https://qr18.cn/CdjtAF
Gradle: https://qr18.cn/DzrmMB
Flutter: https://qr18.cn/DIvKma
Acht Wissenskörper von Android: https://qr18.cn/CyxarU
Android-Kernnotizen: https://qr21.cn/CaZQLo
Android-Interviewfragen aus früheren Jahren: https://qr18.cn/CKV8OZ
Die neuesten Android-Interviewfragen im Jahr 2023: https://qr18.cn/CgxrRy
Android-Interviewübungen für Fahrzeugentwicklungspositionen: https://qr18.cn/FTlyCJ
Audio- und Videointerviewfragen:https://qr18.cn/AcV6Ap

Supongo que te gusta

Origin blog.csdn.net/weixin_61845324/article/details/132782128
Recomendado
Clasificación