Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
Hackforid committed Apr 13, 2018
0 parents commit b57dc29
Show file tree
Hide file tree
Showing 37 changed files with 731 additions and 0 deletions.
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
gen
bin
target
.idea
.settings
*.iml
.classpath
.project
out
classes
gen-external-apklibs
*.properties
.DS_Store
.gradle
build/
*.apk
*.hprof
gradle/
gradlew
gradlew.bat
infer-out/
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# QUIC

QUIC为Google于2013年开发的基于UDP的多路并发传输协议,主要优势在于减少TCP三次握手及TLS握手,同时因为UDP特性在高丢包环境下依旧能正常工作。在使用情景复杂的移动网络环境下能有效降低延时以及提高请求成功率。(https://www.chromium.org/quic)

## Bench

此处选择Android作为测试平台,对同时提供QUIC和HTTP2.0支持的腾讯云CDN图片进行下载测试。

**Server(CDN)**

```Kotlin
"https://stgwhttp2.kof.qq.com/1.jpg",
"https://stgwhttp2.kof.qq.com/2.jpg",
"https://stgwhttp2.kof.qq.com/3.jpg",
"https://stgwhttp2.kof.qq.com/4.jpg",
"https://stgwhttp2.kof.qq.com/5.jpg",
"https://stgwhttp2.kof.qq.com/6.jpg",
"https://stgwhttp2.kof.qq.com/7.jpg",
"https://stgwhttp2.kof.qq.com/8.jpg",
"https://stgwhttp2.kof.qq.com/01.jpg",
"https://stgwhttp2.kof.qq.com/02.jpg",
"https://stgwhttp2.kof.qq.com/03.jpg",
"https://stgwhttp2.kof.qq.com/04.jpg",
"https://stgwhttp2.kof.qq.com/05.jpg",
"https://stgwhttp2.kof.qq.com/06.jpg",
"https://stgwhttp2.kof.qq.com/07.jpg",
"https://stgwhttp2.kof.qq.com/08.jpg"
```

**Client:Android**
Android平台上,我们使用从`Chromium`中抽取的[cornet](https://chromium.googlesource.com/chromium/src/+/master/components/cronet?autodive=0%2F%2F)作为QUIC Client,对比`OKHttp`作为Http2.0 Client。为了避免OKHttp本身优化的问题,我们为QUIC提供了hook OKHttp用的`Interceptor`,此数数据均为QUIC over OKHttp的测试结果。直接使用cornet engine的测试结果大家可以自行运行。

### 丢包测试

1. 模拟正常3G环境 上下行相同 Delay 0ms,Rate 750k

![bench_3g](./doc/bench_3g.png)



1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
52 changes: 52 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 27
defaultConfig {
applicationId "com.smilehacker.quictest"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

ndk {
abiFilters 'armeabi-v7a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

implementation files('libs/cronet_api.jar')
implementation files('libs/cronet_impl_common_java.jar')
implementation files('libs/cronet_impl_native_java.jar')

implementation 'com.squareup.okio:okio:1.14.0'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'

// implementation 'com.packetzoom:pz-android-sdk:3.+'
// implementation 'com.packetzoom:pz-okhttp3-interceptor:3.2.+'
}
Binary file added app/libs/cronet_api.jar
Binary file not shown.
Binary file added app/libs/cronet_impl_common_java.jar
Binary file not shown.
Binary file added app/libs/cronet_impl_native_java.jar
Binary file not shown.
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.smilehacker.quictest

import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("com.smilehacker.quictest", appContext.packageName)
}
}
24 changes: 24 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.smilehacker.quictest" >

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
27 changes: 27 additions & 0 deletions app/src/main/java/com/smilehacker/quictest/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.smilehacker.quictest;

import android.app.Application;


/**
* Created by quan.zhou on 2017/8/5.
*/

public class App {
public static Application INSTANCE ;//= AppGlobals.getInitialApplication();
static {
Application app = null;
try {
app = (Application) Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null);
if (app == null)
throw new IllegalStateException("Static initialization of Applications must be on main thread.");
} catch (final Exception e) {
try {
app = (Application) Class.forName("android.app.ActivityThread").getMethod("currentApplication").invoke(null);
} catch (final Exception ex) {
}
} finally {
INSTANCE = app;
}
}
}
174 changes: 174 additions & 0 deletions app/src/main/java/com/smilehacker/quictest/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package com.smilehacker.quictest

import android.os.AsyncTask
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.widget.Button
import android.widget.TextView
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.Okio
import org.chromium.net.CronetEngine
import java.io.File
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.TimeUnit


