網路傳輸協議: 網路由下往上分為物理層、資料鏈路層、網路層、傳輸層、應用層。 IP協議對應於網路層,TCP協議對應於傳輸層,而HTTP協議對應於應用層,三者從本質上來說沒有可比性,socket則是對TCP/IP協議的封裝和應用(程式設計師層面上)。 TPC/IP協議是傳輸層協議,主要解決資料如何在網路中傳輸,而HTTP是應用層協議,主要解決 如何包裝資料。 IP能鎖定一臺物理機器,對應著一張網路卡,外界發來的資料包網路卡都會接收。如果所有程式都需要監聽網路卡接發資料,每個包都被髮到了所有應用程式,那應用程式符合不了,最後會垮掉,所以就誕生了埠這個標識,從資料安全層面考慮,一個標識號只能被一個應用程式監聽。其實網路卡都是被系統層封裝了,埠和程式之間的關係也是系統封裝好的。我們只需要用socket就行,給定一個埠號就行了。其它的事都交給作業系統去做。 TCP讀取埠號,這個埠號就是建立socket時註冊的,socket建立成功應該有一個process ID,這應該是作業系統來完成的,TCP於是就把[ 埠號 Process ID] 聯絡了起來,於是就和這個Process ID程式交換,完成資料的傳送和接收 tcp的三次握手: 第一次握手主機A通過一個標識為SYN標識位的資料段傳送給主機B請求連線,通過該資料段告訴主機B希望建立連線,需要B應答,並告訴主機B傳輸的起始序列號; 第二次握手是主機B用一個確認應答ACK和同步序列號SYNC標誌位的資料段來響應主機A,一是傳送ACK告訴主機A收到了資料段,二是通知主機A從哪個序列號做標記; 第三次握手是主機A確認收到了主機B的資料段並可以開始傳輸實際資料。 四次斷開: 主機A傳送FIN控制位發出斷開連線的請求; 主機B進行響應,確認收到斷開連線請求; 主機B提出反方向的關閉要求; 主機A確認收到的主機B的關閉連線請求; UDP協議並不提供超時重傳,出錯重傳等功能,所以說其是不可靠的協議。 TCP(Transimision Control Protocal) ==> http ftp smtp ==> 電話
傳輸控制協議 可靠的、面向連線的協議 傳輸效率低
UDP(User Datagram Protocal) ==> qq, 微信 ==> 廣播
使用者資料包協議 不可靠的、無連線的服務 傳輸效率高。 TCP是面向連結的,雖然說網路的不安全不穩定特性決定了多少次握手都不能保證連線的可靠性,但TCP的三次握手在最低限度上(實際上也很大程度上保證了)保證了連線的可靠性;而UDP不是面向連線的,UDP傳送資料前並不與對方建立連線,對接收到的資料也不傳送確認訊號,傳送端不知道資料是否會正確接收,當然也不用重發,所以說UDP是無連線的、不可靠的一種資料傳輸協議。 2。也正由於1所說的特點,使得UDP的開銷更小資料傳輸速率更高,因為不必進行收發資料的確認,所以UDP的實時性更好。
知道了TCP和UDP的區別,就不難理解為何採用TCP傳輸協議的MSN比採用UDP的QQ傳輸檔案慢了,但並不能說QQ的通訊是不安全的,因為程式設計師可以手動對UDP的資料收發進行驗證,比如傳送方對每個資料包進行編號然後由接收方進行驗證啊什麼的,即使是這樣,UDP因為在底層協議的封裝上沒有采用類似TCP的“三次握手”而實現了TCP所無法達到的傳輸效率。 net模組也是node的核心模組,用於底層的網路通訊; http.Server繼承了net.Server; http客戶端與http服務端的通訊均依賴於socket(net.Socket); 主要包含兩個部分:
net.Server tcp/server, 服務端TCP監聽來自客戶端的請求,並使用TCP連線(socket)向客戶端傳送資料; 內部通過socket來實現與客戶端的通訊;
net.Socket tcp/本地,客戶端TCP連線到伺服器,並與伺服器交換資料; socket的node實現,實現了全雙工的stream的介面;
服務端net.Server let net = require('net') let PORT = 8081 let HOST = 'localhost' /**
-
- 建立一個TCP伺服器例項,呼叫listen函式開始監聽指定埠;
-
- 傳入net.createServer()的回撥函式,作為connection事件的處理函式;
-
- 在每個connection事件中,該回撥函式接收到的socket物件是唯一的;
-
- 該連線自動關聯一個socket物件
-
*/ let server = net.createServer((socket) => { console.log('connection:' + socket.remoteAddress, socket.remotePort) // 為這個socket例項新增一個“data”事件處理函式 socket.on('data', (data) => { console.log('DATA' + socket.remoteAddress + ":" + data); socket.write('You said "'+ data +'"\r\n') // 向客戶端回發該資料 })
socket.on('end', () => { console.log('客戶端關閉') /** * 服務端收到客戶端發出的關閉連線請求時,會觸發end事件 * 這個時候客戶端沒有真正的關閉,只是開始關閉; * 當真正的關閉的時候,會觸發close事件; * */ server.unref(); //呼叫了該方法,則所有的客戶端關閉跟本伺服器的連線後,將關閉伺服器 })
// 客戶端關閉事件 socket.on('close', () => { console.log('CLOSED: ' + socket.remoteAddress + ' ' + socket.remotePort); })
/socket.pause() socket.setTimeout(3000) //設定客戶端超時時間,如果客戶端一直不輸入,超過這個時間,就認為超時了 socket.on('timeout', () => { console.log('超時了') socket.pipe(ws, {end: false}) // 預設情況下,當可讀流讀到末尾的時候會關閉可寫流 })/ })
server.listen(PORT, HOST, () => { console.log('服務端的地址是:', server.address()) })
server.on('error', (err) => { console.log(err) })
//服務端也可以通過顯式處理"connection"事件來建立TCP連線,只是寫法不同,二者沒有區別即: /* let server = net.createServer() server.listen(PORT,HOST) server.on('connection', (socket) => { console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort); })*/ server.on('close', () => { //關閉伺服器,停止接收新的客戶端的請求 console.log( 'close事件:服務端關閉' ); })
server.on('error', (error) => { console.log( 'error事件:服務端異常:' + error.message ); })
let net = require('net')
//建立一個TCP客戶端連線到剛建立的伺服器上,該客戶端向伺服器傳送一串訊息,並在得到伺服器的反饋後關閉連線。
var client = new net.Socket() let PORT = 8081 let HOST = 'localhost'
client.connect(PORT, HOST, () => { console.log('connect to ' + HOST + ':' + PORT) client.write('I am happyGloria.') //建立連線後立即向伺服器傳送資料,伺服器將收到這些資料 })
client.on('data', (data) => { console.log('DATA: ' + data) client.destroy() // 完全關閉連線 })
client.on('close', function () { console.log('Connection closed') })
基於tcp的聊天室 let net = require('net') let util = require('util') let HOST = 'localhost' let PORT = 8082 let clients = {}
function broadcast (username, msg) { for (let name in clients) { if (name != username) { clients[name].write(msg + '\r\n') } } }
let server = net.createServer((socket) => { socket.setEncoding('utf8') server.getConnections((err, count) => { socket.write('線上人數是' + count + '位,請輸入你的暱稱:\r\n') })
let username
socket.on('data', (data) => {
data = data.replace(/\r\n/, '')
if (username) {
broadcast(username, `${username} 說: ${data}`)
} else {
if (clients[data]) {
socket.write('您的暱稱' + data + '被佔用了,請您更換新的暱稱\r\n')
} else {
username = data
clients[username] = socket
broadcast(username, `歡迎${username}加入`)
}
}
})
socket.on('end', () => {
broadcast(username, `${username}離開聊天室`)
clients[username] && clients[username].destroy()
delete clients[username]
})
複製程式碼
})
server.listen(PORT, HOST, () => {
console.log(tcp聊天室已啟動,地址是${util.inspect(server.address())}
)
})