久久亚洲成a人片熟女精品色一区二区三区|国产精品视频第一精品视频|av天堂热无码手机版|亚洲?v无码久久无遮挡|国产精品偷伦视频免费观看国产|麻豆国产自产精品丰满熟妇|av无码av不卡一区二区|久久亚洲精品中文字

ARTICLE DETAIL

資訊詳情

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

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

基于TCP的Socket文件上傳實(shí)戰(zhàn):協(xié)議設(shè)計(jì)與粘包處理 最近在幫朋友整理內(nèi)部服務(wù)器之間的日志同步方案時(shí)遇到一個(gè)非常典型的場景兩臺機(jī)器在同一內(nèi)網(wǎng)里需要把幾個(gè)GB的數(shù)據(jù)包從一臺傳到另一臺FTP服務(wù)沒搭、專門的HTTP上傳服務(wù)也沒人維護(hù)臨時(shí)裝個(gè)網(wǎng)盤工具又不現(xiàn)實(shí)。最后又繞回老路子——直接用TCP通信自己寫一個(gè)文件上傳工具。這篇文章就把這個(gè)綜合案例完整拆開講清楚從應(yīng)用層協(xié)議設(shè)計(jì)、服務(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. 動手前先想清楚這個(gè)案例到底在解決什么問題1.1 既要傳得對又要傳得完文件上傳這件事聽上去很簡單但放到TCP通信的場景里難點(diǎn)立刻浮出水面。TCP本身只保證字節(jié)流是可靠有序的它并不理解文件這種業(yè)務(wù)概念。你往Socket里寫一段字節(jié)對端讀到的可能是半段數(shù)據(jù)也可能是兩段數(shù)據(jù)黏在一起。所以這個(gè)綜合案例真正要解決的是在TCP提供的原始字節(jié)流之上設(shè)計(jì)一套能讓雙方都理解的消息協(xié)議把文件的名字、大小、內(nèi)容、校驗(yàn)信息完整地送達(dá)對端。另一個(gè)容易忽略的痛點(diǎn)是傳得完。小文件一次性讀進(jìn)內(nèi)存再write很快就完事但到了幾個(gè)GB的日志文件內(nèi)存根本扛不住必須用緩沖流分塊讀取、分塊發(fā)送。再加上網(wǎng)絡(luò)抖動、客戶端斷線、服務(wù)端磁盤寫入失敗這些情況一個(gè)看起來簡單的上傳工具實(shí)際上要把連接管理、流處理、異常恢復(fù)、資源釋放全部串起來。1.2 為什么這個(gè)場景必須選TCP而不是UDP文件上傳在傳輸層幾乎沒有選擇的余地必須用TCP。UDP雖然發(fā)送快、沒有連接維護(hù)成本但它不保證數(shù)據(jù)包的到達(dá)順序也不保證不丟包。如果基于UDP做文件傳輸要么自己實(shí)現(xiàn)一套復(fù)雜的確認(rèn)重傳機(jī)制要么接受文件內(nèi)容可能損壞的后果。相比之下TCP把擁塞控制、超時(shí)重傳、順序校驗(yàn)這些機(jī)制全部內(nèi)置了你要做的只是在流上面劃分消息邊界已經(jīng)省去了最麻煩的部分。不過這也引出一個(gè)很多新手會犯的錯誤覺得TCP可靠就可以把文件名、文件大小、文件內(nèi)容一次性write出去然后以為對端一定能原樣收到。這個(gè)理解是錯的——TCP的可靠是字節(jié)層面的可靠不是消息層面的可靠。發(fā)送方一次write比如write 1MB數(shù)據(jù)接收方可能分好幾次read才能讀完發(fā)送方連續(xù)write兩次接收方可能一次read就把兩段數(shù)據(jù)一起讀出來了。這就是后面要重點(diǎn)處理的粘包、拆包問題也是整個(gè)協(xié)議設(shè)計(jì)的最核心動機(jī)。1.3 邊界判斷什么時(shí)候該自研什么時(shí)候該用現(xiàn)成方案在做這個(gè)工具之前我也認(rèn)真想過是不是直接用成熟方案更省事。如果只是偶爾在內(nèi)網(wǎng)傳幾個(gè)文件SFTP、NFS、HTTP文件服務(wù)甚至直接掛個(gè)網(wǎng)盤都比自己寫Socket要快。但如果你面對的是批量機(jī)器之間的程序化傳輸、需要在傳輸過程中嵌入特殊的業(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ì)量出了問題沒有現(xiàn)成文檔能查全靠對TCP機(jī)制的理解。所以這個(gè)案例的定位不是替代FTP而是一種在可控場景下靈活定制的技術(shù)方案。理解清楚這一點(diǎn)你才知道哪些協(xié)議字段要考慮、哪些異常分支要處理程序才不會寫到一半失控。2. 應(yīng)用層協(xié)議設(shè)計(jì)先把字節(jié)流翻譯成消息2.1 協(xié)議頭結(jié)構(gòu)固定頭部加變長字段TCP是流協(xié)議沒有天然的消息邊界所以應(yīng)用層必須自己定義幀。我的做法是設(shè)計(jì)一個(gè)固定長度的協(xié)議頭后面跟著文件名和文件內(nèi)容。固定頭部長度的好處是接收方可以先讀固定字節(jié)解析出各個(gè)字段的寬度再按需讀取后續(xù)內(nèi)容。我設(shè)計(jì)的協(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整個(gè)文件內(nèi)容的MD5值用于完整性校驗(yàn)固定頭部一共40字節(jié)。文件名是變長字段跟在固定頭部之后隨后才是文件內(nèi)容。接收方拿到前40字節(jié)后解析出文件名長度再精確讀取文件名最后根據(jù)文件大小循環(huán)讀取剩余內(nèi)容。這里有個(gè)經(jīng)驗(yàn)文件名長度用int而不是short雖然實(shí)際場景里文件名幾乎不會超過65535字節(jié)但用int可以避免一個(gè)很隱蔽的邊界問題。Java的DataInputStream.readUTF內(nèi)部就是用short存長度的文件名稍微長一點(diǎn)就拋UTFDataFormatException排查起來很費(fèi)勁。既然協(xié)議是自己設(shè)計(jì)的就不要把這種限制帶進(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ù)字約束即使消息在底層被拆成了多個(gè)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);這個(gè)寫法是有隱患的read不保證一次讀滿40字節(jié)。必須改成byte[] header new byte[40]; in.readFully(header);不要小看這個(gè)差異實(shí)測中普通局域網(wǎng)環(huán)境還好跨交換機(jī)、跨網(wǎng)段傳輸時(shí)read一次返回不滿的情況非常常見這也是為什么我的接收數(shù)據(jù)總是缺一截的高頻根源。2.3 三個(gè)容易被忽略的協(xié)議細(xì)節(jié)第一個(gè)細(xì)節(jié)是文件名的編碼問題。文件名在傳輸前用UTF-8編碼成字節(jié)數(shù)組長度是字節(jié)數(shù)組的長度而不是字符串的字符數(shù)。接收方讀出字節(jié)數(shù)組后用UTF-8解碼恢復(fù)文件名。如果不統(tǒng)一編碼發(fā)送端用GBK、接收端按UTF-8解析中文文件名必然亂碼服務(wù)端寫出去的文件名也會出問題。第二個(gè)細(xì)節(jié)是目錄穿越。協(xié)議里收到的是客戶端發(fā)來的文件名服務(wù)端如果直接拿它拼路徑比如new File(baseDir, fileName)一旦fileName里包含../服務(wù)端的文件就會寫到baseDir以外這是嚴(yán)重的安全漏洞。服務(wù)端必須對文件名做凈化處理最簡單的方式是取文件名中的最后一個(gè)路徑分隔符之后的部分或者用白名單字符集過濾。第三個(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)錯誤回送給客戶端。這里的半關(guān)閉是關(guān)鍵它告訴TCP我的發(fā)送方向已經(jīng)結(jié)束但連接仍然可以用來接收數(shù)據(jù)。3. 服務(wù)端實(shí)現(xiàn)從accept到落盤的完整鏈路3.1 服務(wù)端骨架ServerSocket加線程池服務(wù)端要做的事情很簡單監(jiān)聽端口、接受連接、為每個(gè)連接分配合適的處理器。但簡單的背后有幾處要注意。一是監(jiān)聽的端口要設(shè)置合理過高如65535在生產(chǎn)環(huán)境容易被防火墻攔截過低如1024以下又需要權(quán)限。案例里我用9999這種高位端口。二是處理方式是單線程還是多線程如果只有一個(gè)客戶端傳文件單線程也夠用但實(shí)際環(huán)境里總會有多個(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)榇罅可蟼髡埱髮?dǎo)致服務(wù)端創(chuàng)建上千個(gè)線程直接打爆內(nèi)存。生產(chǎn)環(huán)境里我一般推薦用ThreadPoolExecutor手動指定核心線程數(shù)和隊(duì)列容量但在教學(xué)案例里固定線程池夠用規(guī)則也簡單。setTcpNoDelay(true)也值得說明它關(guān)閉了Nagle算法。Nagle算法會把小的數(shù)據(jù)包攢在一起發(fā)送以減少網(wǎng)絡(luò)報(bào)文數(shù)量但對于文件上傳這種需要頻繁寫入小塊數(shù)據(jù)的場景Nagle帶來的延遲會讓交互式確認(rèn)變得拖沓。文件上傳更看重吞吐關(guān)閉Nagle后小包的發(fā)送延遲明顯降低。3.2 協(xié)議解析與文件寫入的核心邏輯每個(gè)客戶端連接進(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個(gè)字節(jié)全部讀滿所以循環(huán)條件是基于totalRead fileSize每個(gè)分片按剩余字節(jié)數(shù)取need這樣既避免讀了下一幀的數(shù)據(jù)如果還有的話也保證了大文件能被完整讀完。3.3 路徑處理與服務(wù)端的安全考慮sanitizeFileName和resolvePath這兩個(gè)方法雖然代碼不多卻是服務(wù)端最值得花心思的地方。sanitizeFileName的邏輯是把傳入文件名的所有路徑分隔符替換為下劃線或者提取最后一個(gè)分隔符后面的部分。因?yàn)槲募膩碓词蔷W(wǎng)絡(luò)對端永遠(yuǎn)不能信任。同時(shí)還要過濾掉Windows下不合法字符比如:/\\|?*否則服務(wù)端落在Windows目錄時(shí)文件寫入會拋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ù)上傳適合批處理場景拒絕模式則避免了不小心覆蓋重要文件。你完全可以根據(jù)自己場景調(diào)整但安全邊界一定不能省。3.4 完整性校驗(yàn)與異常清理MD5校驗(yàn)放在文件全部寫入磁盤之后執(zhí)行。理論上應(yīng)該在內(nèi)存里邊收邊算摘要節(jié)省一遍文件的完整讀取但這樣對內(nèi)存的占用太高大文件場景不可行。案例里選擇的是接收完成后對落盤文件重新計(jì)算MD5再與協(xié)議頭攜帶的預(yù)期MD5比較。代價(jià)是文件會被讀第二遍增加了耗時(shí)好處是邏輯簡單、可靠性高。如果文件特別大可以折算一下耗時(shí)能接受就用不能接受就把校驗(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ā)完就斷開連接時(shí)文件往往只有原始大小的一部分這時(shí)候必須把半成品文件刪除并回送錯誤碼。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è)常見的坑。用的是Socket()無參構(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í)會卡很久用戶體驗(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的小文件問題不大但文件一旦到了幾百M(fèi)B甚至幾GBreadAllBytes會直接把內(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來自bis.read的返回值這個(gè)返回值決定了本次寫入的實(shí)際字節(jié)數(shù)。絕大多數(shù)情況下一次read返回的量不會超過64KB所以out.write一次也會把這個(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ù)端可能還沒有機(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)樵谕粋€(gè)Socket里先收到了結(jié)果字節(jié)而把結(jié)果誤當(dāng)成文件內(nèi)容的一部分導(dǎo)致協(xié)議錯亂。所以服務(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秒就會收到SocketTimeoutException不會永遠(yuǎn)阻塞下去。4.4 進(jìn)度感知與回滾提示進(jìn)度反饋并不是協(xié)議的一部分但卻是用戶體感的重要部分。一個(gè)上傳任務(wù)跑十分鐘沒有進(jìn)度提示用戶的第一反應(yīng)是程序卡死了。我這邊實(shí)現(xiàn)的方式是每發(fā)送5%就打印一次日志同時(shí)記錄最后的百分比避免重復(fù)打印。日志的粒度沒必要太細(xì)5%已經(jīng)足夠。如果打印太頻繁大量日志輸出本身也會消耗CPU和磁盤IO。實(shí)測下來5%到10%的粒度最舒服。進(jìn)度日志的另一個(gè)作用是可以作為故障排查的線索。如果一次上傳卡住了日志停留在某個(gè)百分比位置不動你就能判斷問題出在哪個(gè)階段是發(fā)送過程中網(wǎng)絡(luò)斷開還是服務(wù)端處理卡住還是等待響應(yīng)超時(shí)??床坏竭M(jìn)度時(shí)排查這種問題只能靠猜體驗(yàn)完全不一樣。5. 實(shí)測踩坑有些問題不跑一遍大數(shù)據(jù)根本想不到5.1 粘包半包的真實(shí)復(fù)現(xiàn)案例剛跑通的時(shí)候我用一個(gè)100MB的文件測試一切正常。然后換成一個(gè)1.2GB的文件第一次傳輸就出了問題服務(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é)流沒有邊界如果你在接收文件內(nèi)容時(shí)還試圖從同一個(gè)輸入流里提前讀響應(yīng)那讀到的其實(shí)是文件內(nèi)容的一部分。根源是因?yàn)槲募?nèi)容很大在底層會被拆成多個(gè)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è)計(jì)上被壓住了剩下交給readFully去兜底。5.2 long和int的選擇2GB限制第一次設(shè)計(jì)協(xié)議頭時(shí)我圖省事文件大小用的是int。測試到1.8GB一切正常但傳到2.3GB時(shí)客戶端報(bào)告發(fā)送了-1字節(jié)服務(wù)端那邊還沒收到數(shù)據(jù)就已經(jīng)報(bào)錯。后來查了一下問題就是int溢出2.3GB換算成字節(jié)已經(jīng)超過了int的最大值約21.47億算出來的字節(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記錄每次的分片大小兩者組合起來就沒問題了。這個(gè)教訓(xùn)也提醒我協(xié)議設(shè)計(jì)階段最好把字段寬度一次性想清楚。文件大小、偏移量這些天然有變大趨勢的字段寧可用8字節(jié)的long也不要為了省幾個(gè)字節(jié)埋雷。5.3 客戶端斷網(wǎng)之后服務(wù)端留下一堆半成品有一輪測試我模擬了客戶端中途斷開上傳到一半直接切斷了網(wǎng)絡(luò)。服務(wù)端那邊沒有及時(shí)感知到客戶端斷開read方法在等著數(shù)據(jù)直到TCP超時(shí)重傳徹底失敗后才拋出SocketException。這個(gè)過程中服務(wù)端已經(jīng)創(chuàng)建了目標(biāo)文件并寫入了一部分?jǐn)?shù)據(jù)異常發(fā)生后這部分就是殘留的半成品文件。處理方案分兩步第一步在handleClient方法里記錄本次任務(wù)的目標(biāo)文件路徑第二步在catch塊中捕獲所有IOException后把記錄的目標(biāo)文件刪除。刪除動作本身也可能失敗文件被占用或權(quán)限不足那就再想辦法做一次補(bǔ)償或者至少輸出一條足夠顯眼的日志提醒人工介入。另一個(gè)很實(shí)用的技巧是給服務(wù)端設(shè)置SO_TIMEOUT也就是讀超時(shí)。如果一定時(shí)間內(nèi)沒有字節(jié)到達(dá)就直接拋異常結(jié)束任務(wù)而不是無限等待。文件上傳的場景里客戶端長時(shí)間不發(fā)送任何數(shù)據(jù)基本可以判定連接異常等待沒有意義。5.4 Windows和Linux路徑分隔符差異這個(gè)坑很隱蔽客戶端在Windows下把文件路徑D:\logs\access.log直接拼到協(xié)議里服務(wù)端在Linux下按/分割路徑文件名變成了D:\logs\access.log整個(gè)字符串。Windows下執(zhí)行File寫入時(shí)反斜杠會被當(dāng)作轉(zhuǎn)義字符的一部分結(jié)果文件名異常打不開目標(biāo)文件。我給的方案是在sanitizeFileName里統(tǒng)一過濾所有\(zhòng)\和/提取最后一個(gè)分隔符之后的部分作為文件名。這樣客戶端傳D:\logs\access.log過來服務(wù)端寫出的文件名就是access.log。如果確實(shí)需要保持目錄結(jié)構(gòu)那就讓協(xié)議再增加一個(gè)字段顯式表示相對路徑而不是依賴文件名里的分隔符自動解析。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í)行這比直接拋拒絕異常更溫和至少不會讓客戶端瞬間收到失敗。但也要注意如果并發(fā)太高IO和磁盤會成為瓶頸線程再多也沒有收益反而增加上下文切換的成本。實(shí)測幾GB大數(shù)據(jù)并發(fā)時(shí)磁盤寫入速度通常是第一瓶頸并發(fā)數(shù)控制在磁盤能承受的范圍內(nèi)才是對的。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)已有文件大小然后從偏移位置開始讀取并發(fā)送文件剩余部分。服務(wù)端接收時(shí)改用RandomAccessFile打開文件后先seek(offset)再按循環(huán)寫入文件體。這里要注意校驗(yàn)策略也要跟著變?nèi)绻募粋髁艘话刖蛿嗔苏募﨧D5還沒算出來續(xù)傳后的MD5計(jì)算必須在文件補(bǔ)齊之后才能做。另一種做法是傳輸時(shí)對每個(gè)分片計(jì)算CRC32服務(wù)端每收到一片校驗(yàn)一片這樣不用等整個(gè)文件送完就能及時(shí)發(fā)現(xiàn)損壞。代價(jià)是協(xié)議頭里需要再增加分片序號和分片CRC字段復(fù)雜度上了一個(gè)臺階。如果只是作為學(xué)習(xí)案例先把offset機(jī)制實(shí)現(xiàn)出來就夠理解了。真正的生產(chǎn)級續(xù)傳還要解決服務(wù)端如何知道文件已有多少數(shù)據(jù)的問題常見做法是客戶端先發(fā)一個(gè)查詢請求或者協(xié)議頭里帶上服務(wù)端文件的舊大小服務(wù)端比較后決定是覆蓋、跳過還是續(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ā)送端的核心代碼可以簡化為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ò)展這個(gè)案例做完之后可以延伸的方向很多。最容易想到的是加密用TLS替換裸的Socket或者對文件內(nèi)容做AES加密后再傳輸解決明文數(shù)據(jù)在網(wǎng)絡(luò)上被截獲的問題。其次是壓縮在客戶端讀文件時(shí)用GZIPOutputStream包一層文件變小了傳輸自然變快代價(jià)是CPU占用增加。再往下就是秒傳客戶端先發(fā)送文件MD5服務(wù)端如果已經(jīng)有相同摘要的文件就直接返回成功不需要真的傳輸數(shù)據(jù)。還有一個(gè)值得嘗試的方向是傳輸限速和失敗自動重試。限速可以防止一個(gè)上傳任務(wù)占滿整個(gè)出口帶寬影響其他服務(wù)失敗重試則要和斷點(diǎn)續(xù)傳結(jié)合才能做到斷網(wǎng)恢復(fù)后自動接著傳。把這些機(jī)制一個(gè)個(gè)加進(jìn)去一個(gè)簡單的文件上傳Demo就會慢慢變成一個(gè)真正能扛生產(chǎn)環(huán)境的小型分發(fā)系統(tǒng)。這個(gè)案例本身不算復(fù)雜核心就是一段清晰的協(xié)議、一個(gè)按部就班的接收循環(huán)、加上對異常邊界的處理。但把它真正跑通、跑穩(wěn)你會發(fā)現(xiàn)對TCP通信的理解會扎實(shí)不少以后再碰到消息隊(duì)列、RPC框架里的傳輸設(shè)計(jì)和粘包處理背后都是同一套原理。如果實(shí)際操作中遇到了奇怪的問題優(yōu)先從消息邊界是否分明順序是否嚴(yán)格異常是否被吞掉這三個(gè)角度排查大概率能很快找到答案。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
啪啪自拍九九综合| 亚洲97在线| 精品人妻视频一区二区三区蜜桃视频| 国产精品爱欲| 色天堂在线观看| 午夜无遮挡男女啪啪视频| 久久大线蕉一区| 国产精品网址| 日本免费二区三区| 99999精品成人| 黄色操人| 激情丁香五月婷婷| 精品一区二区人妖| 乱伦系列一区二区| 色五月网址| 少妇贴图| 热天堂一区二区| 无码WWW免费视频网站| 久久久久久免费电影| 加勒比大香蕉视频在线| 亚洲国产精品无码AV在线| ji熟女.com| 国产肏逼网站| 美欧色综合| 粉嫩少妇自慰在线| 日韩精品一区二区三区四虎影视| 少妇贴图| 欧洲精品一级二级精品综合视频综合| 久久伊人影院| 亚洲欧美另类激情小说| 久久这里只精品| 97视频在线免费播放| 久久香蕉国产传媒一区剧情天美| 日韩三四五区| 日本一区二区不卡精品| 91精品无码久久久久久久 | 欧美日韩99精品麻豆传媒| 国产欧美日韩女同性恋ww喷水精品| 手机看片日韩人妻| 69精品人人人人| 狠狠久久手机视频精品| 六月丁香网| 精品久久久久久中文字幕视频免费 | 欧亚性爱视频免费看| 久久久久久久伊人精品| 国产精品亚洲天堂网址| 97视频免费播放| 夜夜夜夜久久久久| 久久精品一区二区三区蜜桃臀| 亚洲精品男人的天堂| 操99| 熟妇女人妻呻吟久久AV| 欧美精品日韩久久久九| 午夜在线播放| 亚洲欧美色图小说| 美欧色综合| 亚洲 欧美 天天| 爽爽淫人网| 黑丝91视频| 国产美女精品| 女优大全 - 91n| 久草午夜| 密臀成人视频久久久| 日韩国产十八禁| 天天天肏屄欧美| 操逼999| 黄在线| 久久久久久91香蕉国产| 日本 情色 1区2区3区| 超碰视97中文| 亚洲丨在线| 91东北熟女| 中文字幕欧美丝袜07资源| 无遮挡一级毛片视频免费的| 国产无套粉嫩白浆在| 欧美综合区| 床上啊啊啊一区二区三区| wwe 天天干.com| 97网址www| 精品久久視頻在线| 91中出在线| 国产精品视频一区二区三区八戒| 成人三级片无码| 日韩国产品视频中文字| 免费男人的天堂| 热思思免费视频| 欧美性爱免费短视频| 丁香五月婷婷色| 久久国产对白激情浪潮 | 精品久久久久久中文字幕视频免费| av在线观看不卡网站| 久久露脸国产老熟女| 五月综合久久| 日韩熟女精品无码专区一区二区| 东京热99999| 国产免费一区| 奇米狠999| 刺激性视频黄页| 欧美热图99| 亚川综合视频| 婷婷另类小说| 国产午夜视频| jizz啪啪| 欧美白嫩女HD| 日韩黄色片子| 精品一久久久| 国产一在线观看| 蜜桃在线观看一区二区三区| 欧美在线|亚洲| 国产亚洲精品精AV.| 久久xx| 蜜臀久久99精品久久久久电影| 久草网站免费在线观看| 欧洲精品欧洲精品| 日韩欧美综合激情| 丝袜翘臀后入欧美校园亚洲自拍另类小说一区中文字幕少妇诱惑 | 青娱乐休闲视频在线观看| 中文字幕 人妻不满 在线视频| 久久有码视频| 久操视频资源站公开| 四虎免费看黄| 日本色色色视频| 91欧美综合在线| 国产精品久久| 四虎影视在线| 中英熟女操女| 亚洲精品男人的天堂| 91国产丝袜白虎| 天天爽天天操| 国产农村妇女精品| 少妇500双飞99| 老司机午夜精品视频| 亚洲天堂另类小说男人| 久久这里只精品99re66图| 国产婷婷一区| 欧美一级美片在线观看免费| h色99999| 亚洲一区二区精品福利| 久9爱精品| 九色婷婷| 91久久久亚洲| 色网在线| 日本在线一二 | 青青操97| 被男人添B超爽视频| 亚洲成人精品在线一区| 婷婷五月天在线观看| 久久系列| 亚洲国产一区二区三区在线| 囯产精品久久久久久久久久梁医生| 国产青视频| 欧美日韩 强奸乱伦| 男生通女生屁股| 午夜精品一区二区三区三上悠亚| 免费家庭乱伦视频| 免费成人自拍视频在线| 成人在线视频网| 五月天婷婷影院| 久久久性爱| 岛国成人av在线播放网址| 9久精品| 啊啊啊啊啊啊好湿好爽视频| 一本一道人妻久久一区二区三区 | 天天干18禁| 麻豆伊人网| 久久综合九九| 国产精品爽爽v| 精品人妻一区二区三区四区不卡在| 人人综合| 日韩在线视频1234| 亚州操逼网| 九九九九一区| 日本黄色裸日本黄色裸体| 操逼网免费无码视频| 啪啪啪综合网| 污污污8888| 亚洲欧美经典一区二区| 蜜乳AV一区| www.亚洲黄色| 91精品网站| 天天拍天| 劲爆欧美人妖三区91| 亚洲 中文 女同| www.99在线| 日韩精品 视频一区二区| 97啪啪| av天堂加勒比| 黑人干亚洲| 日韩不卡网操逼中文字幕日韩| 婷婷亚洲天堂| 日本一区二区三区免费观看| 亚洲图片欧美色图| 国产精品午夜AV完会免费 | 久热无码| 丁香五月婷婷五月| 又黄又爽在线观看视频| 熟妇熟女亚洲天堂网| 国产伦精品一区二区三区在线观| 秋霞无码av鲁丝片一区| 色青青久久影视| 国精精品无码一二三区水多多| 久草电影网| 中文字幕精品亚洲熟女| 日韩中文字幕av在线播放| 免费观看欧美日韩操逼视频| 婷婷伊人网| 国产精品老师| 一本一道人妻久久一区二区三区 | 91久久18禁| 久久久久久夜夜夜夜夜| 麻豆九九九| 久久精品亚洲东京热色播| AV天堂丝袜| 996热| 亚洲最新a在线观看| 熟女一区二区| 97视频观看| 欧美激情片一区二区| 一区,二区,三区视频| 91在线页| 黑人粗大V S日韩女优视频| 98超碰日本| 安微少妇操BBB| 日本二三四区| 亚洲天堂五月天国产| 中文字幕一二区二三区人妻专区| 久久精品中文字幕无码l| 夜间福利片1000无码| 五月天黄色激情视频| 欧美99热| 性爱AV天堂| 影音资源男人日韩| 日本视频在线中文字幕| 精品一区二区在线针对华人免费观看这里只有精品免费观看 | 一级性爱视频免费在线| 日本操逼视频导航| 69精品少妇一区二区三区蜜桃| 在线女人91| 天天天天做夜夜夜夜做| 欧美,日韩,中文,另类| 亚洲在线| 国产亚洲综合欧美一区| 日韩伦理视频| 欧美肥臀在线| 人人操我人人干| 亚洲无线码一区国产欧美国| 99自拍B亚洲| 久操视频免费观看| 亚洲天堂电影精品一区| 美女啪欧美一区| 欧美中日韩XXXX| 日韩在线一区高清在线| 五月婷婷丁香| 伊色久人大在线| 尤物视频新赏网鲜网色诱网| 国产91亚洲精品一区二区三区| 免费一级a毛片久久久久久鸭绿欲 国产精品亚洲天堂网址 | 97久精品| 992视频一区| 久久黄色性爱视频| 天天操女人| 白嫩91在线亚洲| 亚洲 欧美 日韩 国产一区二区| 人人操人人操草草| 久久一二三四不卡| A级在线视频| 丁香六月婷婷久久综合| 97欧美色综合| 99re热有精品视频国产| 任我爽视频在线观看| 精品久操| 免费簧片在线观看| 九热超碰| 把腿张开老子CAO烂你| www鬼畜国产男人的天堂| 日本国产二线女色| 人妻精品一区二区在线| 曰韩少妇无码| 97美日韩视频| 美女熟妇色| 中文字幕二区日韩天堂| 日韩精品色呦呦| 国内精品久9| 91精品人妻一区二区-全集完整版免费正片国语-B02AV | 久久一二三四五六七八九区区区| 欧美成不卡网| 亚洲熟伦熟妇AV无码春色| 亚洲精品丝袜-不卡成人免费……| 亚洲视频小说| 五月天激情小说网| 亚洲高潮少妇| 探花精品视频| 亚洲视频,小说| 东京热视频网| 大稥蕉免费视频这里只有精品| 久热99999| 人人妻人射| 五十路熟女工口 | 操老熟女AV| 亚洲AV噜噜狠狠网址蜜桃动漫| 亚洲色图20p| 国产精品懂色tv影视免费观看| 一道本久久棕合爱| 中文字幕一区二区视频在线观看| 丝袜狠狠草尤物人妻av91| 亚洲人妻久久| 丝袜狠狠草尤物人妻av91| 精品久久久av无码免费| 欧亚不卡| 久久久精品视频免费观看| 精品一区99999| 日韩免费看黄片| 清纯唯美第一页| 能看的av| 丰满岳乱妇一区二区三区| 加勒比av中文| 天天插天天操| 日韩综合无码一区久久92| 三级网站超变态精品| 少妇人妻好深太紧了vr91| 色香综合天天影视综合 | 极品色社| 亚洲国产婷婷在线播放| 亚洲少妇视频| 97色爱| 熟女精品va中文字幕| 欧美少妇高潮| 精品久久久久黄少妇| 欧美色视频在线| 蜜臀99久久精品| 91精品电影18| 人人爱人人操人人性| 99在线精品视频| 亚洲男人的天堂AV| 国产欧美日韩在线不卡第一页| 激情一区二区| 国产情色第一第二页在线观看| 九九黄色网| 人妻丰满熟妇一区二区三| 天干天干天干天天做| 日本999精品视频| 人妻嗯啊啊在线播放| 韩日性爱av| 国产三级片在线观看| 岛国999| 久久婷婷在线观看视频| 亚洲成人免费在线| 岛国色情视频在线观看| 日本孕妇一区二区视频操逼免费看| 青青青艹在线视频| 啊嗯好大视频在线观看| 丁香六月激情综合| aⅴ日韩成人电影av在线免费看av大全| 91亚洲人| 黄页av| 国产91丝袜 在线播放| 小草av不卡亚洲二区| 久都青青视频 | 91欧美经典| 久艹99| 久久久久网站-538在线视频-欧美永久乱码 | 日日狠狠久久偷偷色综合免费| 久久国产乱子伦精品免费女人| 精品九九| 人人妻人人玩人人澡人人爽| 色路综合| 果冻传媒A片麻豆熟妇人妻| 色天堂在线观看| 天堂国产AV| 97操97色| 97综合在线观看| 91色艳| 91狠| 久妇网| 一色网男人的天堂| 天天添天天干电影| 天天91~综合入口| AV九九| 五月天激情网站| 九九99精品| 超碰人妻在线| 日韩高潮一区| 中文字幕日产av人| 一区=区三区视频| 精品无吗久久| 亚洲色图国产另类| 操死我了啊啊啊| 亚洲综合伊人| 久久9免费视频| 少妇高潮喷水无套久久久久久| 东京热熟女亚洲视频网站| 1024手机看片欧美日韩| 久久成人网站| 欧美亚洲今日在线| 国产美女在线精品免费看| 国产精品亚洲色婷婷久久久| 黄片免费看的| 色色五月天激情| 亚洲成人av色网| 按摩中文字幕| 久久99999| 综合网亚洲1| 97亚洲中文| 国产91精品在线免费| 亚洲欧美国产中文视频| 999亚洲国产视频| 精品性爱一区二区| 日韩人体偷拍| 97一区二压| 人妻色情天天操| 日本久久超碰| 性爱综合一区二区| 综合欧美日本三级| 欧美人妻二区三区| 亚洲色图第四色| 精…码一二三区| 九九精品无码专区免费| 澳门特级毛片免费观看| 后入人妻一区| 香蕉国产97| 国产女大学生AV| 口爆吞精在线观看| 中文字幕一区二区三区蜜臀| 亚洲国产蜜臀系列在线观看| 久久久久九九九九| 操人91| 级品肉射| 熟妇乱伦一区二区| 91九九| 99黄页网站| 日日夜夜免费| 最新中文字幕在线亚洲| 日韩 人妻 精品| 一本大道久| 乱伦一区二区三区‘| 色色无码| 老熟女综合网| 久久男女激情视频网站| 亚洲少妇色图自慰直播| 欧美影院一区二区三区| 欧美在线视频99| 99热日| 女欧美一区二三区| 97网址www| 9久9久9久9久视频网站| AV不卡在线| 久久超碰亚洲人| 久久久久久久9| 久久性爱城| 91成人久久| 国产日韩欧美操逼视频| 蜜桃久久久久久久| 亚洲美女av无码| 92福利社视频| 久久久久久久9999| 九九综合色| 热热色AV| 亚洲欧美日韩精品久| 美女毛片999| 精品人妻一区二区三区四区石在线| 中文字幕五区| 农村女一级毛卡片| 台湾佬激情综合| 另类欧美色| 激情婷婷丁香| 日韩九九九| AV综合中文字幕干| 夜夜欧美 | 白丝少妇一区二区| 飘花国产午夜精品不卡| 成人五月天色网| 欧美性爱无码一区二区三区| 欧美少妇色综合| 亚洲 日本 国产 综合| 国产乱弄免费在线视频。| 一级做a爰片性色毛片久久| 亚熟在线| 黄色电影观看久久9| 日韩欧亚中文在线| 99性视频| 91熟女视频| 大香蕉伊人在线成人AV在线观看| 国产亚洲精品激情| 久久视频,这里只有精品| 欧美久久伊人| 射久久| 亚洲 se图 欧美电影| 天天色天天干天天爱| 色偷综合| 人妻少妇精品一区二区三区| 26uuu久久| 综合色拍| 欧美图片偷拍| 丰满美女一级毛片在线播放| 色悠久久久av| AV九九| 毛片一区二区| 97视频www| 操逼逼无码| 九九九九九九九九九国产精品 | 色欲久久99精品久久| 91色久| 超碰97人妻在线| 久久久久久性爱视频| 欧美精品日韩久久久九| 久草精品在线| 国产女同性恋视频| 国产精选三级在线观看| 91少妇| 福利天堂| 五月婷婷六月激情| 国产成人99久久亚洲综合| 欧美日韩精品青青| 亚欧美天堂在线| 欧美人妻精品一区二区| 欧美天天射| 亚洲天天精品| 色爱欲亚洲| 九九热在线精品视频| 免费一级性爱久久| 亚欧无码线免费观看视频| 色色青青久久| 久热久| 一区二区三区网站日日骚| 国产亚洲国产超碰| 欧美一区二区三区另类精品| 精品高潮| 草草草草视频| 日韩天天综合| 久久久性少妇| 日韩资源网| 国产乱婷婷精品二区三区| 9色在线| 最新中文字幕精品在线| 亚洲国产尤物yw在线观看| 久久精品一区二区一8| 蜜臀久久99精品久久久久电影| 男人精品天堂一区| 欧美成人综合| 麻豆一区二区三区精品| 国产传媒日本欧美专区| 超碰久久性爱| 亚洲天堂区| 丁香婷婷九月| 久9热| 国产AV久久久蜜爱影集| 人妻熟女av国产网站| 免费伦费视频在线观看| 亚洲天堂AV在线播放| 日本性交操一区二区不卡系列| 九九草| 91亚洲欧美激情| 97精品全部| 久热精品在线| 97超碰香蕉| 国产精品乱码久久久久| 蜜臀久久99精品久久久久久无删减 | 人人污日韩一区二区| 亚洲精品一二三四区| 久九九九九九九热| 欧美午夜色妇色鬼| 丝袜翘臀后入欧美校园亚洲自拍另类小说一区中文字幕少妇诱惑 | www.色婷婷.com| 可以看的av| 七久久久| 欧美亚男人的天堂| 韩日巨乳美女免费视频在线观看| 亚洲日韩乱码中文无码蜜桃臀网站| 97AV在线免费观看| 日韩欧美中文字亚洲慕| 欧美姓爱综合网| 色五月AV| 午夜视频久久久久一区| 亚洲五码一区二区三区| 午夜大香蕉| 性欧美另类高清| AV天堂男人的天堂| 视频在线中文字幕| 国产一区在线看| A级片日韩欧美国产欧美视频精选观看| 免费家庭乱伦视频| 无码在线亚洲| 国产一区96在线| 狠狠图片青青草| 四虎影视永久在线观看精品免费网站| 中国韩国明星一极片一区乱码毛片人妻熟女一区二区三区 | 国内外激情在线| 久操视频在线观看| 伊人五月天激情| 久久久久久人妻| 9 7超碰在线免费观看| 香蕉大久久久| 大香蕉一级黄色片久久| 99re公开精品免费视频| 9 9精品一区二区三区| 国产精品91ai| 亚洲欧洲日韩中文字幕一区| 日本人妻伦在线中文字幕| 亚洲码专区| 日韩情色视频| 亚洲久草AV色图| 草草电影院| 日本在线视频导航| 久插综合| 国产久久久久久| AV网站高清无码在线观看| 熟女人妻久久中文字幕一二区| 熟女一区二区三区| 97在线免费观看视频| 精品久久人妻成人网| 黑人天8A∨高清网站| 美女黄色91| 日韩欧美aⅴ综合网站发布| 欧美日韩超碰在线| 日韩欧美三级| 日韩成人精品视频自拍| 少妇六月天| 97视频免费播放| 欧美性爱伊人| 久久熟女久| 亚洲黄片免费在线播放| 天天日B狠狠操| 国产9熟妇视频网站| 人人操人人搞人人草| 亚洲国产剧情少妇激情| 男人的天堂免费| 国模精品一区二区三区苹果色戒 | 天堂俺去俺来也www久久婷婷| 婷婷成人久久久精品| 人妻熟女一区二区三区视频| 天天精品| 啊啊啊轻点在线观看| 丰满岳乱妇一区二区三区| 亚洲第一页欧美| 亚洲网站一区二区在线| 久久9精品视频| 熟女人妻精品一区二区视频| 欧美色图天堂在线| 少妇高潮九九九九| 天天影视色香色欲| 激情小说亚洲| 超碰在线974| 国产91福利小视频在线观看| 蜜桃传媒一区二区亚洲| 欧美影院一区二区三区| 91暧暧| 欧美大香蕉久| 干B| 色乱二区| 欧美热图99| 日韩啪啪啪啪啪| 啊啊啊想要| 欧美一区二区情色| 国产强奸乱伦xd| 国产久久日| 日韩一级成人毛片免费观看 | 午夜电影在线观看无码专区| 国产成人啪一区二区| 91在线无码精品秘 软件| 欧美日韩亚洲少妇寂寞影院正在播放 | 91成人高清在线观看| 台湾大香蕉99热| 女人被男人桶爽视频网站| 欧美韩日精品资源| 日韩无码专区| 午夜精品久久一区二区| 青青草啪啪网| 国产三级电影免费观看| 亚洲se91| 青青操少妇| 亚洲成熟国产精品美女| 高清视频一区| 日本97久久久精品| 黄色一级视| 亚洲毛片基地专区| 伦理弟一页| www.色99| 欧美性爱日韩高清| 精品高清牛人盗摄一区二区三区中文字幕A片免费在线观看 | 天天舔天天日天天射| 国模精品一区二区三区苹果色戒| 97在线视频观看| 国产乱码精品久久久久久| 国产精品秘 福利姬在线观看| 99re免费| 亚洲日韩黑丝| 亚洲人妻熟妇三十三区| 久久久夜夜嗨免费视频| 激情五月综合开心五月| 5252色欧美在线男人的天堂| 大香蕉欧美伊| 五月婷婷六月丁香| 中文字幕精品日韩中文字幕| 日韩啪啪视频| 日韩精彩视频| 亚洲综合码| 久久婷婷精品| 狠狠操夜夜| 久久久少妇| 久艹免费| 啊啊啊啊啊啊啊在线| 久妇网| 久操热| 国产一区二区三区导航| 看免费的黄片| 久久专区| 中文字幕乱码在线观看| 少妇高潮喷水无套久久久久久| 欧美日韩资源在线| 农村妇女一级二级三级视频| a天堂视频| 日韩熟女操逼| 啊啊啊在线观看| 亚洲区 欧美区| 欧洲一区二区三区四区在线观看| 韩国女主播青草在线| 久久久久久久一级黄色打同平台| 美女被艹尤物视频| 一本一道波多野毛片中文在线| 国产毛片毛片4p懂色| 九九九九九九九九九国产精品 | 操熟女91| 超碰色美女| 亚洲精品国产熟女久久久| 亚洲淫色网中文| 色眯眯射| 青青草伊人久久| 在线女人91| 欧美色偷拍 | 強姦亂倫a| 韩国免费播放一级毛片| 日本视频在线中文字幕| 日韩一级二级三级在线不卡观看完整| 亚洲国产97在线精品一区| 日欧美色| 国产熟女乱论| 亚洲日韩人妻中文字幕一区| 91美女在线视频| 搡老熟女老女人老熟妇免费视频| 日韩成人高清一区二区| 大学生美女口爆| 超碰97人妻在线| 欧美天天射| 啊啊啊想要| 秋霞Av理论一级在线| 性猛交| 精品久久久久久中文字幕视频免费| 啊啊啊爽爽| 97香蕉网| 综合国产影视三级| 思思热久久成人| 天天日B夜夜干B时时操B| 在线观看综合精品亚洲| 色色五月婷婷| 99热色这里只有精品| 亚洲aw毛茸茸在线 | 色综合 加勒比| 青青操日韩| 91欧美综合| 国产黄色剧情影片麻豆免费播放| 乱性AV| 青青久久手机线视频| 5月婷婷6月六月丁香| 九九热精品视频在线观看| 日本韩国一本产品小视频日本韩国一本产品久久久产品小视频日本韩国一本产品久 | 欧美亚洲特P| 九九九热| 无码日韩人妻av一| 日本天天色| 亚洲图片欧美制度| 秋霞福利网| 日韩性色| 欧美一区二区三区四区综合| 亚洲码专区| 久久久久骚| 欧美熟女丝袜| 四虎精品一区二区| 日韩免费在线视频观看| 五月丁香六月婷综合成人综合| 亚洲欧美激情另类色图| 日韩天天本| 亚洲美女精品九九视频| 日韩99精品视频综合区| 91九色精品熟女内射| 久草资源在线视频官方总站日韩丝袜美腿 | 99re在线观看| 亚州欧美在线| 欧美国产欧美在线观看| 精品超碰中文在线| 国产免费一区| 1769一区| 天天干天天拍| 劲爆欧美人妖三区91| 日本欧美中文字幕| 无码自拍SM| 97 九色| 超碰97精品| 亚洲欧美激情在线视频| 99蜜桃臀亚洲成人在线观看| 91性感在线| 色噜噜精品一区二区三| 99爱久久视频频| 大香蕉草草| 婷婷激情五月天小说网| 超碰 av 女人天堂| 久久中出| 五月丁香| 久久超碰天天| 日韩色欲久久一二三四区| 久久大香蕉手机高清| 精品玖九九久| 国产福利精品最新在线| 和协影院中文字幕三区| 亚洲精品欧洲精品| 又黄又硬又粗又长国产视频| 911av网站免费观看| 韩国女主播青草福利视频| 日韩AV噜噜噜一区二区三区四区| 中文字幕中文字幕一区二区| 中美日韩毛片| 老司机福利青青草| 色婷婷五月天| 97国产|免费| 婷婷亚洲五月***久久| 人人操人人射人人干| 蜜乳AV.COM| 精品国产72| 天天激情综合站| 麻豆婷婷成人一二三| 91欧洲国产成人久久精品网站| 久超碰在| 天天肏天天干| 夜夜嗨一区二区| 欧美+日产+中文| 男人天堂久久精品| 精品91日日夜夜超清资源| 色五月综合| 天天做天天爱| 性感女人网页在线观看视频| 懂色Av| 99啪啪视频| 国产精品4p在线观看| 91av天美性媒精品视频| 久久久久久久久久va| 密臀AV在线| 大黄片做爱的大的| 亚洲AV在线资源| 免费人成?大片在线播放| 久久9免费视频| 一级黄色影片| av激情亚洲五月天| 天色综合网| 中文字幕五月婷婷免费| 久久久99999久网站| 最新中文字幕精品在线| 99最新日韩偷拍视频| 超碰在线成人| 日本综合色图| 亚洲欧美另类少妇精品| 亚洲无吗在线视频| 欧美综合色| 你操综合| 在线无码视频| 亚洲九九视频| 台湾佬激情综合| 日韩性爱免费观看视频| 啊啊啊啊好疼视频| 亚州国产精品乱| 美日韩男女操屄视频| 久久高清无码夜夜操| 久久婷婷五月天| 99精品国产户外露出| 久久久久密臀视频| 国产无马av| 亚洲国产一级黄色视频| 大屁股熟女一区二区三区| 另类图片五月天| 亚洲AV成人无码一二三久久| 超碰在线综合97| 欧美日韩情色一区二区| 乱伦1色页| 亚洲国产97| 色麻豆AV| 欧亚洲精品有视频| 在线强奷到舒服的无码视频| 黑人综合网| 色拍偷亚洲| www.97在线| 人成午夜免费大片| 人人操人人插 - 百度 - 百度| 超碰社区97| 中国黑人三级片网站上区| 狠狠干婷婷| 日韩人妻一二三区视频| 丰满熟女一区二区三区在线播放| 色网1| 在线天堂999| 99人人干| AA特级绝黄| 日本东京热久久久电影| 日日骚一区二区三区| 欧美夜色| 打av高清| 亚州色交| 五月天丁香网| 亚洲无吗在线视频| 成年人网站在线免费观看| 我要色综合网站| 国产视频大全| 69综合网| 69AV女优男人的天堂| 91综合在线| 国产精品香蕉热久久新品| 日韩成人在线性爱视频| 九九九九热| 奶水 人妻 哺乳 在线| 啊啊啊久久久视频| 2000亚洲男人天堂| 青草香蕉网| 红桃视频高潮| 美女啊啊啊啊啊啊啊| 丁香五月天视频| 天天天天天干夜夜夜夜夜操| 国产三级中文有码在线视频| 中文字幕五月婷婷免费| 亚洲精品免费中文字幕| 超碰在线人人射| 老熟女91视频| 国产三级片在线观看| 国语精品av| 久热伊人| 亚洲AV色图| 骚货操死你| 97操B| 国语精品对白| 天天干18禁| 男人的天堂久久久| 91欧美综合| 97超碰人妻| 亚洲综合影院| 中文字幕在线免费观看2| 91久久久亚洲| 免费看黄片现成| 国产亚洲欧美每日在线| 成人无码在线视频网站| 久久性生大片免费观看性| 97爱爱| 日韩成人性日韩成人性爱视频在线免费观看| 97色综合中文网| 精品在线蜜臀| 小说区 图片区色 综合区| yaouchengrenav| 热久久国产| 人人操人人uiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii | 红杏大香蕉| 超碰国产情侣自拍网| 鸥美插入视频| 日韩欧美天堂| 最新AV在线| 日本一级真人黄色性爱视频| 国产精品人妻一区二区| 欧美一级特黄淫片在线观看| 国产高清成人传媒影视| 九九内射在线| 天天碰操中国年青熟妇| 国产后入清纯| 欧美天堂在线| 精品人妻一区二区三区四区石在线| 国产三级在线现体验区| 国产亚洲日本精品在线| 丁香五月天社区| 91女网站| 天天操天天看| 欧美人妻少妇| 91艹B视频| 欧美性色欧美| 日本三级久| 久久久穴999| 9ⅰ久久久天天| 蜜臀一区二区三区亚洲最新章节在线观看 - 高清蜜臀一区二区三区亚洲全集播放 | 夜夜操老骚逼视频网站| 日韩精品三级片长长久久| 国内精品99999| 青青青国产手线观看视频2| 思思性爱| 亚洲中文人妻色| 97综合久第一页| 亚洲精品一二牛牛| 九九无码视频| 国产大片精久久久久久| 人妻另类| 操操吧亚洲乱伦视频| 99热精品在线| 久草精品视频| 天天插网| 国产精品成人无码av| 懂色AV中文| 综合亚洲欧美| 日韩一性一交一A片俄罗斯| 美女露胸露尿口| 久草毛片| 国产一在线观看| 国产精品久久久亚洲一区| 97视频新免费| 99亚洲精品| 一二三四免费视频| 91国产丝袜美女| 中文字幕黄片在线| 校园春色 男人天堂 | 18禁免费视频| 69AV女优男人的天堂| 午夜亚洲国产理论秋霞| 亚洲天堂区| 亚洲图片 欧美电影| 在线看片国产精品每日更新| 99在线观看无大码| 蜜臀久久99精品久久久| 99超级碰免费视频| 四虎免费视频| 亚洲成a人片在线观看中文!!!| 99爱久久视频频| 99国产精品| 日韩久久三区| 日韩精品亚洲一二三| 久久99人妖视频国产| 欧美超碰人妻97| 国产精品亚洲天堂网址| 又摸又舔在线观看网站| 中国熟女老妇仑乱一区二区三区| 欧美亚洲综合色| 久久精品无码专区| 日本三级日本三级99| 怡红院亚洲怡春院av| 亚洲一区二区精品福利| 熟妇激情| 粉嫩绯色AV一区二区在线| 好爽要喷了| 日本精品成人无码| 91精品久久久久久| 大香蕉520| 俞拍久久国应视频| 亚洲精品 大香蕉| 亚洲国产熟妇综合色专区| 99少妇精品视频| 日韩精品人妻| 人妻在线臀日韩| 91色人妻| 1024手机看片欧美日韩| 日韩欧美午夜视频在线| 啊啊啊啊,啊啊好多水 | 91亚洲综合在线| 久久黄黄| av网站在线看| 青青草在线视频欧美| 26uuu性| 人妻天天爽夜夜爽2| 色人久久| 日韩亚洲欧美中文字幕| 亚洲棕合电彰| 久草网站免费在线观看| avav青青草久久夜| 九九久久九九久久| 不卡中文字幕aⅴ在线| 91性| www.久久| 国产精品一区二区手机看片| 日欧毛片久久| 日本在线不卡一二区| 欧美一二三| 日本五十路在线| 激情久久久| 欧美高清色| 亚爽爽爽爽爽爽爽爽| 思思热免费视频观看| 亚洲欧美小说| 老司机免费视频在线91| 99ri精品| 妇女性内射冈站HDWWWCOM| 久久大精品乱码视频人妻熟女| 亚洲偷拍自拍在线视频| 无码人妻1727| 97精品综合久久网| 婷婷激情五月| AV一区观看| 国产久久天堂资源| 东北女人的毛片| 六月激情婷婷| 国产精品久久久无码aV去| 97精品网站| 香蕉99秘 精品一区丁香| 九九久久一区二区伦理| 美女久久久久久久久久久| av麻豆啪啪| 日韩啪啪啪啪啪| 日韩人妻播放| 97色碰| 丝袜美腿91| 夜草欧美| 欧美黑人性猛交91| 国产精品岛国片在线观看| 日韩免费中文字幕视频| 91中出在线| 人妻少妇色综合| 热久久91婷婷| 亚洲欧美经典一区二区| 九九精品99| 国产强奸乱伦第1页| 亚洲成人在线资源| 亚熟hd视频在线| 99啪| 天天干天天拍| 96久久精品一二三区色欲| 国产精品一区二区亚洲人成毛片| 97chaopengongkai| 超碰综合97在线| 天天天天天天天天综合| 韩国女主播青草福利视频| 婷婷五月天影院| 西西美女视频网| 91neishe| 九九热最新| 一区二区三区激情在线观看| 一本精品日本在线视频精品| 黑人娇小av在线播放| 97综合激情| 久草综合网| 综合网亚洲1| 伊人aaa| 亚洲欧洲久久天堂| 久插综合| 国产一区麻豆免费观看| 荡小穴在线观看| 日本有码久久| 98一区二区精品| 天天干天天干天天| 天天色播| 婷婷久久五月| 超碰人人干| 日本色婷婷| 色九九综合| 天天插天天插| 亚洲 日本 一 二 三| 五月天激情国产综合婷婷婷| 色婷婷99| 亚洲色天堂日韩中| 91美女在线精品视频| 啊啊啊啊啊啊啊国| 三级片大波波| 日本精品国产视频| 成人热久久精品| 试看福利| 亚洲诱惑天堂| 欧美精品不卡一二三四在线91| 色综合1991| 色色97爱| 亚洲日韩电影| 三级日本一区二区三区| 国产精品亚洲无码| 中国女人内射6XXXXX| 精品一区二区成人动漫|