亚洲有码Av一区二区三区_国产高清啪啪免费视频_69色视频国产_国产成人人人爆出白浆_国产精品自在线拍国_一本久久伊人热热精品无码_午夜性刺激在线看免费带字幕_助力高品质欧美狂喷水_亚洲精品日韩无码_精品无码一区二区三区蜜臀_麻豆高清国产AV_熟妇人素无码中文字幕_亚洲a级片在线观看_国产欧美日韩三区_99国产成人高清在线观看

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營(yíng)的一線實(shí)戰(zhàn)洞察。

基于TCP的Socket文件上傳實(shí)戰(zhàn):協(xié)議設(shè)計(jì)與粘包處理

基于TCP的Socket文件上傳實(shí)戰(zhàn):協(xié)議設(shè)計(jì)與粘包處理 最近在幫朋友整理內(nèi)部服務(wù)器之間的日志同步方案時(shí)遇到一個(gè)非常典型的場(chǎng)景兩臺(tái)機(jī)器在同一內(nèi)網(wǎng)里需要把幾個(gè)GB的數(shù)據(jù)包從一臺(tái)傳到另一臺(tái)FTP服務(wù)沒(méi)搭、專門的HTTP上傳服務(wù)也沒(méi)人維護(hù)臨時(shí)裝個(gè)網(wǎng)盤工具又不現(xiàn)實(shí)。最后又繞回老路子——直接用TCP通信自己寫一個(gè)文件上傳工具。這篇文章就把這個(gè)綜合案例完整拆開(kāi)講清楚從應(yīng)用層協(xié)議設(shè)計(jì)、服務(wù)端實(shí)現(xiàn)、客戶端實(shí)現(xiàn)到實(shí)測(cè)過(guò)程中踩過(guò)的坑和進(jìn)階思路覆蓋到Socket編程里最容易被忽略、但實(shí)際開(kāi)發(fā)又一定會(huì)遇到的細(xì)節(jié)。無(wú)論你是剛開(kāi)始學(xué)網(wǎng)絡(luò)編程的學(xué)生還是工作中突然要處理內(nèi)網(wǎng)文件傳輸?shù)拈_(kāi)發(fā)者照著這套邏輯都能快速落地一套可靠的方案。1. 動(dòng)手前先想清楚這個(gè)案例到底在解決什么問(wèn)題1.1 既要傳得對(duì)又要傳得完文件上傳這件事聽(tīng)上去很簡(jiǎn)單但放到TCP通信的場(chǎng)景里難點(diǎn)立刻浮出水面。TCP本身只保證字節(jié)流是可靠有序的它并不理解文件這種業(yè)務(wù)概念。你往Socket里寫一段字節(jié)對(duì)端讀到的可能是半段數(shù)據(jù)也可能是兩段數(shù)據(jù)黏在一起。所以這個(gè)綜合案例真正要解決的是在TCP提供的原始字節(jié)流之上設(shè)計(jì)一套能讓雙方都理解的消息協(xié)議把文件的名字、大小、內(nèi)容、校驗(yàn)信息完整地送達(dá)對(duì)端。另一個(gè)容易忽略的痛點(diǎn)是傳得完。小文件一次性讀進(jìn)內(nèi)存再write很快就完事但到了幾個(gè)GB的日志文件內(nèi)存根本扛不住必須用緩沖流分塊讀取、分塊發(fā)送。再加上網(wǎng)絡(luò)抖動(dòng)、客戶端斷線、服務(wù)端磁盤寫入失敗這些情況一個(gè)看起來(lái)簡(jiǎn)單的上傳工具實(shí)際上要把連接管理、流處理、異?;謴?fù)、資源釋放全部串起來(lái)。1.2 為什么這個(gè)場(chǎng)景必須選TCP而不是UDP文件上傳在傳輸層幾乎沒(méi)有選擇的余地必須用TCP。UDP雖然發(fā)送快、沒(méi)有連接維護(hù)成本但它不保證數(shù)據(jù)包的到達(dá)順序也不保證不丟包。如果基于UDP做文件傳輸要么自己實(shí)現(xiàn)一套復(fù)雜的確認(rèn)重傳機(jī)制要么接受文件內(nèi)容可能損壞的后果。相比之下TCP把擁塞控制、超時(shí)重傳、順序校驗(yàn)這些機(jī)制全部?jī)?nèi)置了你要做的只是在流上面劃分消息邊界已經(jīng)省去了最麻煩的部分。不過(guò)這也引出一個(gè)很多新手會(huì)犯的錯(cuò)誤覺(jué)得TCP可靠就可以把文件名、文件大小、文件內(nèi)容一次性write出去然后以為對(duì)端一定能原樣收到。這個(gè)理解是錯(cuò)的——TCP的可靠是字節(jié)層面的可靠不是消息層面的可靠。發(fā)送方一次write比如write 1MB數(shù)據(jù)接收方可能分好幾次read才能讀完發(fā)送方連續(xù)write兩次接收方可能一次read就把兩段數(shù)據(jù)一起讀出來(lái)了。這就是后面要重點(diǎn)處理的粘包、拆包問(wèn)題也是整個(gè)協(xié)議設(shè)計(jì)的最核心動(dòng)機(jī)。1.3 邊界判斷什么時(shí)候該自研什么時(shí)候該用現(xiàn)成方案在做這個(gè)工具之前我也認(rèn)真想過(guò)是不是直接用成熟方案更省事。如果只是偶爾在內(nèi)網(wǎng)傳幾個(gè)文件SFTP、NFS、HTTP文件服務(wù)甚至直接掛個(gè)網(wǎng)盤都比自己寫Socket要快。但如果你面對(duì)的是批量機(jī)器之間的程序化傳輸、需要在傳輸過(guò)程中嵌入特殊的業(yè)務(wù)校驗(yàn)比如上傳后觸發(fā)后續(xù)任務(wù)、或者處于一個(gè)不允許額外安裝服務(wù)的受限環(huán)境自己實(shí)現(xiàn)一個(gè)輕量TCP上傳通道反而是更理智的選擇。自研方案的邊界也很明確單機(jī)吞吐、并發(fā)連接數(shù)、斷點(diǎn)續(xù)傳這些能力都取決于你自己的設(shè)計(jì)和代碼質(zhì)量出了問(wèn)題沒(méi)有現(xiàn)成文檔能查全靠對(duì)TCP機(jī)制的理解。所以這個(gè)案例的定位不是替代FTP而是一種在可控場(chǎng)景下靈活定制的技術(shù)方案。理解清楚這一點(diǎn)你才知道哪些協(xié)議字段要考慮、哪些異常分支要處理程序才不會(huì)寫到一半失控。2. 應(yīng)用層協(xié)議設(shè)計(jì)先把字節(jié)流翻譯成消息2.1 協(xié)議頭結(jié)構(gòu)固定頭部加變長(zhǎng)字段TCP是流協(xié)議沒(méi)有天然的消息邊界所以應(yīng)用層必須自己定義幀。我的做法是設(shè)計(jì)一個(gè)固定長(zhǎng)度的協(xié)議頭后面跟著文件名和文件內(nèi)容。固定頭部長(zhǎng)度的好處是接收方可以先讀固定字節(jié)解析出各個(gè)字段的寬度再按需讀取后續(xù)內(nèi)容。我設(shè)計(jì)的協(xié)議頭格式如下字段類型字節(jié)數(shù)說(shuō)明魔數(shù)int4固定為0x4D41494E用于快速識(shí)別合法連接協(xié)議版本int4當(dāng)前為1方便后續(xù)協(xié)議演進(jìn)文件名長(zhǎng)度int4文件名字節(jié)長(zhǎng)度UTF-8編碼后文件大小long8文件內(nèi)容的總字節(jié)數(shù)支持超過(guò)2GB的文件上傳模式int41覆蓋、2追加、3拒絕默認(rèn)1MD5摘要byte[16]16整個(gè)文件內(nèi)容的MD5值用于完整性校驗(yàn)固定頭部一共40字節(jié)。文件名是變長(zhǎng)字段跟在固定頭部之后隨后才是文件內(nèi)容。接收方拿到前40字節(jié)后解析出文件名長(zhǎng)度再精確讀取文件名最后根據(jù)文件大小循環(huán)讀取剩余內(nèi)容。這里有個(gè)經(jīng)驗(yàn)文件名長(zhǎng)度用int而不是short雖然實(shí)際場(chǎng)景里文件名幾乎不會(huì)超過(guò)65535字節(jié)但用int可以避免一個(gè)很隱蔽的邊界問(wèn)題。Java的DataInputStream.readUTF內(nèi)部就是用short存長(zhǎng)度的文件名稍微長(zhǎng)一點(diǎn)就拋UTFDataFormatException排查起來(lái)很費(fèi)勁。既然協(xié)議是自己設(shè)計(jì)的就不要把這種限制帶進(jìn)來(lái)。2.2 粘包與拆包的解決思路定長(zhǎng)頭加變長(zhǎng)體粘包接收方一次讀到了兩條消息的內(nèi)容和拆包一條消息被分成多次讀取是TCP編程的經(jīng)典問(wèn)題。網(wǎng)上??吹絻煞N解法一種是約定固定長(zhǎng)度消息不足補(bǔ)位另一種是使用特殊的結(jié)束符。固定長(zhǎng)度協(xié)議浪費(fèi)帶寬結(jié)束符方案又要求文件內(nèi)容里不能出現(xiàn)結(jié)束符都不太適合文件傳輸。最實(shí)用的還是定長(zhǎng)頭加變長(zhǎng)體的思路接收方先把40字節(jié)的頭部讀完整通過(guò)readFully保證讀取的字節(jié)數(shù)恰好等于頭部長(zhǎng)度然后根據(jù)頭部里的文件名長(zhǎng)度、文件大小再精確讀取對(duì)應(yīng)字節(jié)數(shù)的數(shù)據(jù)。因?yàn)槊看巫x取的量都有明確數(shù)字約束即使消息在底層被拆成了多個(gè)TCP包接收方也只需要循環(huán)讀到指定字節(jié)數(shù)即可粘包也不會(huì)影響后續(xù)解析——因?yàn)槊織l消息的長(zhǎng)度是確定的剩余緩沖區(qū)里的字節(jié)自然屬于下一條消息。在Java里實(shí)現(xiàn)這一點(diǎn)很簡(jiǎn)單使用DataInputStream.readFully(byte[])比反復(fù)調(diào)用read()可靠得多。它內(nèi)部幫你循環(huán)讀取直到讀滿指定長(zhǎng)度或者流已經(jīng)結(jié)束。很多初學(xué)Socket的代碼里喜歡這么寫byte[] header new byte[40]; in.read(header);這個(gè)寫法是有隱患的read不保證一次讀滿40字節(jié)。必須改成byte[] header new byte[40]; in.readFully(header);不要小看這個(gè)差異實(shí)測(cè)中普通局域網(wǎng)環(huán)境還好跨交換機(jī)、跨網(wǎng)段傳輸時(shí)read一次返回不滿的情況非常常見(jiàn)這也是為什么我的接收數(shù)據(jù)總是缺一截的高頻根源。2.3 三個(gè)容易被忽略的協(xié)議細(xì)節(jié)第一個(gè)細(xì)節(jié)是文件名的編碼問(wèn)題。文件名在傳輸前用UTF-8編碼成字節(jié)數(shù)組長(zhǎng)度是字節(jié)數(shù)組的長(zhǎng)度而不是字符串的字符數(shù)。接收方讀出字節(jié)數(shù)組后用UTF-8解碼恢復(fù)文件名。如果不統(tǒng)一編碼發(fā)送端用GBK、接收端按UTF-8解析中文文件名必然亂碼服務(wù)端寫出去的文件名也會(huì)出問(wèn)題。第二個(gè)細(xì)節(jié)是目錄穿越。協(xié)議里收到的是客戶端發(fā)來(lái)的文件名服務(wù)端如果直接拿它拼路徑比如new File(baseDir, fileName)一旦fileName里包含../服務(wù)端的文件就會(huì)寫到baseDir以外這是嚴(yán)重的安全漏洞。服務(wù)端必須對(duì)文件名做凈化處理最簡(jiǎn)單的方式是取文件名中的最后一個(gè)路徑分隔符之后的部分或者用白名單字符集過(guò)濾。第三個(gè)細(xì)節(jié)是響應(yīng)反饋。協(xié)議不能只允許客戶端向服務(wù)端發(fā)送服務(wù)端也需要告訴客戶端上傳結(jié)果。所以客戶端發(fā)完文件內(nèi)容后調(diào)用shutdownOutput進(jìn)入半關(guān)閉狀態(tài)讓服務(wù)端讀到文件流的末尾隨后服務(wù)端寫一個(gè)int類型的狀態(tài)碼比如0表示成功1表示失敗2表示校驗(yàn)錯(cuò)誤回送給客戶端。這里的半關(guān)閉是關(guān)鍵它告訴TCP我的發(fā)送方向已經(jīng)結(jié)束但連接仍然可以用來(lái)接收數(shù)據(jù)。3. 服務(wù)端實(shí)現(xiàn)從accept到落盤的完整鏈路3.1 服務(wù)端骨架ServerSocket加線程池服務(wù)端要做的事情很簡(jiǎn)單監(jiān)聽(tīng)端口、接受連接、為每個(gè)連接分配合適的處理器。但簡(jiǎn)單的背后有幾處要注意。一是監(jiān)聽(tīng)的端口要設(shè)置合理過(guò)高如65535在生產(chǎn)環(huán)境容易被防火墻攔截過(guò)低如1024以下又需要權(quán)限。案例里我用9999這種高位端口。二是處理方式是單線程還是多線程如果只有一個(gè)客戶端傳文件單線程也夠用但實(shí)際環(huán)境里總會(huì)有多個(gè)上傳任務(wù)所以我在最外層放ServerSocket每拿到一個(gè)Socket就交給一個(gè)獨(dú)立的線程去處理。ServerSocket serverSocket new ServerSocket(9999); ExecutorService pool Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); while (!Thread.currentThread().isInterrupted()) { Socket socket serverSocket.accept(); socket.setTcpNoDelay(true); pool.submit(() - handleClient(socket)); }Executors.newFixedThreadPool在線程數(shù)量上可以控制并發(fā)上限避免因?yàn)榇罅可蟼髡?qǐng)求導(dǎo)致服務(wù)端創(chuàng)建上千個(gè)線程直接打爆內(nèi)存。生產(chǎn)環(huán)境里我一般推薦用ThreadPoolExecutor手動(dòng)指定核心線程數(shù)和隊(duì)列容量但在教學(xué)案例里固定線程池夠用規(guī)則也簡(jiǎn)單。setTcpNoDelay(true)也值得說(shuō)明它關(guān)閉了Nagle算法。Nagle算法會(huì)把小的數(shù)據(jù)包攢在一起發(fā)送以減少網(wǎng)絡(luò)報(bào)文數(shù)量但對(duì)于文件上傳這種需要頻繁寫入小塊數(shù)據(jù)的場(chǎng)景Nagle帶來(lái)的延遲會(huì)讓交互式確認(rèn)變得拖沓。文件上傳更看重吞吐關(guān)閉Nagle后小包的發(fā)送延遲明顯降低。3.2 協(xié)議解析與文件寫入的核心邏輯每個(gè)客戶端連接進(jìn)來(lái)后真正的核心邏輯是固定的幾步讀固定頭部、讀文件名、按文件大小循環(huán)讀文件體、寫磁盤、校驗(yàn)、回送結(jié)果。private static final int HEADER_SIZE 40; private static final int MAGIC 0x4D41494E; private void handleClient(Socket socket) { String filePath null; try (DataInputStream in new DataInputStream(new BufferedInputStream(socket.getInputStream())); DataOutputStream out new DataOutputStream(socket.getOutputStream())) { byte[] headerBytes new byte[HEADER_SIZE]; in.readFully(headerBytes); ByteBuffer header ByteBuffer.wrap(headerBytes).order(ByteOrder.BIG_ENDIAN); int magic header.getInt(); int version header.getInt(); int nameLen header.getInt(); long fileSize header.getLong(); int mode header.getInt(); byte[] expectMd5 new byte[16]; header.get(expectMd5); if (magic ! MAGIC) { out.writeInt(STATUS_PROTOCOL_ERROR); return; } byte[] nameBytes new byte[nameLen]; in.readFully(nameBytes); String fileName new String(nameBytes, StandardCharsets.UTF_8); String safeName sanitizeFileName(fileName); File target resolvePath(safeName, mode); if (target null) { out.writeInt(STATUS_EXISTS); return; } filePath target.getAbsolutePath(); long totalRead 0; try (FileOutputStream fos new FileOutputStream(target); BufferedOutputStream bos new BufferedOutputStream(fos)) { byte[] buffer new byte[64 * 1024]; while (totalRead fileSize) { int need (int) Math.min(buffer.length, fileSize - totalRead); int len in.read(buffer, 0, need); if (len -1) break; bos.write(buffer, 0, len); totalRead len; } bos.flush(); } if (totalRead ! fileSize) { out.writeInt(STATUS_INCOMPLETE); return; } byte[] actualMd5 md5(Paths.get(filePath)); if (!MessageDigest.isEqual(expectMd5, actualMd5)) { out.writeInt(STATUS_MD5_ERROR); return; } out.writeInt(STATUS_OK); } catch (IOException e) { if (filePath ! null) { new File(filePath).delete(); } e.printStackTrace(); } }一定要注意文件體的讀取循環(huán)。因?yàn)閕n.read(buffer, 0, need)在一次調(diào)用中不一定能把need個(gè)字節(jié)全部讀滿所以循環(huán)條件是基于totalRead fileSize每個(gè)分片按剩余字節(jié)數(shù)取need這樣既避免讀了下一幀的數(shù)據(jù)如果還有的話也保證了大文件能被完整讀完。3.3 路徑處理與服務(wù)端的安全考慮sanitizeFileName和resolvePath這兩個(gè)方法雖然代碼不多卻是服務(wù)端最值得花心思的地方。sanitizeFileName的邏輯是把傳入文件名的所有路徑分隔符替換為下劃線或者提取最后一個(gè)分隔符后面的部分。因?yàn)槲募膩?lái)源是網(wǎng)絡(luò)對(duì)端永遠(yuǎn)不能信任。同時(shí)還要過(guò)濾掉Windows下不合法字符比如:/\\|?*否則服務(wù)端落在Windows目錄時(shí)文件寫入會(huì)拋IOException。resolvePath則處理上傳模式。如果模式是覆蓋直接返回baseDir下的目標(biāo)文件如果是追加返回目標(biāo)文件并準(zhǔn)備好以追加方式寫入如果是拒絕且文件已存在就返回null通知客戶端上傳被拒絕?;具壿嬍莗rivate File resolvePath(String fileName, int mode) { File baseDir new File(System.getProperty(user.home), uploads); if (!baseDir.exists()) baseDir.mkdirs(); File target new File(baseDir, fileName); if (mode MODE_APPEND) return target; if (mode MODE_REFUSE target.exists()) return null; return target; }這個(gè)邏輯的好處是覆蓋模式天然支持重復(fù)上傳適合批處理場(chǎng)景拒絕模式則避免了不小心覆蓋重要文件。你完全可以根據(jù)自己場(chǎng)景調(diào)整但安全邊界一定不能省。3.4 完整性校驗(yàn)與異常清理MD5校驗(yàn)放在文件全部寫入磁盤之后執(zhí)行。理論上應(yīng)該在內(nèi)存里邊收邊算摘要節(jié)省一遍文件的完整讀取但這樣對(duì)內(nèi)存的占用太高大文件場(chǎng)景不可行。案例里選擇的是接收完成后對(duì)落盤文件重新計(jì)算MD5再與協(xié)議頭攜帶的預(yù)期MD5比較。代價(jià)是文件會(huì)被讀第二遍增加了耗時(shí)好處是邏輯簡(jiǎn)單、可靠性高。如果文件特別大可以折算一下耗時(shí)能接受就用不能接受就把校驗(yàn)機(jī)制降級(jí)為只校驗(yàn)文件大小。校驗(yàn)為什么重要我在一開(kāi)始就強(qiáng)調(diào)TCP是可靠傳輸?shù)玊CP的可靠不等于端到端的內(nèi)容不變。中間設(shè)備改寫、服務(wù)端磁盤壞道、程序緩存沒(méi)刷干凈都可能導(dǎo)致落盤文件和原始文件不一致。有了MD5對(duì)比至少能發(fā)現(xiàn)絕大多數(shù)數(shù)據(jù)損壞的情況。也不要忘了totalRead ! fileSize這種最基本的判斷在客戶端還沒(méi)發(fā)完就斷開(kāi)連接時(shí)文件往往只有原始大小的一部分這時(shí)候必須把半成品文件刪除并回送錯(cuò)誤碼。4. 客戶端實(shí)現(xiàn)連接、按協(xié)議發(fā)送、等待反饋4.1 客戶端骨架與連接超時(shí)設(shè)置客戶端的實(shí)現(xiàn)和服務(wù)端恰好互逆它的職責(zé)是把本地文件轉(zhuǎn)換成協(xié)議頭加文件體的字節(jié)流發(fā)送到服務(wù)端然后等待確認(rèn)。代碼的第一步是建立連接。這個(gè)步驟有幾個(gè)常見(jiàn)的坑。用的是Socket()無(wú)參構(gòu)造加上connect方法可以在建立連接前設(shè)置超時(shí)時(shí)間Socket socket new Socket(); socket.connect(new InetSocketAddress(host, port), 5000); socket.setSoTimeout(30000);connect方法的第二個(gè)參數(shù)是連接超時(shí)毫秒。不設(shè)置的話new Socket(host, port)遇到不可達(dá)地址時(shí)會(huì)卡很久用戶體驗(yàn)極差。setSoTimeout(30000)設(shè)置的是讀超時(shí)也就是等待服務(wù)端響應(yīng)結(jié)果時(shí)的最大阻塞時(shí)間防止服務(wù)端一直不返回導(dǎo)致客戶端線程永久掛掉。這兩個(gè)超時(shí)是網(wǎng)絡(luò)編程里最容易漏掉、但生產(chǎn)環(huán)境又一定必須加的配置。4.2 大文件分塊發(fā)送不要一次性讀進(jìn)內(nèi)存很多初學(xué)者的代碼喜歡這樣寫byte[] data Files.readAllBytes(file.toPath()); out.write(data);這種寫法處理幾MB的小文件問(wèn)題不大但文件一旦到了幾百M(fèi)B甚至幾GBreadAllBytes會(huì)直接把內(nèi)存撐爆。正確做法是分塊讀取并循環(huán)發(fā)送建議的緩沖區(qū)大小是64KB到1MB之間。實(shí)際傳輸中我習(xí)慣用64KB這是一個(gè)在性能和內(nèi)存占用之間比較平衡的值。緩沖區(qū)太小系統(tǒng)調(diào)用次數(shù)變多吞吐上不去緩沖區(qū)太大單個(gè)TCP分片也裝不下反而增加內(nèi)存壓力。try (FileInputStream fis new FileInputStream(file); BufferedInputStream bis new BufferedInputStream(fis)) { byte[] buffer new byte[64 * 1024]; int len; long sent 0; while ((len bis.read(buffer)) ! -1) { out.write(buffer, 0, len); sent len; long percent sent * 100 / fileSize; if (percent lastPercent 5) { System.out.printf(已發(fā)送%d / %d%d%%%n, sent, fileSize, percent); lastPercent percent; } } out.flush(); }out.write(buffer, 0, len)里的len來(lái)自bis.read的返回值這個(gè)返回值決定了本次寫入的實(shí)際字節(jié)數(shù)。絕大多數(shù)情況下一次read返回的量不會(huì)超過(guò)64KB所以out.write一次也會(huì)把這個(gè)緩沖區(qū)的數(shù)據(jù)盡可能推入Socket發(fā)送緩沖區(qū)。由于網(wǎng)絡(luò)擁塞write本身可能導(dǎo)致線程阻塞但這是正常的流量控制不需要額外處理。4.3 半關(guān)閉連接與讀取服務(wù)端響應(yīng)文件內(nèi)容發(fā)送完畢后客戶端不能直接調(diào)用close關(guān)閉連接因?yàn)槿绻B接直接關(guān)閉服務(wù)端可能還沒(méi)有機(jī)會(huì)把處理結(jié)果回傳客戶端就收不到了。正確做法是調(diào)用shutdownOutput()這會(huì)讓TCP連接進(jìn)入半關(guān)閉狀態(tài)告訴服務(wù)端我的發(fā)送方向已經(jīng)結(jié)束但仍然保留接收方向可以繼續(xù)等待服務(wù)端返回結(jié)果。socket.shutdownOutput(); DataInputStream din new DataInputStream(socket.getInputStream()); int status din.readInt(); if (status STATUS_OK) { System.out.println(上傳成功服務(wù)端已校驗(yàn)MD5一致); } else { System.out.println(上傳失敗狀態(tài)碼 status); }這里服務(wù)端也要配合好。服務(wù)端讀取到文件流末尾read返回-1后才會(huì)寫結(jié)果給客戶端。如果數(shù)據(jù)還沒(méi)讀完就寫結(jié)果客戶端那邊會(huì)因?yàn)樵谕粋€(gè)Socket里先收到了結(jié)果字節(jié)而把結(jié)果誤當(dāng)成文件內(nèi)容的一部分導(dǎo)致協(xié)議錯(cuò)亂。所以服務(wù)端一定要保證文件內(nèi)容讀取循環(huán)結(jié)束后再調(diào)用out.writeInt(status)。有一個(gè)小細(xì)節(jié)是客戶端這邊的DataInputStream也需要設(shè)置超時(shí)。我在前面設(shè)置了setSoTimeout(30000)這樣即使服務(wù)端因?yàn)楫惓?ㄔ谖募x取階段客戶端最多等30秒就會(huì)收到SocketTimeoutException不會(huì)永遠(yuǎn)阻塞下去。4.4 進(jìn)度感知與回滾提示進(jìn)度反饋并不是協(xié)議的一部分但卻是用戶體感的重要部分。一個(gè)上傳任務(wù)跑十分鐘沒(méi)有進(jìn)度提示用戶的第一反應(yīng)是程序卡死了。我這邊實(shí)現(xiàn)的方式是每發(fā)送5%就打印一次日志同時(shí)記錄最后的百分比避免重復(fù)打印。日志的粒度沒(méi)必要太細(xì)5%已經(jīng)足夠。如果打印太頻繁大量日志輸出本身也會(huì)消耗CPU和磁盤IO。實(shí)測(cè)下來(lái)5%到10%的粒度最舒服。進(jìn)度日志的另一個(gè)作用是可以作為故障排查的線索。如果一次上傳卡住了日志停留在某個(gè)百分比位置不動(dòng)你就能判斷問(wèn)題出在哪個(gè)階段是發(fā)送過(guò)程中網(wǎng)絡(luò)斷開(kāi)還是服務(wù)端處理卡住還是等待響應(yīng)超時(shí)??床坏竭M(jìn)度時(shí)排查這種問(wèn)題只能靠猜體驗(yàn)完全不一樣。5. 實(shí)測(cè)踩坑有些問(wèn)題不跑一遍大數(shù)據(jù)根本想不到5.1 粘包半包的真實(shí)復(fù)現(xiàn)案例剛跑通的時(shí)候我用一個(gè)100MB的文件測(cè)試一切正常。然后換成一個(gè)1.2GB的文件第一次傳輸就出了問(wèn)題服務(wù)端接收文件到一定大小后拋異常提示文件提前結(jié)束。排查后發(fā)現(xiàn)客戶端在發(fā)送文件內(nèi)容的同時(shí)我把服務(wù)端的響應(yīng)邏輯寫在了文件內(nèi)容讀取完成之前。服務(wù)端一旦讀了幾個(gè)字節(jié)作為響應(yīng)看待整個(gè)消息結(jié)構(gòu)就亂了。這個(gè)坑的本質(zhì)是TCP的字節(jié)流沒(méi)有邊界如果你在接收文件內(nèi)容時(shí)還試圖從同一個(gè)輸入流里提前讀響應(yīng)那讀到的其實(shí)是文件內(nèi)容的一部分。根源是因?yàn)槲募?nèi)容很大在底層會(huì)被拆成多個(gè)TCP包發(fā)送read第一次拿到的可能只有一部分導(dǎo)致代碼邏輯把后面的某些字節(jié)誤判為標(biāo)志位。解決方式也很簡(jiǎn)單嚴(yán)格執(zhí)行協(xié)議順序。文件內(nèi)容的讀取循環(huán)必須跑完totalRead fileSize然后才允許服務(wù)端從輸入流讀取后續(xù)內(nèi)容??蛻舳艘脖仨氂胹hutdownOutput明確表示發(fā)送結(jié)束才能進(jìn)入等待響應(yīng)階段。順序?qū)憣?duì)了粘包半包問(wèn)題就從設(shè)計(jì)上被壓住了剩下交給readFully去兜底。5.2 long和int的選擇2GB限制第一次設(shè)計(jì)協(xié)議頭時(shí)我圖省事文件大小用的是int。測(cè)試到1.8GB一切正常但傳到2.3GB時(shí)客戶端報(bào)告發(fā)送了-1字節(jié)服務(wù)端那邊還沒(méi)收到數(shù)據(jù)就已經(jīng)報(bào)錯(cuò)。后來(lái)查了一下問(wèn)題就是int溢出2.3GB換算成字節(jié)已經(jīng)超過(guò)了int的最大值約21.47億算出來(lái)的字節(jié)數(shù)變成了負(fù)數(shù)整個(gè)發(fā)送循環(huán)直接崩潰。教訓(xùn)很清楚凡是和文件大小相關(guān)的字段一律用long。Java的數(shù)組下標(biāo)仍然是int限制但你真正要寫進(jìn)Socket的是分片不是整個(gè)文件的byte數(shù)組。用long記錄總大小、用int記錄每次的分片大小兩者組合起來(lái)就沒(méi)問(wèn)題了。這個(gè)教訓(xùn)也提醒我協(xié)議設(shè)計(jì)階段最好把字段寬度一次性想清楚。文件大小、偏移量這些天然有變大趨勢(shì)的字段寧可用8字節(jié)的long也不要為了省幾個(gè)字節(jié)埋雷。5.3 客戶端斷網(wǎng)之后服務(wù)端留下一堆半成品有一輪測(cè)試我模擬了客戶端中途斷開(kāi)上傳到一半直接切斷了網(wǎng)絡(luò)。服務(wù)端那邊沒(méi)有及時(shí)感知到客戶端斷開(kāi)read方法在等著數(shù)據(jù)直到TCP超時(shí)重傳徹底失敗后才拋出SocketException。這個(gè)過(guò)程中服務(wù)端已經(jīng)創(chuàng)建了目標(biāo)文件并寫入了一部分?jǐn)?shù)據(jù)異常發(fā)生后這部分就是殘留的半成品文件。處理方案分兩步第一步在handleClient方法里記錄本次任務(wù)的目標(biāo)文件路徑第二步在catch塊中捕獲所有IOException后把記錄的目標(biāo)文件刪除。刪除動(dòng)作本身也可能失敗文件被占用或權(quán)限不足那就再想辦法做一次補(bǔ)償或者至少輸出一條足夠顯眼的日志提醒人工介入。另一個(gè)很實(shí)用的技巧是給服務(wù)端設(shè)置SO_TIMEOUT也就是讀超時(shí)。如果一定時(shí)間內(nèi)沒(méi)有字節(jié)到達(dá)就直接拋異常結(jié)束任務(wù)而不是無(wú)限等待。文件上傳的場(chǎng)景里客戶端長(zhǎng)時(shí)間不發(fā)送任何數(shù)據(jù)基本可以判定連接異常等待沒(méi)有意義。5.4 Windows和Linux路徑分隔符差異這個(gè)坑很隱蔽客戶端在Windows下把文件路徑D:\logs\access.log直接拼到協(xié)議里服務(wù)端在Linux下按/分割路徑文件名變成了D:\logs\access.log整個(gè)字符串。Windows下執(zhí)行File寫入時(shí)反斜杠會(huì)被當(dāng)作轉(zhuǎn)義字符的一部分結(jié)果文件名異常打不開(kāi)目標(biāo)文件。我給的方案是在sanitizeFileName里統(tǒng)一過(guò)濾所有\(zhòng)\和/提取最后一個(gè)分隔符之后的部分作為文件名。這樣客戶端傳D:\logs\access.log過(guò)來(lái)服務(wù)端寫出的文件名就是access.log。如果確實(shí)需要保持目錄結(jié)構(gòu)那就讓協(xié)議再增加一個(gè)字段顯式表示相對(duì)路徑而不是依賴文件名里的分隔符自動(dòng)解析。6. 進(jìn)階方向并發(fā)、續(xù)傳與更高效的數(shù)據(jù)通道6.1 多客戶端并發(fā)上傳的線程池參數(shù)如果同時(shí)上傳的客戶端有好幾個(gè)服務(wù)端的線程池參數(shù)就不能隨便寫了。前面用availableProcessors() * 2只適合CPU密集型的推算文件傳輸其實(shí)是IO密集型任務(wù)線程數(shù)可以略多。一個(gè)比較穩(wěn)的起步配置是ThreadPoolExecutor pool new ThreadPoolExecutor( 4, 16, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100), new ThreadPoolExecutor.CallerRunsPolicy() );核心線程數(shù)4最大線程數(shù)16隊(duì)列容量100。超出隊(duì)列容量時(shí)CallerRunsPolicy讓提交任務(wù)的線程自己執(zhí)行這比直接拋拒絕異常更溫和至少不會(huì)讓客戶端瞬間收到失敗。但也要注意如果并發(fā)太高IO和磁盤會(huì)成為瓶頸線程再多也沒(méi)有收益反而增加上下文切換的成本。實(shí)測(cè)幾GB大數(shù)據(jù)并發(fā)時(shí)磁盤寫入速度通常是第一瓶頸并發(fā)數(shù)控制在磁盤能承受的范圍內(nèi)才是對(duì)的。6.2 斷點(diǎn)續(xù)傳協(xié)議加offset服務(wù)端改用RandomAccessFile斷點(diǎn)續(xù)傳的關(guān)鍵是讓服務(wù)端知道這次從哪里繼續(xù)寫。在協(xié)議頭里增加一個(gè)8字節(jié)的offset字段客戶端發(fā)送前先和服務(wù)端確認(rèn)已有文件大小然后從偏移位置開(kāi)始讀取并發(fā)送文件剩余部分。服務(wù)端接收時(shí)改用RandomAccessFile打開(kāi)文件后先seek(offset)再按循環(huán)寫入文件體。這里要注意校驗(yàn)策略也要跟著變?nèi)绻募粋髁艘话刖蛿嗔苏募﨧D5還沒(méi)算出來(lái)續(xù)傳后的MD5計(jì)算必須在文件補(bǔ)齊之后才能做。另一種做法是傳輸時(shí)對(duì)每個(gè)分片計(jì)算CRC32服務(wù)端每收到一片校驗(yàn)一片這樣不用等整個(gè)文件送完就能及時(shí)發(fā)現(xiàn)損壞。代價(jià)是協(xié)議頭里需要再增加分片序號(hào)和分片CRC字段復(fù)雜度上了一個(gè)臺(tái)階。如果只是作為學(xué)習(xí)案例先把offset機(jī)制實(shí)現(xiàn)出來(lái)就夠理解了。真正的生產(chǎn)級(jí)續(xù)傳還要解決服務(wù)端如何知道文件已有多少數(shù)據(jù)的問(wèn)題常見(jiàn)做法是客戶端先發(fā)一個(gè)查詢請(qǐng)求或者協(xié)議頭里帶上服務(wù)端文件的舊大小服務(wù)端比較后決定是覆蓋、跳過(guò)還是續(xù)傳。6.3 零拷貝數(shù)據(jù)通道FileChannel.transferTo前面的代碼用的是傳統(tǒng)InputStream/OutputStream復(fù)制每個(gè)字節(jié)都要在用戶態(tài)和內(nèi)核態(tài)之間拷貝多次。如果數(shù)據(jù)量極大這部分的CPU消耗非常明顯。Java NIO提供的FileChannel.transferTo可以借助操作系統(tǒng)內(nèi)核的sendfile機(jī)制Linux直接把文件內(nèi)容從文件系統(tǒng)發(fā)送到Socket緩沖省去用戶態(tài)拷貝。發(fā)送端的核心代碼可以簡(jiǎn)化為FileChannel channel FileChannel.open(file.toPath(), StandardOpenOption.READ); long position offset; long count fileSize - offset; while (count 0) { long transferred channel.transferTo(position, count, socketChannel); if (transferred 0) break; position transferred; count - transferred; }需要留意的是transferTo并不是每調(diào)用一次就把count全傳完它返回的是實(shí)際傳輸?shù)淖止?jié)數(shù)所以必須用循環(huán)。另外SocketChannel要設(shè)置為阻塞模式默認(rèn)就是transferTo才能正常工作。我實(shí)測(cè)過(guò)使用零拷貝之后同文件傳輸?shù)腃PU占用幾乎下降了一半對(duì)于大批量文件同步的場(chǎng)景提升非常明顯。零拷貝也不是銀彈如果文件特別小、或者網(wǎng)絡(luò)非??於疟P很慢收益就無(wú)法體現(xiàn)。但作為一種進(jìn)階優(yōu)化思路它確實(shí)是值得了解的經(jīng)典技術(shù)。6.4 還能往哪里擴(kuò)展這個(gè)案例做完之后可以延伸的方向很多。最容易想到的是加密用TLS替換裸的Socket或者對(duì)文件內(nèi)容做AES加密后再傳輸解決明文數(shù)據(jù)在網(wǎng)絡(luò)上被截獲的問(wèn)題。其次是壓縮在客戶端讀文件時(shí)用GZIPOutputStream包一層文件變小了傳輸自然變快代價(jià)是CPU占用增加。再往下就是秒傳客戶端先發(fā)送文件MD5服務(wù)端如果已經(jīng)有相同摘要的文件就直接返回成功不需要真的傳輸數(shù)據(jù)。還有一個(gè)值得嘗試的方向是傳輸限速和失敗自動(dòng)重試。限速可以防止一個(gè)上傳任務(wù)占滿整個(gè)出口帶寬影響其他服務(wù)失敗重試則要和斷點(diǎn)續(xù)傳結(jié)合才能做到斷網(wǎng)恢復(fù)后自動(dòng)接著傳。把這些機(jī)制一個(gè)個(gè)加進(jìn)去一個(gè)簡(jiǎn)單的文件上傳Demo就會(huì)慢慢變成一個(gè)真正能扛生產(chǎn)環(huán)境的小型分發(fā)系統(tǒng)。這個(gè)案例本身不算復(fù)雜核心就是一段清晰的協(xié)議、一個(gè)按部就班的接收循環(huán)、加上對(duì)異常邊界的處理。但把它真正跑通、跑穩(wěn)你會(huì)發(fā)現(xiàn)對(duì)TCP通信的理解會(huì)扎實(shí)不少以后再碰到消息隊(duì)列、RPC框架里的傳輸設(shè)計(jì)和粘包處理背后都是同一套原理。如果實(shí)際操作中遇到了奇怪的問(wèn)題優(yōu)先從消息邊界是否分明順序是否嚴(yán)格異常是否被吞掉這三個(gè)角度排查大概率能很快找到答案。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
天天综合站| 91天天综合在线观看| 久久伊人青青草| 久久超碰97| 久久婷五月天| 黄片免费日韩| 夜夜草天天| 天天日日日射| 90后性网国产欧美| 亚洲国产97在线精品一区| 久久激情亚洲精品无码?V| 五月天加勒比啪| 久久久国产亚洲精品系列| 亚洲色图 91| 午夜丁香婷婷| 亚洲精品三| 丁香婷婷五月| 亚洲欧美日韩不卡人妻| 亚州成人A√| 国产视频一区二区三区在线免费观看| 夜夜嗨绯色| 国产熟女免费观看久久| av网站在线观看了| 淫荡网址| 五月亭亭六月丁香| 97超级色碰碰| 韩国三级三级BD在线| 亚洲最大AV网| 一区二区三区四区免费视频| 性色av婷婷久久一区二区点复制| 国产浮力影院第1页| 日本中文字幕不卡视频| 国产一区免费午夜视频| 韩国一级做A片免费的| 97伊人超碰| 天天干人妻| 亚洲综合网电影91| 青青草中文字幕| 欧美小说区视频区| 大香蕉手机在线| 男人天堂久久精品不卡| 日日爱99| 精品免费囯产一区二区三区| 亚洲日韩美女丝袜美腿人妻视频| 久久久91福利姬| 91色噜噜狠狠| 97在线观看免费视频l| 色五月婷婷久久| 色色色综合网| 五月丁香色情| 亚洲 综合 欧美| 亚洲色图亚洲无码强奸乱伦| 秋霞免费AV| 永久电影三级在线观看| 欧美偷拍区| 淫荡熟女乱伦网| 大香蕉综合网| 日韩一级二级三级| 精品无av| 中国一级αV| 九九九九AV| 亚洲欲色| 青青久久艹| 另类小说综合网| 亚欧无码在线| 欧美久久九九| 91精产一区二区三区| 国产日韩人人| 一卡二卡三卡| 极品欧美一区二区三区| 日韩性爱啪啪视频| 欧美日韩激情无码专区| 99精品网| 亚洲激情色片| 男人的天堂久久久| 黄骗免费| 久热伊人99re| 欧美午夜色妇色鬼| 亚洲成人色情五月天丁香花| 欧美黑人极品高潮喷吹熟女黑人性暴力日韩在线欧美极品一区二区 | 欧美成人9797| 99综合自拍| 97碰在线视频| 黄aaaaaaaaaaaaaaaaaa色网站 | 国产亚洲深夜激情| 丁香五月影院| 久久久久久裸体| 青草地一本线一区二区三区| 日韩性爱1级片视频| 蜜臀久久一区二区| 2019男人的天堂| 亚洲一区操| 国产精品久久久久久久AV大片| 成·人免费午夜在线观看| 强奸乱伦Av网| 久久综合九九| 欧美人妻色| 99久久9| 久啪视频| 久久精品毛片免费不卡| 久久久99久9| 嗯嗯啊啊好疼| 国产无码精品成人| 国产女上位好爽在线| 国产97/欧美| 翔田千里AⅤHD无码| 男人的天堂2010| 亚洲综合另类色图| 精品偷拍13p欧美dodk视频| 9999伦理视频| WWW操逼| 亚州色图狠狠干| 伊人加勒比| 亚洲精品99| 青草地一本线一区二区三区| 丁香五月婷婷色| 91热色| 国产1769在线| 欧综合网| 久操九九九九| 欧美亚洲天天| 精品国产人成在线| 黑人干亚洲| 国产一级特黄大片处女| 欧美 亚洲 另类 综合| 在线播放成人高清免费视频| 久久成人国产| 欧美第一页| 日韩激情啪啪啪| 五月丁香| a片在线播放| 色综合中文字幕不卡| 亚洲欧美色图| 激情小说五月天| sewuyueav| 五月天色图| 国产精品69人妻无码久久久| 97中文综合| 五月天婷婷欧美三区| 色与欲影视| 日本久久久久久久久| 欧洲在线性爱视频| 超碰天天操| 久久久精品成人国产| 欧美日韩亚洲少妇寂寞影院正在播放 | 免费看毛片操穴| 91搡老女人老妇女老熟女歌词翻译| 久久久草成人网站久久久草成人久久久草久久久 | 综合亚洲欧美| 欧美黄色图片| 99热最新| 成人小说视频在线精品欧美| 精品一级| 国产人妻一区二区三区欧美毛片| 97碰在线视频| 日本黄大片在线观看视频| 清清草影| 国产久久一区二区午夜| 岛国大片国产| 欧美 亚洲| 91黑丝操| 日韩天天本| 涩五月婷婷| 自拍偷拍 日韩欧美| 国产精品毛片| 欧美性后入| 桃花色涩综合影院| 国产最新小视频在线播放下载| 中文欧丝袜诱惑| 日日操免费视频| 插B在线观看| 婷婷色播婷婷| 日韩欧美中文日韩欧美色| 欧美色图91| 久久久男人的天堂| 淫乱图区| 欧美性色综合网| 91精品久久久久久综合五月天| 亚洲国产精品无码AV久久久| 91熟女网| 97中文综合| 日本中文字幕一区| 成人A片男人的天堂| 亚洲丝袜二区| 热久久国产| 国产丁香精品露脸视频| 999热这里只有精品| 91热色| 人妻无码后入| 特级特黄一级毛片免费| 丰满人妻一区二区三区免费| 97超碰9| 欧美亚洲色的图| 男人的天堂99| 日本新免费二区三区| 欧美成不卡网| 亚洲国产剧情少妇激情| 天美av在线观看| 婷婷色网| 可以看的av| 色牛牛AV| 超碰免费人人| 九九激情网| 免费A V在线| 六月婷婷激情| 日韩有码 一区二区三区| 情色大香蕉| 97色干| 97人人操人人摸人人爱| 97在线观看播放视频| 日本一级黄色电影| 久久久久人妻二区精品叶可怜| 国产又黄又爽| 91在线秘 男同| 91oumei| 97国产亚洲中文在线| 啊啊啊啊二区好大| 精品久久人妻成人网| 日本中文字幕不卡视频| 免费中文综合精品| 乱伦图av| 国产欧美一区激情交| 免费观看啪视频| 亚洲国产欧美中日韩成人综合视频| 日韩色| 日本网色| 超碰爽人妻熟女Av| 久久夜夜夜| 啊啊啊啊啊,啊啊啊啊好舒服,操我舒服啊啊啊 | 97午夜剧场日韩| 97亚洲一区| 麻豆天美传媒在线视频天堂| 国产吹潮女在线观看| 天天碰久久入| 国产精品96久久久久久| 日韩欧美大力操| 91在线视频免费播放| 尤物一级在线免费观看| a久久| 欧美视频第二页| 美女黑人91神马| 欧美色亚洲色| PMv在线观看| 日韩在线观看字幕精品| 国内精品999| 青青草国产盗摄一二三区| 欧美一品道| 国产精品久久久啊| 成人av影院在线观看| 无遮挡猛进视频免费无限观看| 五月婷视频| 欧美情色贴图| 精品人妻一区二区三区四区不卡在| 国产精品亚洲色婷婷久久久| 色婷婷香蕉| 综合影视国产无码| 麻豆亚洲Av成人无码一区精品| 国产五码丝袜屁眼| 91综合网站| 国产女乱淫真高清免费视频| 操人妻丝袜高跟| 激情综合五月| 久久少妇人妻| 天天谢天天干| 97一区二区蜜臀| 亚洲中文sv| 韩三级a视频在线观看| 久久草草欧美精品| 精品免费成人久久| 天堂蜜桃无码视频一区二区| 玖玖爱一区在线| 蜜臀久久99精品久久久久久| 丰满人妻av一区二区三区| 情色av电影| 97久久综合网| 亚洲AV免费在线| 久久精品99久久久久久| 免费一级a毛片久久久久久鸭绿欲| 91中出在线| 91久久久久久久久久久| 日韩特一级久久| 国产人妖的免费的视频| 五月天伊人| 亚洲丝袜综合| 日韩精品区二区三区不卡| 蜜桃臀久久| 欧美日韩亚洲一区二区在线观看| 伊人色综合超碰| 蜜臀久久99精品久久久久久婷婷| 欧美在线播放aaaa| av天堂加勒比| 九九Av| 天天日天天干天天摸天天操| 亚洲人妻AV| 久久r精品| 超碰成人国产| 久久东京热成人| 日韩无码专区| 亚洲国产综合图区中文字幕 | 天天做天天爱天天高潮| 精品少妇一区二区| 日本色婷婷| 久久香蕉国产线看观看亚洲女人 | 亚洲午夜精品久久久中文影院| 美国aaaaa一级黄片| 黄久在线| 九九99精品| 在线播放一级无码视频| 玖玖蜜臀资源网| 超清福利精品视频在线| 中国小夫妻勾搭露脸淫荡对白| 黄色免费一级在线毛片| 欧美性爱一区二区三区四区 | 99久草| 亚洲色图a| 亚洲人体视频在线观看| 久久欧洲| 亚洲无吗在线视频| 久久久久久久久久9| 国产欧美一区二区| 亚洲人妻中文高清| 男同专区一区二区三区在线| 色诱中文字幕| 欧美日韩 强奸乱伦| 97久久超碰亚洲| 色婷婷丁香五月| 夜夜骑夜夜操| 九九九草| 伊人97超碰| 1级黄色夫妻对换性交免费看| 另类专区加勒比| 六六久久日韩不卡| 人妻中文字幕精品无码| 天美传媒AV在线| 日韩丝袜二区| 无码区蜜乳| 超碰综合97在线| 久热69九色熟妇97| 欧美十八禁网站| 69麻豆天美| 亚洲自拍一区夜夜操| 国产九九九九九九九九| 中文一区二区三区影院| 99re在线视频国产| 国产精品对白内射| 黄片www.| 国语国产操逼伊人AV网| 韩国一级做a久久久久| 亚州中文字幕超碰97| www久| 国产女人和拘做爰视频| 久9无限国产| 热热色色综合| 青娱乐大香蕉| 口爆综合网| 亚洲一区二区中文字幕| 日韩精品一区二区高清| 精品人妻一区二区三区四区| 亚洲天堂人人妻| 欧美日韩国产传媒在线精品| 欧美性爱一级操| 亚洲欧美国产中文视频| 麻豆国产精品午夜视频| 大香蕉狠狠爱| 久久精品国产99久久,亚洲日韩久久日本一区一区三区 | 亚洲图片日本AⅤ欧美在线| 在线天堂资源亚洲| 中文字幕无码不卡啪啪| 欧美 综合 亚洲| 神马久久中文字幕| 麻豆人妻少妇在线免费观看| 中文字幕国产精品1区| 青青草日韩无码| 大地资源在线观看中文第二页| 黄色片一区二区三区四区五区| 久久性爱视频免费看| 无码欧美有限公司| 97中文综合| 北条麻妃性愛视频| 男人精品天堂一区| 天天搞欧美| 人妻少妇久久| 91热色| 99热99re6国产在线播放| 精品人妻无码一区二区三区不卡-精品人妻无码一区二区...|精品少妇一区二区三 | 极品美女福利在线观看| 无码日韩人妻av一| 国内毛片婷婷六月色| 美女视频尤物网在线看| 91人妻精华帖| 久久久免费的精品| 99操碰| 色香色香欲天天天影视综合网| 激情五月丁香五月| 国产11页| 亚洲精品欧美专业| 熟女91网| 欧美高清第一页| 国产高清成人免费视频| 青青青国产手线观看视频2| 亚洲啪啪视频免费| 天堂中文资源在线bt| 5278欧美一区二区三区| 五月丁香啪啪啪| 久久秀这里有精品| 天天热精品| 久久久久久人| 一二三区操逼国产91| 无遮挡一级毛片视频免费的| 国产极品一区二区三区三州| 精品一区二区三区蜜桃臀赵总 | 99热这里都是精品| 九九视频黄色片| 国语精品内射在线观看| 五月丁香色色网| 99热色这里只有精品| 亚州色图欧美| 啊啊啊啊啊好多水| AV不卡在线| 超碰免费在线| 久久97超碰香蕉| 麻豆91熟妇人妻中文字幕茄子| 日本韩高清无砖码22o| 国产亚洲性生活视频播放| 日本视频在线观看污污污| 五月天精品| 蜜桃久久精品一区二区三区| 99re黄 | 日韩 欧美 国产 麻豆| 大香蕉人妻久久| 色就色综合| 屌色在线97视频| 九区国产| 91肉片| 亚洲精品美女久久久久久久久| 四虎永久在线精品免费网址| 台湾佬中文娱乐网久久久久久久久久com | 三及片网站| 草莓精品视频在线免费观看| 欧美操逼一二三区| 久操| 久久久久久69国产一区二区| 超碰亚洲欧美日韩无| 久久视频,这里只有精品| 一区在线精品中文字幕| 嗯啊不要在线| 精品人妻一区二区蜜桃视频| 亚洲囯产精品女人久久久| 国色天香av| 97色欧洲| 色综合九九| 青青操视频在线| 日韩免费在线观看不卡| 久久发布国产伦子伦精品| 日1区2区3区2020| 特级毛片特黄久久免费看| 精品99999久久久久久| 久久久久久久久久久久97| 日本成人电影资源网| 久操91视频| 可以在线观看的黄色网址| 夜夜夜久久| 无码人妻一区二区三区免费九色| 国产av尤物| 欧美色97| 男人夜色天堂ss| 精品97久久| 九九人人操| 97欧美日韩综合| 午夜丁香| 亚洲熟女av中文字幕| AV大香蕉| 国产精品乱码久久久久| 天天91~综合入口| 男女国产精品| 99青草| 精品欧美老熟女一二区| 国产原创精品| 国产亚洲性生活视频播放| 久久精品老司| 上海一级黄片| 99热线麻豆| 91粉嫩萝控精品福利网站_精品影音先锋国 | 免费少妇一区二区| 91AV天美在线视频| 久久老子无码午夜伦不卡| 四虎精品永久在线观看| 操人91| 精品国产无码中文| 国产一区二区三区,在线观看观看| 亚洲无码国产探花在线观看| 亚洲区限制级| 日韩国产中文字幕| 精品人妻一区二区三区在| 嗯嗯啊啊用力视频免费| 欧美一级特黄淫片在线观看| 熟妇一区,二区,三区。| 人人爽天天爽| 干干干天天| 99精品九九九九九九| 亚洲国产第一页综合视频| 日本成熟少妇A∨网站| 91AV天堂| 91 在线亚洲| 人人操人人摸人人看人人干| 97综合久第一页| 精品美女少妇一区二区三区| 嗯嗯嗯,草死我| 男人天堂欧美| 亚洲精品天天影视综合网 | 亚洲国产成人精品无码专区| 91亚洲人| 色哟哟av网址| juliaann精品熟女一区| 欧美性巨大╳╳╳╳╳高跟鞋| 欧美情色亚洲| 啊啊啊啊好疼视频| 综合网~91综合网| 婷婷五月天网| 日韩欧美综合激情| 国产无码精品久久久久久| 欧美色自拍| 天堂av最新电影网| 欧美性xxxxx狂欢| 少妇内射视频| 爱爱动态试试看6 0秒| www.av在线观看| 一本色道久久天天射天天干| 果冻传媒A片麻豆熟妇人妻| 四季AV一区二区凹凸精品小说| 日本三级黄页| 国产精品无码AV网站| 日韩 国产 欧美自拍| 中文字幕AV中出| 啊嗯嗯啊好大好爽| 精品96久久| 伊人97色天使| 日本成a人v网站在线观看| 亚洲综人网| 亚州熟妇精品| 性色av一区二区| 天天欧美色| 性爱综合网| 日韩不卡毛片Av免费高清| 亚洲精品成人动漫在线| 成人五月天丁香激情综合| 加勒比99999| 天天影视之亚洲综合网| 是还免费视频1727我| 美腿丝袜高跟网免费视频免费视频| 天美国产精品| 日韩情色视频| 国产精品久久发布| 亚洲国产综合久久久性感熟妇| 日日日日日| 无码色| 大香蕉九九| 午夜大香蕉| 五月丁香啪啪| 素人播放一区| 伊人色综合超碰| 一区二区三区四区五区高清无码永久视频 | 国产精品97超碰| 99久久9| 啊啊啊啊在线播放| 久久精品成人| 精品视频久久| 日本一区二区亚洲综合| 国产男女边吃边摸视频网站| 美国精品国产精品| 逼逼逼逼操操操操操操操操操午夜剧场| 都市久久精品激情亚洲| 国产成人免费观看在线视频| 91干熟女| 日本熟妇色熟妇在线视频播放| 国产极品久久久| 日本中文字幕一区| 一区二区三区一亚洲中文字幕、综合区灬 | 国产嫩草精品A88AV在线| 99九九久久| 青草园大香蕉| 色图综合| 很黄很色的视频在线观看| 日韩AV无码中文一区二区| 欧亚免费视频| 久久久久密| 日韩精品大香蕉伊人在线| 成人情色综合网| 亚洲开心网| 草草影院最新网址| a网站免费观看| 夜夜夜夜夜夜夜夜夜狠狠狠狠狠狠狠| 99re这里| 亚洲一二三四区| 在线看的av| 99热在线观看| 久久爽爽精品| 麻豆精品.欧美精品.日韩精品.| 91精品无码久久久久久久| 欧美日韩精品一区二区三区高清| 日本一级黄色电影| 99只有精品| 激情五月综合| 极品极品色影院| 密臀在线免费观看| 黄站在线免费观看| 人人妻碰人人免费| 亚洲色综合| 热久日综合| 91国模| 国产极品美女高潮无套在线观看| 夜夜爽77777| 97欧美久久久久久久| 日韩肏逼视频| 久草大| 亚洲天堂日本| 日本成人免费一区二区三区| 狠狠干综合| 色女免费在线观看视频网址| 欧美日韩插逼视频| 亚洲天堂,男人| 亚洲性综合11| 成人性爱视频在线看| 九九精品美女高溯喷水| 日本道人妻久久久在线不卡色视频| 九九精品99| 福利风月五月天影院| 国产精品夜夜| 99re久久| 午夜操操操| 国产精品黄色三级av| 射欧美综合| 久久婷婷电影网| 2010男人的天堂| 国产精品一二三| 亚洲一区二区三区AV无码| 青青草手机在线免费观看| 中文字幕一区二区视频在线观看 | 超碰到97情色| 黄页视频网站野外| 淫乱图区 | 欧美激情总合网| 97免费视频在线| 五月丁香影院| 亚州性色| 日韩美一区| 欧美黑人极品高潮喷吹熟女黑人性暴力日韩在线欧美极品一区二区老师黑人潮喷一 | 欧美精品999| 中国的操老妇女| 萌白酱自拍视频| julia中文字幕在线观看| 国产日韩区| 欧亚成人在线视频| 日韩人妻中文视频| 熟妇女伦乱视频视频| 色色色99| 久久系列| 国产精品亚洲四五区在线观看| 国产精品激情久久久久久久| 久久久久9| 熟女色综合久久| 国产精品一区二区麻豆| 五月丁香大香蕉| 国产精品96久久久久久| av操操不卡| 国产一区自拍欧美日韩| 日日干夜夜欢| 99re视频在线观看这里只有精品| 精品福利| 亚洲激情色片| 在线看免费无码AV天堂的| 麻豆 美女 丝袜 人妻 中文| 久久亚洲影院一区二区| 无码不卡八戒| 久久成人网站| 99超碰色| 成人aⅴ一区二区三区| 噜噜噜亚洲精| 久久综合久久综合人久久夜精品| 国产极品精品美女视频| 人妻少妇无码| 大香蕉伊人网WWWn0n| 国产欧美精选激情视频| 欧美性色欧美| 97色欧州| 五月天婷婷久久| 日本孕妇一区二区视频操逼免费看| 亚洲免费97免费| 欧美性生活综合| 欧美夜夜草视频| 欧亚日韩综合精品国产| 亚洲综合小说另类图欧美视频激情小说色五月天| 国产精品九九九| 精品二999| 能看的AV| 亚洲成?V人片在线观看福利| 97免费在线观看| 亚洲丝袜99| 色呦呦、国产精品| 亚洲精品人妻吞精av| 国产高清亚洲日韩一区| 超碰 国产熟女精品一区| 俺去久久| 激情AV| 国产高清免费不卡av| 91天天综合日韩欧美| 日韩无码AB| 日韩伦理久 久久 清纯| 六月婷婷色综合| 可能人人看人人摸| 成人精品无码| 久久啊啊| 亚洲影视高清三级-草1024榴社区入口-品爱AV| 一区中文字幕二区日韩| 天天日天天插| 亚洲欧洲综合视频在线| AV网站高清无码在线观看| 97精品久久| 78精品| 欧洲自拍第一页| 丰满精品人妻少妇久久字幕| 亚洲情色五月天 | 国桃视频产巨乳精品一区二区在线| 狠狠 91| 狠狠干综合| 亚欧性爱ab| 中文字幕日韩人妻视频一区二区三区| 很很操在线| 中文字幕欧美丝袜07资源| 东北老熟女| 久久r精品| 蜜臀一二三区| 少妇特黄一区二区三区| 狠狠做深爱婷婷久久二区| 丝袜视频一区二区在线播放国产中文| 99久久这里只有精品| 夜夜一区二区| 欧美天天插| 婷婷色一区| 亚洲91少妇| 日本曲间由美性生活片| 天天色悠悠激情| 日韩专区数据列表-第3230页-精品国产一区二区三区香蕉 久久99熟女人妻中文字 | 久久国产在线一区二区| 日本久久女同性恋视频| 日韩性爱一级片| 久9re热视频这里只有精品| 三级片网站在线播放| 69精品久久久久中文字幕| 日本高清有码网址视频| 国产精品久久久久综合| 99综合免费视频| 国产久久一区二区午夜| 久久久久久久久久久97| 久久亚洲天天做| 久久m| 中文字幕一区二区在线日韩精品| 久久精品亚洲婷婷| 日本人妻一区二区| 久久AV无码1区2区3区| 麻豆国产第一| 日韩毛片9| 三四中文字幕| 国产粉嫩蜜臀av一区二区三区| 秋霞一集毛片观看| 家庭乱伦性爱av| 六月丁香久久| 在线天堂999| 蜜臀久久久国产| 一级免费精品| 久久精品黄色| 亚洲春色欧美激情自拍| 欧亚乱色熟女一区二区| 这里只有精品久久| wwe 天天干.com| 美女91色黄18| 欧美第二页| 北约熟女超碰| AA丁香综合激情| 欧美亚洲20p| 韩国毛片一区二区三区| 久久9久| 思思热国产在线视频| 色婷婷小说| 黄呦呦在线| 久久啊啊| 欧美日韩 强奸乱伦| 成人综合色网| 欧美一品道| 女人天堂av在线播放| 日韩性爱免费视频在线网站| 天堂蜜桃无码视频一区二区| 校园春色宗合网| 国产又粗又大硬免费色网视频| 东亚亚洲无码高清| 啊啊啊好湿国产一二| 自拍第一页| 97资源视频| 97精品国产97久久久久久| 大香蕉综合在线| 翔田千里A片一区二区| 免费人成?大片在线播放| 亚洲丝袜B诱惑| 国产精品不卡少妇白| 区自美91| 国产中午字一暮区| 欧美日韩另类在线播放| 超碰97起碰| 亚洲欧美电影| 男女打扑克高清网站| 成 人 影视 一区 二区 三区 四区| 伊人青青一区成人视频在线观看区| 国产精品天美传媒| 国厂麻豆77q4| 揉揉揉夜夜| 欧洲亚洲国产综合在线| 一级啊性爱在线视频| 中文字幕一区 二区三四五 区日 日骚| 欧美成不卡网| 婷婷性网| 日本一区二区三区四区免费观看| AV中文在线| 欧美91网站| 久久专区| 日本在线播放不卡一区| 1024亚洲中文字幕久在线看片你懂的 | 国内自拍 日韩激情 99| 国产四虎在线| 成人AV在线网站| 18禁免费视频| 97玖玖人妻| 欧美日韩欧美| 日本日皮视频逼| 啊啊啊啊操死我了| 欧美组图日韩亚洲中文字幕| 十八禁视频网站| 欧美传媒一区| 国产av强奸美女| 国产精品91ai| 日韩免费a级毛片无码a∨| 国产激情久久久| 欧美在线第五页| 欧美亚洲尤物久久| 日影院久久婷婷夜夜网| 中文字幕日韩专区精品系列 | 麻豆这里只有精品| 天天综合网合集91| 人人摸人人摸人人干| 99久久久无码精品国产人| 97一本大道亚洲一区| 欧美日韩亚洲少妇寂寞影院正在播放| 91性网| 午夜国产综合视频在线观看| 亚洲一级特黄大片在线播放91| 色婷五月| 国产精品在线网站| 变态综合色| julia中文字幕在线观看| 东北女人性交| 国产女人与拘做受视频免费| 日韩成人色图| 熟女露脸激情自拍视频| a片久久久久久久久久久久 | 情色五月天网| 五月天婷婷小说| 日韩三A大片在线观看| 性生活无遮挡纯毛片在线看| 少妇500双飞99| 亚欧精品久久久久久久久久久| 99性爱视频| 爱射综合| 中文字幕成人| 视频一区二区三区精品| 熟妇人妻一区二区三区| 99亚洲国产精品色一区二区三区| 午夜αv| jizzjizz欧美| 麻豆天美传媒在线视频天堂| 夜夜操91744565| 亚洲一区二区三区中文字幕| 亚洲s在线观看| 国产 日韩 欧美 中文 另类,国产 欧美 另类 制服 变态,高清 日韩 欧美 中文,高 | 亚洲伊人久久精品狠狠在线| 超碰 国产熟女精品一区| 97综合在线| 免费又黄又裸乳的视频| 天天看片青娱乐| 粉嫩av一区二区三区四季| 久操高青| 婷婷六月天| 日韩女模中文造逼| 色综合久久av| 秋霞午夜成人福利片片| 春色91| 五月天亚洲网| 精品人妻一二三四区视频| 色诱中文字幕| 91色综合激情| 4141514逼喷水三级片| 性吧在线视频| 欧美|91色综合| 国产超碰在线| 欧美操人| 天天影视网综合少妇| www.久久| 九九热男人天堂| 久久极品一区二区| 一二三四日本视频高清| 日韩欧美天天爽爽爽天天爽爽| 岛国黄色大片网站| 人人操肉肉| 91在线视频免费播放| av网页一区二区三区| 成人免费福利在线观看| 中国女人内射6XXXXX| 黄片直播三级黄片两女一男| 国产亚洲精品自在线亚洲情侣| 久久久精品成人国产| 国产 码在线成人网站| 在线黄页看毛片| 99色婷婷中文字幕乱色| B049AV在线播放| 97国产中文| 亚洲国产综合视频| 黄色片一区二区三区四区五区| 亚洲一区二区精品福利| 激情久久久| 97超碰久| 久久久精精精| 18禁中文字幕| 亚洲男人的天堂亚洲| 麻豆一区在线| 97在线视频免费观看| 久久久久亚洲av综合波多野制衣| 精品国产www久久| 欧洲射精91| 天美传媒精品久久视频| 成人性爱免费播放| 婷婷五月天激情小说| 97亚洲欧美日韩| 97超碰欧美| 亚洲超碰在线| 色欧洲97| 91成人高清在线观看| 亚洲……91| 风间由美日韩欧美久久| 日韩中文字幕国产| 精品国产一区二区久久| 超碰欧美97资源| 日韩操逼HD| 欧美亚州综合图片| 色欲天天婬色婬香WWW夜色| 免费视频无码| 男人的天堂一区| 人人操AV| 久久久久久精品免费看A级| 日本亚洲嫩草影院啪啪| 嗯嗯啊啊用力视频免费| 国产精品熟女丝袜一区二区| 日韩免费中文字幕视频| 色97综合中文字幕| 高跟伊人julia ann| 91色综| 在线视频日韩欧美国产| 天天操夜夜嗨| 丰满人妻一区二区三区免费| 中文一区二区婷婷视频| 日韩在线欧美精品一区二区| 国模不卡一本二本三电影| 可以免费看黄片的视频| 青青草狠狠撸| 高清无码网址| 国产 日韩 欧美高清| 久男人久久| 无码99| 日本一级二级三级网站| 99国产精品| 欧美亚洲涩涩| 久久精品国产亚洲AV成人直播| 亚洲色图伊人网| 国产主播福利| 色偷偷超碰亚洲| 久久精品亚洲婷婷| 果冻传媒A片麻豆熟妇人妻| 亚洲无吗在线视频| 日日摸日日碰| 一级AV性爱| 免费啪啪啪网站18岁| 欧美亚洲高清晰| 亚洲男人天堂2019| 欧美亚洲一级在线观看| 风间由美日韩欧美久久| 99婷婷| 蜜屁av| 日本午夜久久电影| 九九久久久| 色婷婷五月天| 欧美精品庄| 激情看片网站| 亚洲人精品午夜不卡| 午夜精品视频777| 97人人夜| 人人噜夜夜操| 67914亚洲精品| 免费看黄片现成| 久久亚州大香蕉| 久久久久久久久久久久久久久性生活视频 | 亚洲图片欧美在线视频| 日本青青草在线| 国产免费黄色一级大片| 人人澡综合涩| 亚州精品一区二区三区香中文字幕在线| 亚洲阿v天堂在线| 91美女视频电影| 免费综合亚洲中文| 久9久| 熟女自慰久久久| 中文一区二区婷婷视频| 国产又粗又又黄又猛| 亚洲人妻av| 色情五月丁香| 蜜桃网熟妇| 深喉吞精| 91bbb| 国产二区三区免费视频| 欧洲性爱无码区| 欧美成人免费在线观看| 日日干天天干夜夜爽| 久久久一区二区三区三州| 八戒午夜福利理论片| 一本久道在线综合视频| 两性综合网| 亚洲日本成人动漫| 欧美精品双插| 天美av在线| 日本一级特级毛片视频| 岛国色情视频在线观看| 日韩成人大片在线观看| 九九色色| 色天堂综合| 国产欧美另类久久久精品课程| 色噜噜国产精品视频一区二区| 国产婷婷综合在线观看| 性在久久久久久| 色5月婷婷| 我中文字幕6区| 欧美碰碰综合色| 日日噜噜夜夜狠狠视频无| 综合久久中文字幕综合日韩精品 | 长长久久88视频| 欧美亚州综合图片| 97在线观| 国人欧美精品一区二区| 男人的天堂在线2| 国产夫妻性生活视频| 秋霞成人做爱| 91在线欧色| 精品一区二区2| 99re公开精品免费视频| 蜜臀在线免费观看在线免费观看| 91麻豆天美国产欧美高潮| 免费看黄视频亚洲网站| 日本丝袜美腿人妻九九| 午夜超碰| 伊人激情| 久久有码视频| 26uuu国产免费观看| 久久原创中文| 福利操逼| 色九九九综合| 欧美天天干| 91总综合网| 日韩免费在线视频观看| juliaann丝袜大战黑鬼| 国产无码久久高清| 五月婷婷hd| 亚欧成人综合影院| 久久亚洲熟妇在线视频| 日本性爱欧美性爱| 欧美天天综合站| 久久性爱网站| 草B在线| 精品视频久久久久九九九九9999| 伊人性在线视频| 玖色AV| 国产性久久久| 人人摸人人舔一区二区| 欧美性生活综合| 国产偷拍网站| 久久久久网站-538在线视频-欧美永久乱码 | 超碰人人干天天射| 亚洲天堂综合AV| 性暴力欧美猛交在线直播| 使劲用力艹少妇视频一区二区| 欧美日韩性爱操大逼| 蜜臀99久久精品久久久久久| 久久国产99精品72福利 | 国产综合久久久鬼色| 性开放中文AV高清无码免费看| 97欧美综合网| 天天综合网91入口| 久草视频分类在线| 国产隔壁老王影院在线| 亚洲日韩狠狠撸视频| 密臀在线免费观看| 91色久| 国产女人高潮嗷嗷嗷叫小说 | 国产一级作爱毛片| 国产精品久久久久久久AV大片| 日韩欧美福利视频看看| 91爱| 97色97干| 2019精品国产无码成人| 欧美性生活男人的天堂| 九月丁香婷婷色| 欧美婷婷五月天| av九九| 久久秀这里有精品| 欧美老妇综合网| 国产精品不卡一区二区三区| 射丝袜大香蕉| 青青草无码视频| 黄色不卡视频| 亚洲美女精品| 综合网欧美在线| 久综合网| 无套内射性感少妇视频| 黄色在线网站| yellow网站免费观看日韩高清无码| 亚洲日韩美女丝袜美腿人妻视频| 亚洲性感丝袜诱惑在线观看| 东北老女人的激情视频| 美日韩一卡二卡三卡免费人妻精品| 91天堂色男人的天堂| 天天视频黄| 国产精品在线一区二区| 人妻精品一区二区全免费| 91亚洲色人| 黑丝少妇麻豆| 天美麻豆精品视频99| 亚洲有码 视频一区| 丝袜喷水在线| 九月丁香| 国产操逼逼网| www.91逼逼.com| 99人妻| 丰满人妻区一区二区三| 97这里有精品| 大香蕉日韩| 99re98| 欧美熟妇色| 亚洲情色综合网| 青青草吊丝| 精品91摸| 日韩一999精品| 久久综合18p| 人妻日日夜夜精品| 99999精品成人| 五十路人妻在线| 天天综合青苹果|