摘要:協(xié)議族協(xié)議協(xié)議協(xié)議應(yīng)用程序通過套接字對協(xié)議和協(xié)議所提供的服務(wù)進行訪問。網(wǎng)絡(luò)層完成將分組報文傳輸?shù)剿哪康牡?,即路由功能,一般采用協(xié)議。程序間達(dá)成的這種包含了信息交換的形式和意義的共識稱為協(xié)議。
TCP/IP協(xié)議族:IP協(xié)議、TCP協(xié)議、UDP協(xié)議
應(yīng)用程序通過套接字API對UPD協(xié)議和TCP協(xié)議所提供的服務(wù)進行訪問。
底層由物理層:基礎(chǔ)的通信信道構(gòu)成,如以太網(wǎng)/WIFI或調(diào)制解調(diào)器撥號連接。
網(wǎng)絡(luò)層:完成將分組報文(packet)傳輸?shù)剿哪康牡?,即路由功能,一般采用IP協(xié)議。IP協(xié)議提供了一種數(shù)據(jù)服務(wù):每組分組報文都有網(wǎng)絡(luò)獨立處理和分發(fā),就像信件或包裹通過郵政系統(tǒng)發(fā)送一樣。每個IP報文必須包含一個保存其目的地址的字段。
傳輸層:提供了TCP協(xié)議和UDP協(xié)議,這兩種協(xié)議都建立在IP層提供的服務(wù)之上,IP協(xié)議只是將分組報文分發(fā)到不同的主機,然后還需要更細(xì)粒度的尋址將報文發(fā)送到主機指定的應(yīng)用程序端口,這個尋址功能是TCP或UDP要完成的,因此他們也成為端到端的傳輸協(xié)議。IP協(xié)議只是將數(shù)據(jù)從一個主機傳到另一個主機。
TCP協(xié)議提供一個可信賴的字節(jié)流信道(處理報文丟失、重傳及順序混亂問題),一種面向連接的協(xié)議:在使用它進行通信之前,兩個應(yīng)用程序之間首先要建立一個TCP連接,這涉及到兩臺機器的TCP部件完成握手消息的交互。
UDP協(xié)議不嘗試對IP層產(chǎn)生的錯誤進行修復(fù),僅僅是簡單地擴展IP協(xié)議,傳輸?shù)蕉丝凇?/p>
網(wǎng)絡(luò)層中的IP協(xié)議就像把郵件送到某個街道的某個樓的信箱,而傳輸層則是將信件送到該樓層的具體某個房間里頭。
IP協(xié)議其實是單播協(xié)議,還有多播協(xié)議,廣播到任意數(shù)量的地址。
什么是套接字(Socket)一種抽象層,應(yīng)用程序通過它來發(fā)送和接受數(shù)據(jù),就像應(yīng)用程序打開一個文件句柄,將數(shù)據(jù)讀寫到穩(wěn)定的存儲器上一樣。使用socket可以將應(yīng)用程序添加到網(wǎng)絡(luò)中,并與處于同一個網(wǎng)絡(luò)中的其他應(yīng)用程序進行通信。一臺計算機上的應(yīng)用程序向socket寫入的信息能夠被另一臺計算機上的另一個應(yīng)用程序讀取。
不同類型的socket與不同類型的底層協(xié)議族以及同一個協(xié)議族的不同協(xié)議棧相關(guān)聯(lián)。
TCP/IP協(xié)議族中的主要socket類型為流套接字(stream scoket)和數(shù)據(jù)報套接字(datagram socket)。
流套接字將TCP作為其端對端協(xié)議,提供了一個可信賴的字節(jié)流服務(wù)。
數(shù)據(jù)報套接字使用UDP協(xié)議,提供了一個best-effort的數(shù)據(jù)報服務(wù)。
一個套接字抽象層可以被多個應(yīng)用程序引用,每個使用了特定套接字的程序都可以通過那個套接字進行通信。每個端口都標(biāo)識了一臺主機上的一個應(yīng)用程序。實際上,一個端口確定了一臺主機上的一個套接字。
任何要交換信息的程序之間在信息的編碼方式上必須達(dá)成共識(比如將信息表示為位序列),以及哪個程序發(fā)送消息,什么時候和怎么接受信息都將影響程序的行為。程序間達(dá)成的這種包含了信息交換的形式和意義的共識稱為協(xié)議。用來實現(xiàn)特定應(yīng)用程序的協(xié)議稱為應(yīng)用程序協(xié)議。
TCP/IP協(xié)議的唯一約束是,信息必須在塊(chunk)中發(fā)送和接收,而塊的長度必須是8位的倍數(shù),因此我們可以認(rèn)為TCP/IP協(xié)議中傳輸?shù)男畔⑹亲止?jié)序列。
如果是自己設(shè)計和編寫套接字的客戶端和服務(wù)器端,則可以隨心所欲地定義自己的應(yīng)用程序協(xié)議。
對于需要超過一個字節(jié)來表示的數(shù)據(jù)類型,我們必須知道這些字節(jié)的發(fā)送順序。
一種是從整數(shù)的右邊開始,由低位到高位發(fā)送,即little-endian順序;一種是從左邊開始,由高位到低位發(fā)送,即big-endian順序。
對于任何多字節(jié)的整數(shù),發(fā)送者和接收者必須在使用big-endian順序還是使用little-endian順序上達(dá)成共識。
另外一個需要達(dá)成的共識是:所傳輸?shù)臄?shù)值是有符號的還是無符號的。
對于給定的k位,我們可以通過二進制補碼來表示-2的k-1次方到2的k-1次方-1范圍的值,如果使用無符號,則可以表示0到2的k次方-1之間的數(shù)值。
DataOutputStream允許你將基本數(shù)據(jù)類型按big-endian順序進行編碼,即將整數(shù)以適當(dāng)大小的二進制補碼的形式寫到流中。
在一組符號與一組整數(shù)之間的映射稱為編碼字符集,比如ASCII(將英文字母、數(shù)字、標(biāo)點符號以及一些特殊符號映射為0-127的整數(shù)),Unicode(映射到0~65535之間的的整數(shù))。
編碼方案:發(fā)送者和接收者需要對這些整數(shù)如何表示成字節(jié)序列達(dá)成一致。
字符集:charset,由編碼字符集和字符的編碼方法結(jié)合起來。
Java的輸入輸出流Java里頭內(nèi)置了特定的序列化,隱藏了所有繁瑣的參數(shù)編碼解碼細(xì)節(jié)。Serialization處理了將實際的Java對象轉(zhuǎn)換成字節(jié)序列的工作,因此你可以在不同虛擬機之間傳遞Java對象實例。
缺點是:它們比較籠統(tǒng),在通信開銷上不能做到最高效,比如一個對象的序列化形式其包含的信息在JVM環(huán)境以外是毫無意義的;其次是Serializable和Externalizable接口不能用于已經(jīng)定義了不同傳輸格式的情況;最后用戶自定義的類必須自己實現(xiàn)序列化接口,容易出錯。
public byte[] toWire(VoteMsg msg) throws IOException { String msgString = MAGIC + DELIMSTR + (msg.isInquiry() ? INQSTR : VOTESTR) + DELIMSTR + (msg.isResponse() ? RESPONSESTR + DELIMSTR : "") + Integer.toString(msg.getCandidateID()) + DELIMSTR + Long.toString(msg.getVoteCount()); byte data[] = msgString.getBytes(CHARSETNAME); return data; }
文本方式通常使用一個魔數(shù)來開頭。
2、基于二進制的編碼方式二進制格式使用固定大小的消息,每條消息由一個特殊字節(jié)開始(魔數(shù))。
/* Wire Format * 1 1 1 1 1 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * | Magic |Flags| ZERO | * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * | Candidate ID | * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * | | * | Vote Count (only in response) | * | | * | | * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ public byte[] toWire(VoteMsg msg) throws IOException { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(byteStream); // converts ints short magicAndFlags = MAGIC; if (msg.isInquiry()) { magicAndFlags |= INQUIRE_FLAG; } if (msg.isResponse()) { magicAndFlags |= RESPONSE_FLAG; } out.writeShort(magicAndFlags); // We know the candidate ID will fit in a short: it"s > 0 && < 1000 out.writeShort((short) msg.getCandidateID()); if (msg.isResponse()) { out.writeLong(msg.getVoteCount()); } out.flush(); byte[] data = byteStream.toByteArray(); return data; }
TCP協(xié)議是一個基于流的服務(wù),因而需要提供字節(jié)的幀。
// Create an inquiry request (2nd arg = true) VoteMsg msg = new VoteMsg(false, true, CANDIDATEID, 0); byte[] encodedMsg = coder.toWire(msg); // Send request System.out.println("Sending Inquiry (" + encodedMsg.length + " bytes): "); System.out.println(msg); framer.frameMsg(encodedMsg, out); 這里采用了基于顯示長度的方式來標(biāo)識幀大?。篖engthFarmer類為每條消息添加一個長度前綴。 public void frameMsg(byte[] message, OutputStream out) throws IOException { if (message.length > MAXMESSAGELENGTH) { throw new IOException("message too long"); } // write length prefix out.write((message.length >> BYTESHIFT) & BYTEMASK); out.write(message.length & BYTEMASK); // write message out.write(message); out.flush(); }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65889.html
摘要:以太坊的使用基礎(chǔ)指南通過本節(jié)可以獲取用戶參與到以太坊項目中的基本方法。的發(fā)布是以太坊平臺的第二個主要版本,也是以太坊發(fā)布的第一個正式版本。硬分叉變更以太坊從狹義上來說,是一系列協(xié)議。 以太坊的使用:基礎(chǔ)指南通過本節(jié)可以獲取用戶參與到以太坊項目中的基本方法。首先,要想成為網(wǎng)絡(luò)中的節(jié)點,需要運行一個以太坊客戶端。在選擇客戶端這一節(jié)中列出了多重實現(xiàn),同時針對不同的安裝應(yīng)選擇什么樣的客戶端給出...
摘要:背景現(xiàn)在寫客戶端或者網(wǎng)頁的時候越來越多的需要與長連接打交道尤其是在這個老板動不動就要搞一個聊天系統(tǒng)的時代后端大哥們于是分分鐘就能造一個基于或者的消息協(xié)議出來但是問題在于每做一個新項目后端大哥們就能造出一個新協(xié)議而且能有各種神奇的限制比如說要 背景 現(xiàn)在寫客戶端或者網(wǎng)頁的時候, 越來越多的需要與長連接打交道, 尤其是在這個老板動不動就要搞一個聊天系統(tǒng)的時代, 后端大哥們于是分分鐘就能造一...
閱讀 961·2019-08-30 14:24
閱讀 999·2019-08-30 14:13
閱讀 1807·2019-08-29 17:21
閱讀 2694·2019-08-29 13:44
閱讀 1667·2019-08-29 11:04
閱讀 453·2019-08-26 10:44
閱讀 2573·2019-08-23 14:04
閱讀 915·2019-08-23 12:08