99精品久久精品一区二区-亚洲熟妇无码?v在线播放-日本国产精品无码字幕在线观看-久久久亚洲永夜AV-亚洲一级无码一区二区一-免费国产成高清人在线视频-中文字幕乱码免费观看-国产毛片精品妇女久久久

ARTICLE DETAIL

資訊詳情

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

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

基于TCP的Socket文件上傳實(shí)戰(zhàn):協(xié)議設(shè)計與粘包處理 最近在幫朋友整理內(nèi)部服務(wù)器之間的日志同步方案時遇到一個非常典型的場景兩臺機(jī)器在同一內(nèi)網(wǎng)里需要把幾個GB的數(shù)據(jù)包從一臺傳到另一臺FTP服務(wù)沒搭、專門的HTTP上傳服務(wù)也沒人維護(hù)臨時裝個網(wǎng)盤工具又不現(xiàn)實(shí)。最后又繞回老路子——直接用TCP通信自己寫一個文件上傳工具。這篇文章就把這個綜合案例完整拆開講清楚從應(yīng)用層協(xié)議設(shè)計、服務(wù)端實(shí)現(xiàn)、客戶端實(shí)現(xiàn)到實(shí)測過程中踩過的坑和進(jìn)階思路覆蓋到Socket編程里最容易被忽略、但實(shí)際開發(fā)又一定會遇到的細(xì)節(jié)。無論你是剛開始學(xué)網(wǎng)絡(luò)編程的學(xué)生還是工作中突然要處理內(nèi)網(wǎng)文件傳輸?shù)拈_發(fā)者照著這套邏輯都能快速落地一套可靠的方案。1. 動手前先想清楚這個案例到底在解決什么問題1.1 既要傳得對又要傳得完文件上傳這件事聽上去很簡單但放到TCP通信的場景里難點(diǎn)立刻浮出水面。TCP本身只保證字節(jié)流是可靠有序的它并不理解文件這種業(yè)務(wù)概念。你往Socket里寫一段字節(jié)對端讀到的可能是半段數(shù)據(jù)也可能是兩段數(shù)據(jù)黏在一起。所以這個綜合案例真正要解決的是在TCP提供的原始字節(jié)流之上設(shè)計一套能讓雙方都理解的消息協(xié)議把文件的名字、大小、內(nèi)容、校驗(yàn)信息完整地送達(dá)對端。另一個容易忽略的痛點(diǎn)是傳得完。小文件一次性讀進(jìn)內(nèi)存再write很快就完事但到了幾個GB的日志文件內(nèi)存根本扛不住必須用緩沖流分塊讀取、分塊發(fā)送。再加上網(wǎng)絡(luò)抖動、客戶端斷線、服務(wù)端磁盤寫入失敗這些情況一個看起來簡單的上傳工具實(shí)際上要把連接管理、流處理、異常恢復(fù)、資源釋放全部串起來。1.2 為什么這個場景必須選TCP而不是UDP文件上傳在傳輸層幾乎沒有選擇的余地必須用TCP。UDP雖然發(fā)送快、沒有連接維護(hù)成本但它不保證數(shù)據(jù)包的到達(dá)順序也不保證不丟包。如果基于UDP做文件傳輸要么自己實(shí)現(xiàn)一套復(fù)雜的確認(rèn)重傳機(jī)制要么接受文件內(nèi)容可能損壞的后果。相比之下TCP把擁塞控制、超時重傳、順序校驗(yàn)這些機(jī)制全部內(nèi)置了你要做的只是在流上面劃分消息邊界已經(jīng)省去了最麻煩的部分。不過這也引出一個很多新手會犯的錯誤覺得TCP可靠就可以把文件名、文件大小、文件內(nèi)容一次性write出去然后以為對端一定能原樣收到。這個理解是錯的——TCP的可靠是字節(jié)層面的可靠不是消息層面的可靠。發(fā)送方一次write比如write 1MB數(shù)據(jù)接收方可能分好幾次read才能讀完發(fā)送方連續(xù)write兩次接收方可能一次read就把兩段數(shù)據(jù)一起讀出來了。這就是后面要重點(diǎn)處理的粘包、拆包問題也是整個協(xié)議設(shè)計的最核心動機(jī)。1.3 邊界判斷什么時候該自研什么時候該用現(xiàn)成方案在做這個工具之前我也認(rèn)真想過是不是直接用成熟方案更省事。如果只是偶爾在內(nèi)網(wǎng)傳幾個文件SFTP、NFS、HTTP文件服務(wù)甚至直接掛個網(wǎng)盤都比自己寫Socket要快。但如果你面對的是批量機(jī)器之間的程序化傳輸、需要在傳輸過程中嵌入特殊的業(yè)務(wù)校驗(yàn)比如上傳后觸發(fā)后續(xù)任務(wù)、或者處于一個不允許額外安裝服務(wù)的受限環(huán)境自己實(shí)現(xiàn)一個輕量TCP上傳通道反而是更理智的選擇。自研方案的邊界也很明確單機(jī)吞吐、并發(fā)連接數(shù)、斷點(diǎn)續(xù)傳這些能力都取決于你自己的設(shè)計和代碼質(zhì)量出了問題沒有現(xiàn)成文檔能查全靠對TCP機(jī)制的理解。所以這個案例的定位不是替代FTP而是一種在可控場景下靈活定制的技術(shù)方案。理解清楚這一點(diǎn)你才知道哪些協(xié)議字段要考慮、哪些異常分支要處理程序才不會寫到一半失控。2. 應(yīng)用層協(xié)議設(shè)計先把字節(jié)流翻譯成消息2.1 協(xié)議頭結(jié)構(gòu)固定頭部加變長字段TCP是流協(xié)議沒有天然的消息邊界所以應(yīng)用層必須自己定義幀。我的做法是設(shè)計一個固定長度的協(xié)議頭后面跟著文件名和文件內(nèi)容。固定頭部長度的好處是接收方可以先讀固定字節(jié)解析出各個字段的寬度再按需讀取后續(xù)內(nèi)容。我設(shè)計的協(xié)議頭格式如下字段類型字節(jié)數(shù)說明魔數(shù)int4固定為0x4D41494E用于快速識別合法連接協(xié)議版本int4當(dāng)前為1方便后續(xù)協(xié)議演進(jìn)文件名長度int4文件名字節(jié)長度UTF-8編碼后文件大小long8文件內(nèi)容的總字節(jié)數(shù)支持超過2GB的文件上傳模式int41覆蓋、2追加、3拒絕默認(rèn)1MD5摘要byte[16]16整個文件內(nèi)容的MD5值用于完整性校驗(yàn)固定頭部一共40字節(jié)。文件名是變長字段跟在固定頭部之后隨后才是文件內(nèi)容。接收方拿到前40字節(jié)后解析出文件名長度再精確讀取文件名最后根據(jù)文件大小循環(huán)讀取剩余內(nèi)容。這里有個經(jīng)驗(yàn)文件名長度用int而不是short雖然實(shí)際場景里文件名幾乎不會超過65535字節(jié)但用int可以避免一個很隱蔽的邊界問題。Java的DataInputStream.readUTF內(nèi)部就是用short存長度的文件名稍微長一點(diǎn)就拋UTFDataFormatException排查起來很費(fèi)勁。既然協(xié)議是自己設(shè)計的就不要把這種限制帶進(jìn)來。2.2 粘包與拆包的解決思路定長頭加變長體粘包接收方一次讀到了兩條消息的內(nèi)容和拆包一條消息被分成多次讀取是TCP編程的經(jīng)典問題。網(wǎng)上??吹絻煞N解法一種是約定固定長度消息不足補(bǔ)位另一種是使用特殊的結(jié)束符。固定長度協(xié)議浪費(fèi)帶寬結(jié)束符方案又要求文件內(nèi)容里不能出現(xiàn)結(jié)束符都不太適合文件傳輸。最實(shí)用的還是定長頭加變長體的思路接收方先把40字節(jié)的頭部讀完整通過readFully保證讀取的字節(jié)數(shù)恰好等于頭部長度然后根據(jù)頭部里的文件名長度、文件大小再精確讀取對應(yīng)字節(jié)數(shù)的數(shù)據(jù)。因?yàn)槊看巫x取的量都有明確數(shù)字約束即使消息在底層被拆成了多個TCP包接收方也只需要循環(huán)讀到指定字節(jié)數(shù)即可粘包也不會影響后續(xù)解析——因?yàn)槊織l消息的長度是確定的剩余緩沖區(qū)里的字節(jié)自然屬于下一條消息。在Java里實(shí)現(xiàn)這一點(diǎn)很簡單使用DataInputStream.readFully(byte[])比反復(fù)調(diào)用read()可靠得多。它內(nèi)部幫你循環(huán)讀取直到讀滿指定長度或者流已經(jīng)結(jié)束。很多初學(xué)Socket的代碼里喜歡這么寫byte[] header new byte[40]; in.read(header);這個寫法是有隱患的read不保證一次讀滿40字節(jié)。必須改成byte[] header new byte[40]; in.readFully(header);不要小看這個差異實(shí)測中普通局域網(wǎng)環(huán)境還好跨交換機(jī)、跨網(wǎng)段傳輸時read一次返回不滿的情況非常常見這也是為什么我的接收數(shù)據(jù)總是缺一截的高頻根源。2.3 三個容易被忽略的協(xié)議細(xì)節(jié)第一個細(xì)節(jié)是文件名的編碼問題。文件名在傳輸前用UTF-8編碼成字節(jié)數(shù)組長度是字節(jié)數(shù)組的長度而不是字符串的字符數(shù)。接收方讀出字節(jié)數(shù)組后用UTF-8解碼恢復(fù)文件名。如果不統(tǒng)一編碼發(fā)送端用GBK、接收端按UTF-8解析中文文件名必然亂碼服務(wù)端寫出去的文件名也會出問題。第二個細(xì)節(jié)是目錄穿越。協(xié)議里收到的是客戶端發(fā)來的文件名服務(wù)端如果直接拿它拼路徑比如new File(baseDir, fileName)一旦fileName里包含../服務(wù)端的文件就會寫到baseDir以外這是嚴(yán)重的安全漏洞。服務(wù)端必須對文件名做凈化處理最簡單的方式是取文件名中的最后一個路徑分隔符之后的部分或者用白名單字符集過濾。第三個細(xì)節(jié)是響應(yīng)反饋。協(xié)議不能只允許客戶端向服務(wù)端發(fā)送服務(wù)端也需要告訴客戶端上傳結(jié)果。所以客戶端發(fā)完文件內(nèi)容后調(diào)用shutdownOutput進(jìn)入半關(guān)閉狀態(tài)讓服務(wù)端讀到文件流的末尾隨后服務(wù)端寫一個int類型的狀態(tài)碼比如0表示成功1表示失敗2表示校驗(yàn)錯誤回送給客戶端。這里的半關(guān)閉是關(guān)鍵它告訴TCP我的發(fā)送方向已經(jīng)結(jié)束但連接仍然可以用來接收數(shù)據(jù)。3. 服務(wù)端實(shí)現(xiàn)從accept到落盤的完整鏈路3.1 服務(wù)端骨架ServerSocket加線程池服務(wù)端要做的事情很簡單監(jiān)聽端口、接受連接、為每個連接分配合適的處理器。但簡單的背后有幾處要注意。一是監(jiān)聽的端口要設(shè)置合理過高如65535在生產(chǎn)環(huán)境容易被防火墻攔截過低如1024以下又需要權(quán)限。案例里我用9999這種高位端口。二是處理方式是單線程還是多線程如果只有一個客戶端傳文件單線程也夠用但實(shí)際環(huán)境里總會有多個上傳任務(wù)所以我在最外層放ServerSocket每拿到一個Socket就交給一個獨(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)榇罅可蟼髡埱髮?dǎo)致服務(wù)端創(chuàng)建上千個線程直接打爆內(nèi)存。生產(chǎn)環(huán)境里我一般推薦用ThreadPoolExecutor手動指定核心線程數(shù)和隊列容量但在教學(xué)案例里固定線程池夠用規(guī)則也簡單。setTcpNoDelay(true)也值得說明它關(guān)閉了Nagle算法。Nagle算法會把小的數(shù)據(jù)包攢在一起發(fā)送以減少網(wǎng)絡(luò)報文數(shù)量但對于文件上傳這種需要頻繁寫入小塊數(shù)據(jù)的場景Nagle帶來的延遲會讓交互式確認(rèn)變得拖沓。文件上傳更看重吞吐關(guān)閉Nagle后小包的發(fā)送延遲明顯降低。3.2 協(xié)議解析與文件寫入的核心邏輯每個客戶端連接進(jìn)來后真正的核心邏輯是固定的幾步讀固定頭部、讀文件名、按文件大小循環(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個字節(jié)全部讀滿所以循環(huán)條件是基于totalRead fileSize每個分片按剩余字節(jié)數(shù)取need這樣既避免讀了下一幀的數(shù)據(jù)如果還有的話也保證了大文件能被完整讀完。3.3 路徑處理與服務(wù)端的安全考慮sanitizeFileName和resolvePath這兩個方法雖然代碼不多卻是服務(wù)端最值得花心思的地方。sanitizeFileName的邏輯是把傳入文件名的所有路徑分隔符替換為下劃線或者提取最后一個分隔符后面的部分。因?yàn)槲募膩碓词蔷W(wǎng)絡(luò)對端永遠(yuǎn)不能信任。同時還要過濾掉Windows下不合法字符比如:/\\|?*否則服務(wù)端落在Windows目錄時文件寫入會拋IOException。resolvePath則處理上傳模式。如果模式是覆蓋直接返回baseDir下的目標(biāo)文件如果是追加返回目標(biāo)文件并準(zhǔn)備好以追加方式寫入如果是拒絕且文件已存在就返回null通知客戶端上傳被拒絕。基本邏輯是private 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; }這個邏輯的好處是覆蓋模式天然支持重復(fù)上傳適合批處理場景拒絕模式則避免了不小心覆蓋重要文件。你完全可以根據(jù)自己場景調(diào)整但安全邊界一定不能省。3.4 完整性校驗(yàn)與異常清理MD5校驗(yàn)放在文件全部寫入磁盤之后執(zhí)行。理論上應(yīng)該在內(nèi)存里邊收邊算摘要節(jié)省一遍文件的完整讀取但這樣對內(nèi)存的占用太高大文件場景不可行。案例里選擇的是接收完成后對落盤文件重新計算MD5再與協(xié)議頭攜帶的預(yù)期MD5比較。代價是文件會被讀第二遍增加了耗時好處是邏輯簡單、可靠性高。如果文件特別大可以折算一下耗時能接受就用不能接受就把校驗(yàn)機(jī)制降級為只校驗(yàn)文件大小。校驗(yàn)為什么重要我在一開始就強(qiáng)調(diào)TCP是可靠傳輸?shù)玊CP的可靠不等于端到端的內(nèi)容不變。中間設(shè)備改寫、服務(wù)端磁盤壞道、程序緩存沒刷干凈都可能導(dǎo)致落盤文件和原始文件不一致。有了MD5對比至少能發(fā)現(xiàn)絕大多數(shù)數(shù)據(jù)損壞的情況。也不要忘了totalRead ! fileSize這種最基本的判斷在客戶端還沒發(fā)完就斷開連接時文件往往只有原始大小的一部分這時候必須把半成品文件刪除并回送錯誤碼。4. 客戶端實(shí)現(xiàn)連接、按協(xié)議發(fā)送、等待反饋4.1 客戶端骨架與連接超時設(shè)置客戶端的實(shí)現(xiàn)和服務(wù)端恰好互逆它的職責(zé)是把本地文件轉(zhuǎn)換成協(xié)議頭加文件體的字節(jié)流發(fā)送到服務(wù)端然后等待確認(rèn)。代碼的第一步是建立連接。這個步驟有幾個常見的坑。用的是Socket()無參構(gòu)造加上connect方法可以在建立連接前設(shè)置超時時間Socket socket new Socket(); socket.connect(new InetSocketAddress(host, port), 5000); socket.setSoTimeout(30000);connect方法的第二個參數(shù)是連接超時毫秒。不設(shè)置的話new Socket(host, port)遇到不可達(dá)地址時會卡很久用戶體驗(yàn)極差。setSoTimeout(30000)設(shè)置的是讀超時也就是等待服務(wù)端響應(yīng)結(jié)果時的最大阻塞時間防止服務(wù)端一直不返回導(dǎo)致客戶端線程永久掛掉。這兩個超時是網(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的小文件問題不大但文件一旦到了幾百M(fèi)B甚至幾GBreadAllBytes會直接把內(nèi)存撐爆。正確做法是分塊讀取并循環(huán)發(fā)送建議的緩沖區(qū)大小是64KB到1MB之間。實(shí)際傳輸中我習(xí)慣用64KB這是一個在性能和內(nèi)存占用之間比較平衡的值。緩沖區(qū)太小系統(tǒng)調(diào)用次數(shù)變多吞吐上不去緩沖區(qū)太大單個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來自bis.read的返回值這個返回值決定了本次寫入的實(shí)際字節(jié)數(shù)。絕大多數(shù)情況下一次read返回的量不會超過64KB所以out.write一次也會把這個緩沖區(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ù)端可能還沒有機(jī)會把處理結(jié)果回傳客戶端就收不到了。正確做法是調(diào)用shutdownOutput()這會讓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后才會寫結(jié)果給客戶端。如果數(shù)據(jù)還沒讀完就寫結(jié)果客戶端那邊會因?yàn)樵谕粋€Socket里先收到了結(jié)果字節(jié)而把結(jié)果誤當(dāng)成文件內(nèi)容的一部分導(dǎo)致協(xié)議錯亂。所以服務(wù)端一定要保證文件內(nèi)容讀取循環(huán)結(jié)束后再調(diào)用out.writeInt(status)。有一個小細(xì)節(jié)是客戶端這邊的DataInputStream也需要設(shè)置超時。我在前面設(shè)置了setSoTimeout(30000)這樣即使服務(wù)端因?yàn)楫惓?ㄔ谖募x取階段客戶端最多等30秒就會收到SocketTimeoutException不會永遠(yuǎn)阻塞下去。4.4 進(jìn)度感知與回滾提示進(jìn)度反饋并不是協(xié)議的一部分但卻是用戶體感的重要部分。一個上傳任務(wù)跑十分鐘沒有進(jìn)度提示用戶的第一反應(yīng)是程序卡死了。我這邊實(shí)現(xiàn)的方式是每發(fā)送5%就打印一次日志同時記錄最后的百分比避免重復(fù)打印。日志的粒度沒必要太細(xì)5%已經(jīng)足夠。如果打印太頻繁大量日志輸出本身也會消耗CPU和磁盤IO。實(shí)測下來5%到10%的粒度最舒服。進(jìn)度日志的另一個作用是可以作為故障排查的線索。如果一次上傳卡住了日志停留在某個百分比位置不動你就能判斷問題出在哪個階段是發(fā)送過程中網(wǎng)絡(luò)斷開還是服務(wù)端處理卡住還是等待響應(yīng)超時??床坏竭M(jìn)度時排查這種問題只能靠猜體驗(yàn)完全不一樣。5. 實(shí)測踩坑有些問題不跑一遍大數(shù)據(jù)根本想不到5.1 粘包半包的真實(shí)復(fù)現(xiàn)案例剛跑通的時候我用一個100MB的文件測試一切正常。然后換成一個1.2GB的文件第一次傳輸就出了問題服務(wù)端接收文件到一定大小后拋異常提示文件提前結(jié)束。排查后發(fā)現(xiàn)客戶端在發(fā)送文件內(nèi)容的同時我把服務(wù)端的響應(yīng)邏輯寫在了文件內(nèi)容讀取完成之前。服務(wù)端一旦讀了幾個字節(jié)作為響應(yīng)看待整個消息結(jié)構(gòu)就亂了。這個坑的本質(zhì)是TCP的字節(jié)流沒有邊界如果你在接收文件內(nèi)容時還試圖從同一個輸入流里提前讀響應(yīng)那讀到的其實(shí)是文件內(nèi)容的一部分。根源是因?yàn)槲募?nèi)容很大在底層會被拆成多個TCP包發(fā)送read第一次拿到的可能只有一部分導(dǎo)致代碼邏輯把后面的某些字節(jié)誤判為標(biāo)志位。解決方式也很簡單嚴(yán)格執(zhí)行協(xié)議順序。文件內(nèi)容的讀取循環(huán)必須跑完totalRead fileSize然后才允許服務(wù)端從輸入流讀取后續(xù)內(nèi)容??蛻舳艘脖仨氂胹hutdownOutput明確表示發(fā)送結(jié)束才能進(jìn)入等待響應(yīng)階段。順序?qū)憣α苏嘲氚鼏栴}就從設(shè)計上被壓住了剩下交給readFully去兜底。5.2 long和int的選擇2GB限制第一次設(shè)計協(xié)議頭時我圖省事文件大小用的是int。測試到1.8GB一切正常但傳到2.3GB時客戶端報告發(fā)送了-1字節(jié)服務(wù)端那邊還沒收到數(shù)據(jù)就已經(jīng)報錯。后來查了一下問題就是int溢出2.3GB換算成字節(jié)已經(jīng)超過了int的最大值約21.47億算出來的字節(jié)數(shù)變成了負(fù)數(shù)整個發(fā)送循環(huán)直接崩潰。教訓(xùn)很清楚凡是和文件大小相關(guān)的字段一律用long。Java的數(shù)組下標(biāo)仍然是int限制但你真正要寫進(jìn)Socket的是分片不是整個文件的byte數(shù)組。用long記錄總大小、用int記錄每次的分片大小兩者組合起來就沒問題了。這個教訓(xùn)也提醒我協(xié)議設(shè)計階段最好把字段寬度一次性想清楚。文件大小、偏移量這些天然有變大趨勢的字段寧可用8字節(jié)的long也不要為了省幾個字節(jié)埋雷。5.3 客戶端斷網(wǎng)之后服務(wù)端留下一堆半成品有一輪測試我模擬了客戶端中途斷開上傳到一半直接切斷了網(wǎng)絡(luò)。服務(wù)端那邊沒有及時感知到客戶端斷開read方法在等著數(shù)據(jù)直到TCP超時重傳徹底失敗后才拋出SocketException。這個過程中服務(wù)端已經(jīng)創(chuàng)建了目標(biāo)文件并寫入了一部分?jǐn)?shù)據(jù)異常發(fā)生后這部分就是殘留的半成品文件。處理方案分兩步第一步在handleClient方法里記錄本次任務(wù)的目標(biāo)文件路徑第二步在catch塊中捕獲所有IOException后把記錄的目標(biāo)文件刪除。刪除動作本身也可能失敗文件被占用或權(quán)限不足那就再想辦法做一次補(bǔ)償或者至少輸出一條足夠顯眼的日志提醒人工介入。另一個很實(shí)用的技巧是給服務(wù)端設(shè)置SO_TIMEOUT也就是讀超時。如果一定時間內(nèi)沒有字節(jié)到達(dá)就直接拋異常結(jié)束任務(wù)而不是無限等待。文件上傳的場景里客戶端長時間不發(fā)送任何數(shù)據(jù)基本可以判定連接異常等待沒有意義。5.4 Windows和Linux路徑分隔符差異這個坑很隱蔽客戶端在Windows下把文件路徑D:\logs\access.log直接拼到協(xié)議里服務(wù)端在Linux下按/分割路徑文件名變成了D:\logs\access.log整個字符串。Windows下執(zhí)行File寫入時反斜杠會被當(dāng)作轉(zhuǎn)義字符的一部分結(jié)果文件名異常打不開目標(biāo)文件。我給的方案是在sanitizeFileName里統(tǒng)一過濾所有\(zhòng)\和/提取最后一個分隔符之后的部分作為文件名。這樣客戶端傳D:\logs\access.log過來服務(wù)端寫出的文件名就是access.log。如果確實(shí)需要保持目錄結(jié)構(gòu)那就讓協(xié)議再增加一個字段顯式表示相對路徑而不是依賴文件名里的分隔符自動解析。6. 進(jìn)階方向并發(fā)、續(xù)傳與更高效的數(shù)據(jù)通道6.1 多客戶端并發(fā)上傳的線程池參數(shù)如果同時上傳的客戶端有好幾個服務(wù)端的線程池參數(shù)就不能隨便寫了。前面用availableProcessors() * 2只適合CPU密集型的推算文件傳輸其實(shí)是IO密集型任務(wù)線程數(shù)可以略多。一個比較穩(wěn)的起步配置是ThreadPoolExecutor pool new ThreadPoolExecutor( 4, 16, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100), new ThreadPoolExecutor.CallerRunsPolicy() );核心線程數(shù)4最大線程數(shù)16隊列容量100。超出隊列容量時CallerRunsPolicy讓提交任務(wù)的線程自己執(zhí)行這比直接拋拒絕異常更溫和至少不會讓客戶端瞬間收到失敗。但也要注意如果并發(fā)太高IO和磁盤會成為瓶頸線程再多也沒有收益反而增加上下文切換的成本。實(shí)測幾GB大數(shù)據(jù)并發(fā)時磁盤寫入速度通常是第一瓶頸并發(fā)數(shù)控制在磁盤能承受的范圍內(nèi)才是對的。6.2 斷點(diǎn)續(xù)傳協(xié)議加offset服務(wù)端改用RandomAccessFile斷點(diǎn)續(xù)傳的關(guān)鍵是讓服務(wù)端知道這次從哪里繼續(xù)寫。在協(xié)議頭里增加一個8字節(jié)的offset字段客戶端發(fā)送前先和服務(wù)端確認(rèn)已有文件大小然后從偏移位置開始讀取并發(fā)送文件剩余部分。服務(wù)端接收時改用RandomAccessFile打開文件后先seek(offset)再按循環(huán)寫入文件體。這里要注意校驗(yàn)策略也要跟著變?nèi)绻募粋髁艘话刖蛿嗔苏募﨧D5還沒算出來續(xù)傳后的MD5計算必須在文件補(bǔ)齊之后才能做。另一種做法是傳輸時對每個分片計算CRC32服務(wù)端每收到一片校驗(yàn)一片這樣不用等整個文件送完就能及時發(fā)現(xiàn)損壞。代價是協(xié)議頭里需要再增加分片序號和分片CRC字段復(fù)雜度上了一個臺階。如果只是作為學(xué)習(xí)案例先把offset機(jī)制實(shí)現(xiàn)出來就夠理解了。真正的生產(chǎn)級續(xù)傳還要解決服務(wù)端如何知道文件已有多少數(shù)據(jù)的問題常見做法是客戶端先發(fā)一個查詢請求或者協(xié)議頭里帶上服務(wù)端文件的舊大小服務(wù)端比較后決定是覆蓋、跳過還是續(xù)傳。6.3 零拷貝數(shù)據(jù)通道FileChannel.transferTo前面的代碼用的是傳統(tǒng)InputStream/OutputStream復(fù)制每個字節(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ā)送端的核心代碼可以簡化為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í)測過使用零拷貝之后同文件傳輸?shù)腃PU占用幾乎下降了一半對于大批量文件同步的場景提升非常明顯。零拷貝也不是銀彈如果文件特別小、或者網(wǎng)絡(luò)非??於疟P很慢收益就無法體現(xiàn)。但作為一種進(jìn)階優(yōu)化思路它確實(shí)是值得了解的經(jīng)典技術(shù)。6.4 還能往哪里擴(kuò)展這個案例做完之后可以延伸的方向很多。最容易想到的是加密用TLS替換裸的Socket或者對文件內(nèi)容做AES加密后再傳輸解決明文數(shù)據(jù)在網(wǎng)絡(luò)上被截獲的問題。其次是壓縮在客戶端讀文件時用GZIPOutputStream包一層文件變小了傳輸自然變快代價是CPU占用增加。再往下就是秒傳客戶端先發(fā)送文件MD5服務(wù)端如果已經(jīng)有相同摘要的文件就直接返回成功不需要真的傳輸數(shù)據(jù)。還有一個值得嘗試的方向是傳輸限速和失敗自動重試。限速可以防止一個上傳任務(wù)占滿整個出口帶寬影響其他服務(wù)失敗重試則要和斷點(diǎn)續(xù)傳結(jié)合才能做到斷網(wǎng)恢復(fù)后自動接著傳。把這些機(jī)制一個個加進(jìn)去一個簡單的文件上傳Demo就會慢慢變成一個真正能扛生產(chǎn)環(huán)境的小型分發(fā)系統(tǒng)。這個案例本身不算復(fù)雜核心就是一段清晰的協(xié)議、一個按部就班的接收循環(huán)、加上對異常邊界的處理。但把它真正跑通、跑穩(wěn)你會發(fā)現(xiàn)對TCP通信的理解會扎實(shí)不少以后再碰到消息隊列、RPC框架里的傳輸設(shè)計和粘包處理背后都是同一套原理。如果實(shí)際操作中遇到了奇怪的問題優(yōu)先從消息邊界是否分明順序是否嚴(yán)格異常是否被吞掉這三個角度排查大概率能很快找到答案。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
99热在线观看| 五月天色丁香| 色婷婷丁香AV综合| 97丁香五月| 婷婷开心激情五月激情网| 久久五月婷婷丁香| 偷拍91九色| 99精品人人| 色综合久久88色综合天天看| 久久精品9| 色五月婷婷网| 超碰在线资源| www婷婷| 天天狠狠夜夜狠狠2023| ..真实国产乱子伦毛片| 婷婷午夜综合| 久久婷婷欧美| 91无码高清| 激情综合国产| 日韩国产在线精品| 国产在线中文字幕| 丁香五月六月婷婷殴美综合| 大香蕉手机视频| 开心激情综合| 五月丁香婷婷狠狠操| 五月亭亭色| 啪啪啪大香蕉| www,天天干| 五月丁香啪啪网| 中文字幕无码人妻少妇免费视频| 丁香久月婷| 久久九色| AV六月丁香| 五月天大香蕉| 色爱亚洲| 午夜天堂一区人妻| 久久婷婷色情7777网站| 五月天色视频| 五月婷婷色情| Caoub青青超碰 | 色久婷婷五月| 五月婷婷激情综合网 | 色婷婷丁香五月| 九九RE视频在线精品| av一级棒av| 99se丁香| 人妻乱码久久久| 激情六月天婷婷| 伊人久久婷婷| av操B网站| 91婷婷丁香| 人人干女人| 色色色色色色色色网站| 久久婷婷人人| 性做爰1一7伦| 26.uuu丁香五月婷婷| 五月丁香综合网色欲| 狠婷婷五月| 99精品无码视频| 人人添人人| 五月天社区婷婷丁香社区| 五月婷婷人人人操| 色婷婷六月| 性做久久久久久久免费看| 丁香深五月婷婷| 伊人五月天综合网| www.婷婷五月| 亚洲国产黄色电影| 中文字幕性爱丰满| 天天爱天天狠天天透| 综合色色综合| 色婷婷五月天天天天天天天天天| 激情五月综合| 久草婷婷视频| 丁香五月婷婷影院| 国产精品涩涩涩视频网站| 中文在线视频久1| 天天操天天谢| 91欧美| 婷婷激情六月综合| 五月综合无码| 性爱五月婷| 亚洲熟妇AV乱码在线观看| 99热成人精品| 91操碰| 五月婷婷综合影院| 成人免费va| 色噜噜狠狠色综无码久久合欧美| 婷婷五月在线综合| 狠狠狠狠狠狠| 亚洲色图45p| 五月花在线观看视频| 五月丁香激情啪啪| 久久婷婷六月综合综合| 九月丁香网婷婷| 91日日日| 91婷婷五月天嫩女| 老司机视频lsj爱就色| 国产精品噜噜在线视频| 五月激情站| 久草 天堂| 丁香六月激情综合| 97丁香五月| 婷婷区日本| 夜夜干夜夜操| 国产xxxxx在线观看| 色五月婷婷网| 亚洲AV成人一区二区在线观看| 五月色婷婷综合丁香精品无遮挡| 99热在线精品观看| 五月天婷五月天综合网小说首页-五月天激激婷婷大综合,婷婷亚洲综合五月天小说 | www.99久久久久99| 五月亭亭网成人在线视频| 99热香港| 99热这里只有精品2| A久久| 综合五月草| 激情色色色| 99er这里只有精品视频| 禁欲电影完整版在线播放| 色播婷婷大香蕉| 熟女网站久久| 欧美色图45678| 91久久久久久久久18| 久久久性爱视频| 九九性爱网| 97黑人精品区| 婷婷亚洲综合| Www99热| 色婷婷成人| 欧美三级巜人妻互换| 综合久久丁香婷婷,五月婷婷六月丁香,开心激情综合网,六月丁香在线观看,婷婷丁 | 久久狠狠干| 免费AV黄在线播放| 亚洲AV日韩无码| 欧美啪啪五月天| 亚洲VA口| 久久永久网址| 日本综合99| 日本 @ va 免费| 午夜]香婷婷深深爱| 五月激情丁香久久综合网| 久久最新色| 情色五月天网站| www.99热视频| 六月婷婷色宗合| 婷婷五月另类网站| 丁香激情网| 九九热色视频| 79精品视频在线观看,| 综合激情网五月激情| 欧美色频| 婷婷五月天a| 狠狠精品干练久久久无码中文字幕| 欧美色小说婷婷| 农村熟妇高潮精品A片| 五月天激情婷婷丁香| 伊人五月天| 色婷五月| 大香蕉 婷婷| 99亚州综合精品成人网| 99ri久久| 色婷婷六月| A网在线欧洲| 五月丁香婷婷综合| 丁香五月天无码| 伊人香大香蕉视频| 久久婷婷精品| 天天操天天草天天草天天| 99热最新国内| 九九久久精品| 99热这里只有精品在线播放| 亚洲高清在线| 色婷婷av综合网| 79精品视频| 精品牛仔裤超碰| AA丁香综合激情| 久久99免费视频| 婷婷99中文字幕| 伊人啪啪网| 五月婷婷六月丁香综合| 九九成人电影婷婷| 人人摸人人搞| 六月丁香色婷婷| 超碰av天堂| 超碰在线观看99| 99riAV国产精品视频| 婷婷色情网| 一区二区三区XXXXXX| 色五月欧美| 五月激情婷婷六月丁香| 性视频久久| 亚洲精品V天堂中文字幕| 夜丁香综合| 丁香六月综合激情| 99色色网| 综合久久综合久久| 江苏少妇性BBB搡BBB爽爽爽| 丁香六月久久| www.色多多婷| 天天日日夜夜| 婷婷在线综合| 亚洲色激情| 五月婷婷影| 麻豆AV一区二区三区| www.色九月| 四虎婷婷五月天| 九月激情综合| 久久九九经典| 国产麻豆视频| 天天插天天插| 激情五月天电影| 婷婷五月天激情综合深爱激情| 99re66热这里只有精品| 99热色婷婷| 全部老头和老太XXXXX| 久久99激情| 五月婷婷福利| 狠狠干婷婷| 91精品91久久久久77777| 超碰操网| 婷婷五月六月丁香| 五月婷婷网五月在线| 亚洲激情亚洲激情| 色色丁香婷婷| 久久综合婷婷五月| 91刘玥视频在线观看| 国产毛片欧美毛片久久久| 九九九九毛片| 99操免费视频| 日本婷婷| 五月天狠狠| 婷婷综合五月色播| 大狠狠在线| 天天日天天添| 久9无码视频| 久草视频大香蕉99| 久草热8精品视频在线观看 | 久久婷鲁| 日操熟女| 婷婷五月丁香五月| 99日本精品视频热| 天天肏在线观看| 79精品视频在线观看,| 激情五月六月丁香| 思思热再线视频| 婷婷五六月丁香| 亚洲无码另类| www.综合久久.com| 大香蕉欧美在线| 婷婷丁香五月基地| 丁香五月婷婷综合激情啪啪啪啪啪啪啪| 农村熟妇高潮精品A片| 欧美婷婷五月丁香| 五月丁香偷拍| 五月天色色色| 欧美大肥婆大肥BBBBB| 狠狠的日| 啪啪啪大香蕉| 被强行糟蹋的女人A片| 99在线免费视频| 91丨九色丨大屁股| www.99热| 日本一级一级一级一级| 狠狠色情婷婷| 99色色| 五月开心网| 天天干天天 亚洲| 精品色情一区二区三区四区| 色综合天天| 99久久免费性爱视频`| av久热| 五月丁香久久久久| 九九色婷婷| 人人摸人人操人人爽| 婷婷综合97| 久9久成人精品视频| 久婷久婷| 天天日天天舔天天摸| 91九色视频| 久久久久婷婷| www99热| 亚洲小视频免费播放| 久操操| 亚洲艹网| 激情亚洲色图片丁香综合| 天天爱天天做综合| 日日日影院| 久久久精品人妻录| 另类激情五月在线视频欧美| 女同激情久久av久久| 五月丁香婷婷基地| 大香蕉婷婷| 婷婷伊人网| 99热这里| 日本三级99人妇网站| 99久操视频| 婷婷五月在线| 婷婷的99视频网站| 五月丁香啪啪激情| 亚洲欧美国产高清vA在线播放| 五月天最新网| 婷婷操逼| 五月丁香婷婷激情视频| 任你日热视频| 人人爱人人草| 成人va在线播放| 99九无网码| 思思热精品在线| 中文成人在线| 99热69| 婷婷五月天堂| 激情深爱综合| AV变态另类一区二区| 婷婷激情视频| 亚洲小说欧美激情| WWW色色色COm| 国产精品第一国产精品| 色九月婷婷丁香| 日撸夜撸日操| 亚洲综合色网| 婷婷五月色| 婷婷在线网| 色五月天丁香婷婷色| 丁香六月综合激情| 色播五月婷婷| 欧美婷婷丁香五月| 狠狠五月天| 亚洲视频99| 精品国产乱码久久久久久免费| 九一九九黄色| 色一区高清| 丁香九月婷婷色| 天堂色婷婷| 涩涩五月天| 久久桃花网色婷婷| 婷婷99视频精品| 色五月在线| 五月激情婷婷丁香| 激情五月天网| 91九色精品| 亚洲九九免费| 久久精彩视频| 大战熟女丰满人妻AV| 免费啪啪啪网站| 欧韩性爱| 99热这里只有精品8| 久热 91| 五月日韩中文字幕| 久久色吧| 天天日,天天干,天天操| 九九热精品在线| 热久久思思热思思| 九九婷婷综合| 久色大香蕉| 九九亚洲视频| 大伊久久| 五月婷视频| 亚洲无码色| 另类视屏| 人人看人人摸人人| 色婷婷五月综合| 天堂网色色| 天天色综网| 六月婷婷色综合| 天天爽在线视频| 色婷婷丁香特级性爱视频| 香蕉狠狠爱视频| 嫩模草| 天天曰夜夜爽| 26UUU欧美激情一区二区| 婷婷丁香五月天综合在线日韩| 亚洲操逼片| 天天撸天天干天天插| 日韩九区| 婷婷五月亚洲综合| 五月天成人在线视频网站| 夜夜嗨一区二区三区直播内容| AV在线观看网站| 人人舔人人| 丁香婷婷色五月| 97操碰在线视频| 97欧美在线| 色综合伊人网| 日本不卡高字幕在线2019| 超碰婷婷色| 日本五月丁香| 99热这里只有99| 五月天成人综合| 天天综合久久| 亚洲无码黄色| 《久久综合九色综合97婷婷| 9色免费网| 激情五月天情色| 五月丁香 六月婷婷a| 夜色综合网| 日韩AV在线免费| 日在线V视频在线播放| 婷婷丁香五月天激情| 国产精品视频| www久久久久久久| 99热| 色色色色色综合| 丁香婷婷六月天| 日韩九区| 97久久人人| 色五月婷婷五月天| 九九www| 99热在线成人网站| 91日婷婷在线| 六月丁香六月婷婷欧美| 丁香九色不卡aaa| 激情宗合网激情五月天| 色五月婷婷7777| 四五月婷婷| 婷婷久久综合| 久久婷婷五月国产激情综合片| 婷婷的久久网站| 日本一级特黄大片AAAAA级| 色五月激情五月| 久久一级免费黄色片| 可以观看的AV| 国产成人亚洲综合亚洲| 开心五月激情婷婷| 特黄三级又爽又粗又大| 五月天婷婷色综合| 日本九九视频| 91热视频色网站| 伊人久久大香线蕉av最新| 九九综合九九| 中字幕视频在线永久在线观看免费| 亚洲国产无线乱码在线观看| 色99最新网址| 香蕉99网| 婷婷久久99| 插插干干干色| 色欲天天综合网| 熟女激情网| 99热这里| 青草激情在线| 午夜免费高清AV片| 最新激情五月天| 色 免费网站视频| 激情婷婷五月久久| 91精品久久久久久综合五月天| 97在线观视频免费观看| 国产综合视频婷婷| 噜噜噜噜婷婷五月天| 亚洲综合成人网| 丁香五月骚喷水视频| 青青草青青草五月天| 无码激情精品色婷婷久久久久| 610018岁成人视频| 五月婷婷丁香社区| 激情四射婷婷| 丁香六月婷婷久久综合| 丁香婷婷在线| 色色色在线| 草草视频91| 青青操绿aaa一区日v| 大色鬼综合| 亚洲性爱99在线| 亚洲天堂制| 成人版视频在线观看| 91九色首页| 午夜不卡久久精品无码免费 | 91操人视频| 婷婷五月丁香六月伊人网| 九九国产精视频| 99热最新网址| 丁香五月影院| 东京热人妻一区二区三区在线| 色三级色三级| 国产精品久久久60086| 天天爽综合网| 五月婷婷五月天| 婷婷激情97| 天色综合网站| 国产探花一片区| 91久久久久久久久| 久久这里只有精品无码| 神马欧美精| 五月天偷拍| 欧美在线干| 婷婷五月免费观看| 欧美日韩aaa| 久久婷婷综合五月天| 丁香婷婷五月天网站| 一本婷婷丁香久久| tingtingjiqingwuyue| 国产视频色色色色色色色 | 五月天婷婷丁香花| 九九热免费视频| 激情五月开心五月丁香五月| 久久这里只有精彩| 五月天婷婷丁香人人操91| 91性高潮久久久久久久久| 天天插天天日天天爽| 国产操肏网站| 丰满少妇猛烈A片免费看观看| www天天色天天射| 亚洲色五月| ZpRSw| 激情丁香婷婷六月天| 天天操婷婷| 亚洲激情四谢| 五五月丁香花激情综合网| 久久人妻超碰一区| 色婷婷五月在线| 日本婷婷丁香五月| 久久伦乱| 91九色超碰| 久99久99精品免| 丁香五月婷婷啪| 亚洲精品九九| 色综合久久888| 亚洲va在线| 综合亚洲六月婷婷在线| 丁香天堂夜| 精品一二三区久久AAA片| 久久婷婷五月综合啪| 婷婷五月天99| www.天天色综合| 97自拍视频网| 天天干天天爽| 久热伊人| 免费看欧美成人A片无码| 天天干天天玩天天夜天天射天天操天天日蜜臀少妇 | 97色伦另类图片小说视频| 九九热大香蕉| 伊人激情网| AA片在线观看视频在线播放| 婷婷色导航| 五月丁香六月婷婷激情视频在线观看免费 | 五月色精品| 色噜噜狠狠色综合无码久久欧美| 色综合色色色| 色噜噜狠狠色综无码久久合欧美| 久久人妻高清中文| 五月天堂婷婷| 五月天婷婷黄色视频| 日韩黄色网络| 无码一级片| 婷婷色导航| 黄色片区子| 高清无码入口| 五月花综合视频| 色婷婷综合五月| 五月丁香啪啪| 91尤物九色在线| 日本三级色| 爱iii做iiii日| 欧美交换配乱吟粗大25P| 丁香色色网| 久草热在线视频| 激情网婷婷婷| 久久九九蜜| 六月丁香婷| 日韩 中文 欧美| 人人妻人人澡人人爽| 婷婷五月丁香色色| 丁香六月婷婷基地| 丁香五月婷婷av影院| 精品一二三区久久AAA片| 99热这里只有精彩| 色五月在线观看| 97人妻碰碰碰久久香蕉| 97丁香五月| 色婷狠狠| 丁香桃色网| 天天开心AV色综合婷婷五月天| 天天插综合在线| 9er热在线精品视频| 丁香婷婷性爱| 久久之人妻| 综合色色网| 日本精品99| 亚洲性视频| 九九精品99久久久| 99九九视频| 丁香密臀AV激情网| 大香蕉久久久| 999婷婷综合| 亚洲天堂爱爱| 五月六月播婷婷| 亚洲色情久久| 亚洲婷婷视频| 色欲色天天香综合| 五月丁香激情四射| 婷婷综合五月| 色久播播| 五月丁香999| 五月深爱婷婷| 色色色欧美色色| 婷婷丁香激情| 国产婷婷五月色情综合| 婷婷亚洲综合| 婷婷五月丁香久久| 国产 亚洲 在线| 色五月婷婷丁香五月| 国产欧洲欧洲精品久久| 欧亚成人A片一区二区| 五月丁香啪| 国产69精品久久久久999小说| 不卡在线中文字幕无| 精品久9| 色婷婷亚洲综合av| 久久久久8888| 欧美六月婷婷| 91婷婷伊人牛牛| 啪啪婷婷五月天激情| 婷婷永久在线| 色。 日日日| 色爱终和网| 操嫩逼电影| 五月天另类小说| 99热免费精品| 亚洲欧洲另类| 激情六月天| 97超级碰人人| 大香蕉七区| 99操逼| 久热这里只有精品性色AV| 天天摸天天肏| 99亚洲大片精品永久在线观看 | 日日操人人操| 99综合网| 色婷婷五月综合| 成人视频网| 人妻有码乱操| 丁香五月天黄色片| 狠狠人妻久久久久久综合丁香| 五月天婷网| 性天堂久久| 丁香五月婷婷基地| 97在线观视频免费观看| 丁香五月综合久久| wWwCom夜操wwW| 五月丁香婷婷伊人| 五月天婷婷伊人| 九九色色| 亚洲精品中文字幕成人片| 亚洲精品成人| 五月丁香六月婷婷亚洲| 97碰| 超碰在线观看三级片| 精品九九网| 国产亚洲精品人人| 丁香五月激情六月综合| 综合网啪| 色五月丁香com| 天天操中文字幕| 日日噜噜久久婷婷五月天 | 五月婷婷亚洲综合在线 | 99婷五月| 久久狠狠欧美| 五月天综合激情网| 思思热在线观看| 在线sebiav精品视频| 久99久在线观看| 中文aV网| 97操碰在线视频| 天天做天天爱天天爽| 91国产精品视频播放| 99色婷婷视频| 久久激情五月网| 夜夜夜夜夜操| 久久婷婷电影| 桃色激情婷婷伊人网| 99国产精品久久久久久久久久久| 亚洲精品无AMM毛片| 五月婷婷丁香大陆免费| 久久99美女精彩视频| 激情床戏| 亚洲一二三网| 噜噜噜噜噜日本视频| 天天天天天天噜| 亚洲va综合va国产va中文| 国产亚洲色婷婷久久99精品91 www.riverspirits.org www.hnnun.com www.changh | 99热精品中文字幕| 国产亚洲精品久久久久久郑州| 9九色首页| 中出内射的人妻视频| 99re热视频这里只精品| 激情内射人妻1区2区3区| 成人欧美日韩| 天天噜噜| 色色激情五月天| 香蕉视频性爱BB做爱| 色综合色婷婷色伊人| 丁香色成人| 色婷婷av综合网| 九九视频精品这里只有| 大香蕉婷婷丁香天堂AV| 国产亚洲av片| 五月综合激情视频| 国产av天堂| 久久久久9| 欧美激情VA永久在线播放| 五月婷婷色在线| 日操| 色综合99| 9191avse| 婷婷五月天激情网| 久久99免费视频| 天天综合亚洲综合| 狠狠精品干练久久久无码中文字幕 | 婷婷色五月激情| 十一月婷婷激情四射| 夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂亚洲亚洲亚洲亚洲亚洲亚洲亚洲亚洲色 | 97日本在线播放| 五月六月丁香婷婷在线观看| 色综合色色色| 九九精品99| 91丁香五月| 久久人五月| 丁香五月婷婷成人网| www.99视频| 区区久久妻| 97狠狠色| 久久婷婷91| 亚洲99在线| 停停综合色色| 九九视频在线| 九九久久精品國產| 亚洲热热视频| 男女啪啪做爰高潮无遮挡| 丁香五月AV在线| 久久HD| 激情五月开心五月在线视频| 天天做夜夜爽| 激情五月天久久丁香| 婷婷丁香色五月| 少妇高潮一区二区三区99欧美| 综合99综合久久久久久久| 色婷青青| 色五月视频,小说| 激情综合网激情五月俺也去| 婷婷激情5月| 丁香六月天婷婷色| 丁香五月天视频在线播放| 丁香婷婷激情网站| 四虎婷婷五月天| 国产精品色情AAAAA片软件| 日日色五月天| 中日韩美欧成人一区二区精品在线| 激情五月婷婷在线区| 色五月激情五月| 天天摸,天天爽| 日本三级日本三级99| 99这里只有精品视频免费| 99热精品在线| 这里只有精品视频视频在线观看| 亚洲色综合| 一本久久亚洲五月婷婷| 日本色色影片| 丁香色综合| 9超碰在线| 九九热10| wWwCom夜操wwW| 婷婷激情四射| 婷婷五月天无码视频| 五月婷婷色欲| 五月丁香六月婷婷在线| 五月婷婷九九热| 99成人| 六月婷婷综合| 九九精品自拍| 丁香六月婷婷综合啪啪| 另类激情五月在线视频欧美| 五月丁香福利| 超碰久热| 思思热国产| 五月激情久久| 色热久| 超碰三级秋霞| 99综合五月免费视频色婷婷| www夜夜操wwwcon| 亚洲蜜乳AV| 99热99在线| 亚洲五月婷婷在线| 99婷婷综合| 天天做好综合色| 婷婷中文无码| 97精品人人A片免费看| 五月婷婷伊人网| 国产精品A片在线| 欧美天堂久久| 2025中文在线视频字幕免费观看| 噼里啪啦完整版中文在线观看| 久久久久久五月天| 超碰色色综合| 久久视频在线视频| 五月天成人网在线观看| 超碰国产在线| 五月天丁香啪啪网| 五月综合丁香婷婷| 日本婷婷丁香五月| 色婷婷五月影视| 欧美日韩91| 五月天自拍视频| www.com亚洲网站在线免费| 国产色色视频| 久久婷婷激情视频| 久热91精品| 国产全是老熟女太爽了| 五月天久久91| 99热每日| 任你艹| 色久综合天天做视频| 日本色99| 丁香激情网| 亚洲色热| 婷婷五月,综合伊人| 无码区婷婷五月花开| 婷婷激情蜜桃玖玖丁香| 97性视频| 丁香情色五月| 丁香五月中文字幕色播| oumeisesewang| 狠狠爱婷婷色| 9久热| 五月天另类小说久久小说网| 五月天激情图片| A片试看120分钟做受视频红杏| 呦呦v线| 色你久久| 97luluse| 涩涩五月天| 97深爱伊人综合| 婷婷涩涩五月天| 丁香五月婷婷激情97| av在线免费播放| www.色擼擼.com| 99色综合网| 美女伊人久久| 国产熟妇乱子伦hd| 久碰视频| 亚洲综合五月| 思99热精品久久只有精品| 91免费看片| 激情综合网激情五月丁香五月俺也去| 天天肏视频| 思思re99视频在线观看| 五月综合婷婷开心网| 色五婷婷| 夜色.cnm| 精品一二三区久久AAA片| 午夜成人天堂久久无码日韩久久| 久色国产| 久久这里面只有精品视频| 婷婷久久婷婷色五月| 人妻丰满精品一区二区A片| 91狠狠综合网| 特级片神马电影| 婷婷五月成人色综合| 超碰在线观看成人视| 久久久久久久11111111111| 亚洲视频99| 日本女人久久| www.henhengan| 99热免费精品热久久66| 4399在线观看免费高清黄色视频| 99riAV成人在线视频| 日本狠狠干| 九九热AV| 91超级碰在线视频| 永久精品| 99综合激情久久精品久久| 婷婷丁香六月影视| 大香蕉伊人99| 日本无码专区| wwwss在线观看| 九九视频这里只有精品| www.久久99热地址发布| 99热精品中文字幕| 天天情色综合网| 成人网站免费sxj| 琪琪理论片| 欧美天天草人人草| 91超碰在线观看| 丁香婷婷色情| 亚洲无码你懂的| 色婷婷亚洲综合网站| 日本九九九九| 综合图片色色| www狠狠| 五月丁香天堂网婷婷| www.25五月婷婷| 天天综合网91| HD久久精品视频| 欧美日韩国产一二区| 秋霞少妇AV网站| 色伦专区97中文字幕| 婷婷色五月天在线观看| 亚洲综合99| 五月婷婷激情| 日韩啪| 99热在线观看精品| 色日本网| 五月天婷婷中文字幕在线播放| 六月婷婷激情小说网| 六月婷婷狠狠做| 99热在线这里只有精品| 日韩人妻无码一区二区| 久久精品99| 夜夜大香蕉婷婷丁香| 99ri精品在线| 色色A| 亚洲第一第二网站| 玖玖精品视频| 天天影院色| 五月激情天| 国产超碰在线| 成人片黄网站色大片免费毛片| 亚洲乱码精品久久久久..| 99久久国产宗和精品1上映| 欧美在线| 亚洲成人AV高清字幕| 婷婷五月花.97| 婷婷影视久久| 中文字幕人妻AV| 91色性感五月婷婷丁香| 国产欧美日韩综合精品一区二区| 亚洲中文字幕AV在线| 五月天丁香婷婷视频网址| 亚洲AV日韩在线观看| 国产精品黑丝| www.99.色| 色婷婷六月| 天天操夜夜爽歪歪| 香蕉综合在线| 夜色.cnm| 色婷成人狠干| 狠狠色噜噜狠狠| 久草A片| 色五月婷婷激情综合网| 亚洲色婷婷网站| http://www.lingjunshare.com/| 久久永久网址| 成人色五月天婷婷| 色五月激情五月丁香五月婷婷啪啪综合| www.91.com处女在线直播| 69堂午夜视频最新地址| 激情桃色网 | 4399成人黄A片| 久色资源| 日本乱子人伦在线视频| AV成人在线网站| Y11111111111少妇电影院| 99热销国产这里有精品| 99啪啪视频| 开心久久爱五月天| 五月婷婷开心综合| 久久这里都是精品| 91avse| 五月天婷婷色色| 久久久精品免费啪啪国| 伊人玖玖婷婷| 天天玩夜夜操天天爽| 丁香香五月激情免费视频| 色色色视频| 五月天婷婷丁香六月| 丝袜激情网| 久久久ww| 成 人片 黄 色 大 片| 97超碰色| 99ri国产| 五月天激情国产综合AV| 天天爽天天日| 人人草人人爱| 九九视频在线| 人五月天婷婷喷水| 97人人做| 99热在线中文字幕| 精品人妻伦一二三区久久| 久久激情五月| 九九爱精品网站| 婷婷五月丁香五月基地| 中文字幕,综合,91| 免費亭亭成人| 久久精彩免费视频| 五月丁香激情综合网| 欧美性猛交XXXX乱大交极品| 久久日韩婷婷五月| 91超碰九色| 综合久久五月| 亚韩在线视频| yazhoujiqingav| 成人av在线网址| 激情网第四色| 婷婷色狠狠| 91九九热| 少妇婷婷五月天| 日本3级片一区2区| 99ri视频在线观看| 丁香激情网| 九九色video| 天天开心天天色| 激情五月丁香综合网站 | 五月天婷婷色在线视频免费观看 | 欧美色九| 开心五月婷婷在线视频免费观看| 情色五月天网站| 成人无码精品1区2区3区免费看| 涩涩涩,com| 色久五月| 婷婷伊人久久| 日日操夜夜爽| 99热国产在线| 99色在线| 五月天丁香色色| 九九这里只这里只有精品| 色色色区| 色婷婷六月天在线| 91viP在线看| www.色99| 亚洲色区17| 久久曰曰| 五月丁香六月综合激情无码软件亮点| 色婷婷97| 玖玖热99| 精品福利911| 九九激情综合| OUMEIRIHANCHENGREN| 日本久久婷婷| 99视频日韩| 婷婷久久五月天中文字幕在线观看| av在线免费播放| 婷婷噜噜| 五月在线| 婷婷激情人妻| 79色色色色| 免费看欧美成人A片无码| 日韩狠狠色| 五月婷婷激情69| 亚洲综合九九| 91vip在线观看| 五月丁香六月欧美综合网站| 国产99久久久国产精品免费看 | 狠狠九九婷婷韩| 日韩婷婷五月| 五月婷在线观看| 色域五月丁香| 五月天久久婷婷婷| 丁香五月在线| 9人人操人人看| 婷婷五月天激情诱惑| 91久久日日| 亚洲欧洲色色| 婷婷色网| 九九超碰人人| 五月婷婷网站| 丰满少妇乱A片无码| 五月综合视频在线| 丁香婷婷性爱| 亚洲色图啪啪| 久久性爰视频这里只有精品| 国色天香成人网| 九九精品99| 婷丁香五月天| 五月婷婷色色网址| 日本va欧美va欧美va| 婷婷五月丁香图片人人操| 亚洲 激情 中文| 99综合| 中文无码婷婷| 噜噜噜狠狠色综| 亚洲成人另类| 婷婷五月天伊人| se婷97| 99热精品在线播放| 婷婷丁香五月社区亚洲| 梁铮版《蜘蛛女侠》在线| 五月天久久婷婷| 色噜噜狠狠色综合日日| 久99久视频| 五月婷婷三级| 在线观看亚洲视频影院| Av在线不卡一区| www.91有码.com| 国产真实乱了老女人视频| 激情的五月婷婷蜜桃| 久久ww| 亚州激情网站无码| 天天日天天干天天操| 5月激情天| 五月天色不卡| 亚洲午夜精品久久久久久人妖| 色婷婷综合网| 九九青草热| 激情五月天综合网| 日本三级片片| 激情综合网色播五月| 日本三级毛片| 毛片毛片毛片毛片| 99草视频在线观看| 亚洲激情综合五月婷婷啪啪| 亚洲三A| 99爱无码| 青草视频在线观看视频 | 天天肏高清在线| 婷婷五月电影| 97色婷| 五月天激情小说婷婷| 五月婷婷六月天| 欧美天天综合网站上去吧| 九九热在线精品视频| 久久停停超碰| 另类在线| 亚洲精品小视频| 蒲京久久无码视频| 丁香六月欧美| 色色五月天丁香婷婷| 色色色五月婷婷| 五月激情啪啪| 五月丁香影视| 五月久久亚洲| 日韩欧美婷婷丁| 人人操人人爰人人一天天碰夜夜拍夜夜爽-中国A级毛片天天看天天谢… | 丁香婷婷婷| 五月丁香综合网| 99热在线精品播放| 色99热| 狠狠夜夜五月丁香| 五月天激情影院| 色五月婷婷视频| 色五月激情图片| 开心五月网 | 97偷拍对白视频| 色婷婷丁香五月| 免费AV播放| 少妇人妻偷人精品无码视频新浪| 五月天婷婷黄色| 9久热精品在线视频| 五月丁香婷婷人体| 亚洲成人无码专区| 婷婷五月六月丁香| 色啪综合| 五月天精品视频| 六月香五月婷| 丁香五月婷婷综合激情啪啪啪啪啪啪啪| 99国产在线| 国产五月天婷婷| 97操碰人免费| 色香欲综合| 婷婷久久国产视频| 中文AⅤ大全| 婷婷色情网| 狠狠色官网| 亚洲另类AV| 99热国产| 在线中文字幕视频| 丁香久久在线| 激情图片久久| 色婷婷成人丁香| av婷婷丁香| 五月婷婷久久综合| 五月婷无码| 中文字幕亚洲-区久久99婷婷| 超碰色综合| 国熟女视频| 伊人9在线| 天天干天天干天天| 性天天中文网| 丁香五月婷婷啪啪啪| 97久久人人人干| 亚洲成人无码网站| 色的色综合| 2016日日夜夜操| 久久久GOGO无码啪啪艺术| 五月婷婷六月丁香在线| 久久婷婷网| 99精品偷自拍| 五月激情六月丁香| 丁香五月天人体| 久热伊人9| 成人综合网站| 99热碰碰| 99爱最新免费视频在线观看| 禁片二区| 97色干| 五月花婷婷丁香| 亚洲五月婷| 丁香色色五月| 久久这里只有精品99| 中文在线视频久1| 欧美久草在线日本一级特黄大片做受9在线观看韩国电影《两个女人》未删减-毛片 | 99热超碰在线| 精品五月花| 人人草人人舔| 婷婷五月天六月丁香| 色婷婷在线综合色播网| 欧美超级视频97| 婷婷情色开心五月天99| 99热这里只有精品1025| 亚洲激情电影五月天色婷婷丁香一起草 | 丁香婷婷五月天在线视频| 丁香六月婷婷缴情欧美| 国产九月婷婷| 五月婷婷av| 亚洲五月婷婷| 夜夜做天天爽| 97人人草| 国产九九一区二区三区| 欧美特大片黄| 亚洲六月色| 色色色色网色色网色色| 三区激情四射av| 99九九在线观看免费| 激情五月天久久| 五月天激情婷婷| 色欧美影院| 人妻VideOssS人妻高清| 精品网站99| 色三级色三级| 成人久碰| 五月丁香777| 能看的av| 色婷婷五月天天天干天天操天天爽| 五月天天丁香婷婷在线中| 五月婷婷色播| 色综合久久8| 五月天久久色| 婷婷丁香五月综合| 亚洲视频五区| 中文字幕色色色| 丁香婷最新动态| 9久热在线视频精品| 操操操91| 色呦呦在线| 无人区码一码二码三码医生系列| 五月丁香六月欧美综合网站| 182TV大香蕉| 五月天婷婷激情小说电影| 久久久久婷婷五月热综合| 99a级片| 亚洲爆乳无码精品AAA片蜜桃| 丁香九月激情| 99超级碰碰| 五月天色丁香| 五月成人综合| 激情五月丁香在线观看直播| 五月丁香色色综合| 色色五月天丁香婷婷| 综合爱久久| 婷婷香蕉香| 五月天婷婷婷| 97色婷婷| 欧美性生交XXXXX无码小说| 狠狠干,狠狠操| 情色五月天网站| h亚洲| 99精品免费| 五月激情网综合| 五月激情网五月综合网| 色五月丁香五月激情五月激情| 超碰在线国产| 爽极品色| 日韩狠狠色| 婷婷六月激情| 婷婷五月综合久久中文字幕| 久久性综合| 五月丁香六月婷| 六月婷婷五月丁香| 色色色色综合网|