class MainActivity : AppCompatActivity() {

private lateinit var mEngine: CronetEngine
private lateinit var mOkhttpClent: OkHttpClient
private lateinit var mOkhttpClentWithQUIC: OkHttpClient

private val mTvResultHttp by lazy { findViewById<TextView>(R.id.tv_http_result) }
private val mTvResultQuic by lazy { findViewById<TextView>(R.id.tv_quic_result) }
private val mTvResultQuicOverOKHttp by lazy { findViewById<TextView>(R.id.tv_quic_ok_result) }

companion object {
val TAG = MainActivity::class.java.simpleName


const val TEST_TYPE_QUIC = 1
const val TEST_TYPE_HTTP = 2
const val TEST_TYPE_QUIC_OVER_OKHTTP = 3
}

private val IMGS = arrayOf(
"https://stgwhttp2.kof.qq.com/1.jpg",
"https://stgwhttp2.kof.qq.com/2.jpg",
"https://stgwhttp2.kof.qq.com/3.jpg",
"https://stgwhttp2.kof.qq.com/4.jpg",
"https://stgwhttp2.kof.qq.com/5.jpg",
"https://stgwhttp2.kof.qq.com/6.jpg",
"https://stgwhttp2.kof.qq.com/7.jpg",
"https://stgwhttp2.kof.qq.com/8.jpg",
"https://stgwhttp2.kof.qq.com/01.jpg",
"https://stgwhttp2.kof.qq.com/02.jpg",
"https://stgwhttp2.kof.qq.com/03.jpg",
"https://stgwhttp2.kof.qq.com/04.jpg",
"https://stgwhttp2.kof.qq.com/05.jpg",
"https://stgwhttp2.kof.qq.com/06.jpg",
"https://stgwhttp2.kof.qq.com/07.jpg",
"https://stgwhttp2.kof.qq.com/08.jpg"
)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

mEngine = CronetEngine.Builder(applicationContext)
.enableQuic(true)
.build()

mOkhttpClentWithQUIC = OkHttpClient.Builder()
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.cache(null)
.addInterceptor(QUICInterceptor())
.build()
mOkhttpClent = OkHttpClient.Builder()
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.cache(null)
.build()

findViewById<Button>(R.id.btn_quic_test).setOnClickListener {
doTest(TEST_TYPE_QUIC)
}

findViewById<Button>(R.id.btn_http_test).setOnClickListener {
doTest(TEST_TYPE_HTTP)
}
findViewById<Button>(R.id.btn_quic_ok_test).setOnClickListener {
doTest(TEST_TYPE_QUIC_OVER_OKHTTP)
}
}

private fun doTest(type: Int) {
object : AsyncTask<Void, Void, Long>() {
override fun doInBackground(vararg params: Void?): Long {
Log.i(TAG, "start test:")
var cost = 0L
for (i in 0 until IMGS.size) {
try {
cost += when(type) {

TEST_TYPE_HTTP -> okhttpJob(mOkhttpClent, IMGS[i])
TEST_TYPE_QUIC -> quicJob(IMGS[i])
TEST_TYPE_QUIC_OVER_OKHTTP -> okhttpJob(mOkhttpClentWithQUIC, IMGS[i])
else -> 0
}
} catch (e : Throwable) {
Log.e(TAG, "job error", e)
}
}

return cost
}

override fun onPostExecute(result: Long) {
super.onPostExecute(result)
val label = "download img ${IMGS.size} times and cost: $result ms"
when(type) {

TEST_TYPE_HTTP -> mTvResultHttp.text = label
TEST_TYPE_QUIC -> mTvResultQuic.text = label
TEST_TYPE_QUIC_OVER_OKHTTP -> mTvResultQuicOverOKHttp.text = label
}

}

}.execute()
}

private fun quicJob(img: String) : Long {

val time = System.currentTimeMillis()


val connection = mEngine.openConnection(URL(img)) as HttpURLConnection

// can also hook system default
// URL.setURLStreamHandlerFactory(mEngine.createURLStreamHandlerFactory());

connection.requestMethod = "GET"
connection.connect()

val source = Okio.buffer(Okio.source(connection.inputStream))
val downloadedFile = File(this.externalCacheDir, "a.jpg")
val sink = Okio.buffer(Okio.sink(downloadedFile))
sink.writeAll(source)
sink.close()
source.close()

val cost = System.currentTimeMillis() - time
Log.i(TAG, "download complete $img cost = ${System.currentTimeMillis() - time}")

return cost
}

private fun okhttpJob(client: OkHttpClient, img: String) : Long {
val time = System.currentTimeMillis()

val req = Request.Builder()
.url(img)
.build()

val resp = client.newCall(req).execute()

val source = resp.body()!!.source()
val downloadedFile = File(this.externalCacheDir, "b.jpg")
val sink = Okio.buffer(Okio.sink(downloadedFile))
sink.writeAll(source)
sink.close()
source.close()

val cost = System.currentTimeMillis() - time
Log.i(TAG, "download complete $img cost = ${System.currentTimeMillis() - time}")
return cost
}
}
Loading

0 comments on commit b57dc29

Please sign in to comment.