TCP
·TCP(Transmission Control Protocol傳輸控制協議)是一個可靠的面向連接的傳輸層協議
TCP/IP分層模型分為了七層,在每一層中都包含了一些相對獨立的具體的協議。從上往下分為應用層、表示層、會話層、傳輸層、網絡層,數據鏈路層和物理層。
tcp數據包控制位URG、ACK、PSH、PST、SYN、FIN,對tcp的連接、傳輸和斷開進行指揮。
·它可以讓你將數據從一台計算機完整有序的傳輸到另一台計算機,內置機制能夠控制數據包的延遲率及丟包率不會太高
·發送方將數據轉為字節流分成,將數據交給IP層。接收方接收后重新裝配成原始的數據。
應用層發出請求(數據,有含義的,http等),發出的請求在傳輸層添加TCP控制信息(TCP頭部+數據=>數據段segment),再傳遞給網絡層添加IP頭部標識唯一地址(IP頭部+數據段=>數據包Packet),然后到達數據鏈路層添加MAC頭部及尾部讓兩者的網卡進行通信,之后通過物理層(網線、光纖等)以比特流的形式進行傳輸。到達目標主機之后,由下向上進行解析,在數據鏈路層去除mac頭尾,再向上到達網絡層去掉IP頭部,再向上到達傳輸層去除TCP頭部,最終到達應用層得到數據。
·TCP對字符和字符編碼是完全無知的,不同的編碼會導致傳輸的字節數不同
·TCP使用流控制來確保兩點之間傳輸數據的平衡,以防止快速的發送方淹沒慢速的接收方
·TCP在傳輸前要經過3次握手才能形成會話,只有會話形成后,服務器端和客戶端才能互相發送數據。
TCP三次握手:第一次握手是主機A向主機B請求建立連接,發送SYN;第二次握手是主機B同意主機A的連接請求,發送SYN、ACK;第三次握手是主機A向主機B發送ACK。
·在會話過程中,服務器和客戶端分別提供一個套接字(socket),這兩個套接字共同形成一個連接。服務器和客戶端通過套接字進行通信
TCP服務器
開發過程中主要控制的是應用層和傳輸層。
net模塊用於實現TCP服務器端和客戶端之間的通信。
·options參數:
-allowHalfOpen:是否允許半開連接。屬性值為false時,TCP服務器收到客戶端的FIN包時會回發FIN包,為true時,服務器收到客戶端FIN包不回發FIN包
·connectionListener=function(socket){}客戶端連接時的回調函數
-socket表示服務器監聽的socket端口對象
·返回被創建的服務器server
·listen(port,[host],[backlog],[callback])
-port監聽的端口號
-host監聽的IP地址或主機名
-backlog等待隊列中的最大數量(默認511)
創建一個tcpserver.js
var net = require('net'); var util = require('util'); //net.Socket 雙工流 Duplux
var server = net.createServer({allowHalfOpen:true},function(socket){ console.log(util.inspect(socket.address())); //查看當前連接數量
server.getConnections(function(err,count){ console.log(count); }); socket.on('error',function(err){//監聽客戶端的錯誤
console.log(err); socket.destroy(); }); socket.setEncoding('utf8');//可讀流的方法
socket.on('data',function(data){ console.log(data); }) socket.on('close',function(err){//end事件與close事件的區別是:close一定會觸發,end則不一定觸發
console.log(err); socket.destroy(); }) }) server.on('error',function(err){//監聽服務器端的錯誤
console.log(err); }); server.listen(8089,function(){ console.log(util.inspect(server.address())); setTimeout(function(){ server.close();//客戶端全都斷開后才會關閉服務端
},3000) }); server.on('close',function(err){ console.log('服務器端正常關閉'); });
寫一個tcpSocket.js
var net = require('net'); var util = require('util'); var fs = require('fs'); //net.Socket 雙工流 Duplux
var ws = fs.createWriteStream('./socket.txt'); var server = net.createServer({allowHalfOpen:true},function(socket){ socket.setTimeout(10*1000); socket.on('timeout',function(){ socket.resume(); socket.pipe(ws,{end:false}); ws.write(socket.remoteAddress) }); }) server.on('error',function(err){ console.log(err); }); server.listen(8089,function(){ console.log(util.inspect(server.address())); });
socket
·net.socket代表一個socket端口對象
·socket端口對象可用來讀取客戶端發送的流數據,讀到數據時觸發data事件
-socket.on('data',function(){})
·創建socket: var socket = new net.Socket([options])
-options
-fd socket文件描述符
-type 客戶端協議,tcp4或tcp6
-allowHalfOpen是否允許半開連接
.連接TCP服務器:socket.connect(port,[host],[connectListener])
-port 端口
-host主機地址
-connectListener 連接成功后的監聽
·向服務器發送數據:socket.write(data,[encoding],[callback])
-data 寫入的數據
-encoding 編碼
創建一個tcpClient.js
var net = require('net'); var util = require('util'); var socket = new net.Socket({allowHalfOpen:true}); socket.setEncoding('utf8'); socket.connect(8089,'localhost',function(){ socket.write('hello');//客戶端向服務端發送數據 socket.on('data',function(data){//客戶端接收來自服務器的方法 console.log(data); }); });
net類方法
·net.isIP 判斷字符串是否是IP
·net.isIPv4 是否是IPv4地址
·net.isIPv6 是否IPv6地址
UDP
tcp是基於連接的協議,進行通信前客戶端與服務器要先建立連接。UDP是面向非連接的協議,可能直接發數據包
UDP不要求分組順序到達傳輸層中
受網絡影響可能丟失數據包
資源消耗少,處理速度快,適合音頻、視頻和普通數據傳輸
UDP協議中的包成為數據報datagram
UDP協議不區分客戶端和服務端的概念,都可以創建socket對象
·var socket = dgram.createSocket(type,[callback])
·type 協議類型可以是udp4或udp6
·callback = function(msg,rinfo)收到數據時的回調函數
-msg收到的數據
-rinfo
-address 發送者的IP
-family 地址類型
-port發送者的socket端口號
-size發送者發送的數據字節數
·綁定地址和端口 socket.bind(port,[address],[callback])
·發送數據 socket.send(buf,offset,length,port,address,[callback])
-buf 要發送的數據buffer
-offset從緩存區中第幾個字節開始發送數據
-length發送數據的字節數
-port接收數據的端口號
-address 接收數據的IP地址
-callback function(err,bytes){}發送完畢時所調的回調函數
-err 發送出錯時觸發的錯誤對象
-bytes發送數據的字節數
創建一個udpServer.js
var dgram = require('dgram'); var socket = dgram.createSocket('udp4'); socket.on('message',function(msg,rinfo){ console.log(msg.toString()); console.log(rinfo); socket.send(msg,0,msg.length,rinfo.port,rinfo.address); }); socket.bind(41234,'localhost');
創建一個udpClient.js
var dgram = require('dgram'); var socket = dgram.createSocket('udp4'); socket.on('message',function(msg,rinfo){ console.log(msg.toString()); console.log(rinfo); }); socket.send(new Buffer('珠峰培訓'),0,6,41234,'localhost',function(err,bytes){ console.log('發送了個%d字節',bytes); }); socket.on('error',function(err){ console.error(err); });
創建一個即時通訊的tcp聊天chatServer.js
/** * 1.創建一個服務器 * 2. 客戶端可以連接服務器 * 3.客戶端可以發言,然后廣播給大家 * 4.客戶端連接和退出后都要通知大家。 * 5.顯示當前的在線人數 */
var net = require('net'); var util = require('util'); var clients = {}; var server = net.createServer(function(socket){ var nickname; socket.setEncoding('utf8'); server.getConnections(function(err,count){ socket.write('歡迎光臨,當前在線'+count+'人,請輸入用戶名\r\n>'); }); socket.on('data',function(data){ //console.log(new Buffer(data));
data = data.replace(/\r\n/,''); if(nickname){ broadcast(nickname,nickname+":"+data+'\r\n'); }else{ nickname = data; clients[nickname] = socket; broadcast(nickname,nickname+'加入了聊天室\r\n'); } }); socket.on('end',function(){ broadcast(nickname,nickname+'離開了聊天室\r\n'); clients[nickname].destroy(); delete clients[nickname]; }); socket.on('error',function(){ }); socket.on('close',function(){ }); }) function broadcast(nickname,msg){ for(var name in clients){ if(nickname != name) clients[name].write(msg); } } server.listen(8088);