⑴ Nodejs學習筆記之NET模塊
一,開篇分析
從今天開始,我們來深入具體的模塊學習,這篇文章是這個系列文章的第三篇,前兩篇主要是以理論為主,相信大家在前兩篇的學習中,
對NodeJS也有一個基本的認識,沒事!!!趁熱打鐵,讓我們繼續將NodeJS進行到底,好了廢話不多說,直接進入今天的主題
「Net模塊」
,那麼」Net「應該如何理解那?
它是做什麼用的那?(Net模塊可用於創建Socket伺服器或Socket客戶端。NodeJS
的數據通信,最基礎的兩個模塊是
Net
和
Http,前者是基於
Tcp
的封裝,後者本質還是
Tcp
層,只不過做了比較多的數據封裝,我們視為表現層)。
這里參考一下NodeJS
「http.js」
中的源碼:
從圖中不難看出
HttpServer繼承了Net類,具有了相關的通信能力,做了比較多的數據封裝,我們視為更高級的表現層。
擴展知識(以下是「inherits」的源碼):
復制代碼
代碼如下:
exports.inherits
=
function(ctor,
superCtor)
{
ctor.super_
=
superCtor;
ctor.prototype
=
Object.create(superCtor.prototype,
{
constructor:
{
value:
ctor,
enumerable:
false,
writable:
true,
configurable:
true
}
});
};
功能是實現繼承復用。
剛才做了一個簡要的概述,裡面有一些常用的概念,這里做個簡短的概念普及介紹:
(1),TCP/IP------TPC/IP協議是傳輸層協議,主要解決數據如何在網路中傳輸。
(2),Socket------socket則是對TCP/IP協議的封裝和應用(程序層面)。
(3),Http------HTTP是應用層協議,主要解決如何包裝數據。
(4),網路七層模型------物理層、數據鏈路層、網路層、傳輸層、會話層、表示層和應用層。
總結一下:Socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個調用介面(API)。
從而形成了我們知道的一些最基本的函數介面,比如Create、Listen、Connect、Accept、Send、Read和Write等等。
TCP/IP只是一個協議棧,就像操作系統的運行機制一樣,必須要具體實現,同時還要提供對外的操作介面
實際上,傳輸層的TCP是基於網路層的IP協議的,而應用層的HTTP協議又是基於傳輸層的TCP協議的,而Socket本身不算是協議,就像上面所說,它只是提供了一個針對TCP或者UDP編程的介面。
二,體驗一把
好了,概念我們也有了,來個例子:
1,建立server.js
復制代碼
代碼如下:
var
net
=
require('net')
;
var
server
=
net.createServer(function(c)
{
//
Connection監聽器
console.log("伺服器已連接")
;
c.on("end",
function()
{
console.log("伺服器已斷開")
;
})
;
c.write("Hello,Bigbear
!
")
;
c.pipe(c)
;
})
;
server.listen(8124,
function()
{
//
Listening監聽器
console.log("伺服器已綁定")
;
})
;
2,建立client.js
復制代碼
代碼如下:
var
net
=
require('net')
;
var
client
=
net.connect({
port:
8124
},function(){
//
connect監聽器
console.log("客戶端已連接")
;
client.write('Hello,Baby
!
')
;
});
client.on("data",
function(data)
{
console.log(data.toString())
;
client.end()
;
});
client.on("end",
function(){
console.log("客戶端斷開連接")
;
})
;
分析一下:
服務端------net.createServer創建一個
TCP
服務,這個服務綁定(server.listen)在
8124
這個埠上,創建
Server
後我們看到有一個回調函數,
在調用上面函數的時候傳入一個參數,這個參數也是函數,並且接受了
socket
,這個由其他方法構造的一個管道(pipe),他的作用就是用來數據交互的。
pipe
是需要
Client
跟
Server
打招呼才能建立的,如果此刻沒有客戶端訪問
Server,這個
socket
就不會存在了。
客戶端------net.connect顧名思義,就是連接到服務端,第一個參數是對象,設置埠(port)為
8124,也就是我們伺服器監聽的埠,由於沒有設置
host
參數,那默認就是
localhost
(本地)。
在
Server
中,socket
是管道的一端,而在
client
中,client
本身就是管道的一端,如果是多個客戶端連接
Server,Server
會新建多個
socket,每個
socket
對應一個
client。
運行結果:
三,案例引入
(1),下面代碼僅僅是伺服器向客戶端輸出一段文本,完成服務端到客戶端的單向通訊。
復制代碼
代碼如下:
//
Sever
-->
Client
的單向通訊
var
net
=
require('net');
var
chatServer
=
net.createServer();
chatServer.on('connection',
function(client)
{
client.write('Hi!
');
//
服務端向客戶端輸出信息,使用
write()
方法
client.write('Bye!
');
client.end();
//
服務端結束該次會話
});
chatServer.listen(9000);
Telnet測試一下:telnet127.0.0.1:9000
執行
telnet後,與服務點連接,反饋
Hi!
Bye!
的字元,並立刻結束服務端程序終止連接。
如果我們要服務端接到到客戶端的信息?
可以監聽
server.data
事件並且不要中止連接(否則會立刻結束無法接受來自客戶端的消息)。
(2),監聽
server.data
事件並且不要中止連接(否則會立刻結束無法接受來自客戶端的消息)。
復制代碼
代碼如下:
//
在前者的基礎上,實現
Client
-->
Sever
的通訊,如此一來便是雙向通訊
var
net
=
require('net');
var
chatServer
=
net.createServer(),
clientList
=
[];
chatServer.on('connection',
function(client)
{
//
JS
可以為對象自由添加屬性。這里我們添加一個
name
的自定義屬性,用於表示哪個客戶端(客戶端的地址+埠為依據)
client.name
=
client.remoteAddress
+
':'
+
client.remotePort;
client.write('Hi
'
+
client.name
+
'!
');
clientList.push(client);
client.on('data',
function(data)
{
broadcast(data,
client);//
接受來自客戶端的信息
});
});
function
broadcast(message,
client)
{
for(var
i=0;i<clientList.length;i+=1)
{
if(client
!==
clientList[i])
{
clientList[i].write(client.name
+
"
says
"
+
message);
}
}
}
chatServer.listen(9000);
上面是不是一個完整功能的代碼呢?我們說還有一個問題沒有考慮進去:那就是一旦某個客戶端退出,卻仍保留在
clientList裡面,這明顯是一個空指針。
(3),處理clientList
復制代碼
代碼如下:
chatServer.on('connection',
function(client)
{
client.name
=
client.remoteAddress
+
':'
+
client.remotePort
client.write('Hi
'
+
client.name
+
'!
');
clientList.push(client)
client.on('data',
function(data)
{
broadcast(data,
client)
})
client.on('end',
function()
{
clientList.splice(clientList.indexOf(client),
1);
//
刪除數組中的制定元素。
})
})
NodeTCPAPI已經為我們提供了
end
事件,即客戶端中止與服務端連接的時候發生。
(4),優化broadcast
復制代碼
代碼如下:
function
broadcast(message,
client)
{
var
cleanup
=
[]
for(var
i=0;i<clientList.length;i+=1)
{
if(client
!==
clientList[i])
{
if(clientList[i].writable)
{
//
先檢查
sockets
是否可寫
clientList[i].write(client.name
+
"
says
"
+
message)
}
else
{
cleanup.push(clientList[i])
//
如果不可寫,收集起來銷毀。銷毀之前要
Socket.destroy()
用
API
的方法銷毀。
clientList[i].destroy()
}
}
}
//Remove
dead
Nodes
out
of
write
loop
to
avoid
trashing
loop
index
for(i=0;i<cleanup.length;i+=1)
{
clientList.splice(clientList.indexOf(cleanup[i]),
1)
}
}
注意的是一旦
「end」
沒有被觸發,會出現異常,所以才做優化工作。
(5),NetAPI中還提供一個
error
事件,用於捕捉客戶端的異常
復制代碼
代碼如下:
client.on('error',
function(e)
{
console.log(e);
});
四,總結一下
1,理解開篇的相關概念
2,認識Http與Net模塊之間的關系
3,結合本文的例子,查閱相關api去實踐
4,socket客戶端與伺服器端之間的通信思想
5,有興趣可以完善一下那個聊天室的例子
⑵ 怎麼連接nodejs伺服器
前面已經學習了WebSocket API,包括事件、方法和屬性。詳情:WebSocket(二)--APIWebSocket是基於事件驅動,支持全雙工通信。下面通過三個簡單例子體驗一下。
簡單開始
1.安裝node。/
2.安裝ws模塊
ws:是nodejs的一個WebSocket庫,可以用來創建服務。
3.server.js
在項目裡面新建一個server.js,創建服務,指定8181埠,將收到的消息log出來。
//建立連接
var ws = new WebSocket("ws://localhost:8181"); var nickname = "";
ws.onopen = function (e) {
console.log('Connection to server opened');
} //顯示
function appendLog(type, nickname, message) { if (typeof message == "undefined") return; var messages = document.getElementById('messages'); var messageElem = document.createElement("li"); var preface_label; if (type === 'notification') {
preface_label = "<span class="label label-info">*</span>";
} else if (type == 'nick_update') {
preface_label = "<span class="label label-warning">*</span>";
} else {
preface_label = "<span class="label label-success">"
+ nickname + "</span>";
} var message_text = "<h2>" + preface_label + " "
+ message + "</h2>";
messageElem.innerHTML = message_text;
messages.appendChild(messageElem);
} //收到消息處理
ws.onmessage = function (e) { var data = JSON.parse(e.data);
nickname = data.nickname;
appendLog(data.type, data.nickname, data.message);
console.log("ID: [%s] = %s", data.id, data.message);
}
ws.onclose = function (e) {
appendLog("Connection closed");
console.log("Connection closed");
} //發送消息
function sendMessage() { var messageField = document.getElementById('message'); if (ws.readyState === WebSocket.OPEN) {
ws.send(messageField.value);
}
messageField.value = '';
messageField.focus();
} //修改名稱
function changName() { var name = $("#name").val(); if (ws.readyState === WebSocket.OPEN) {
ws.send("/nick " + name);
}
}
運行結果:
頁面關閉之後,連接馬上斷開。
這種實時響應的體驗簡直不能太爽,代碼也清爽了,前端體驗也更好,客戶端不用一直發請求,服務端不用等著被輪詢。
小結:上面例子的代碼都很好理解,接下來學習WebSocket協議。
⑶ c#怎麼向nodejs的socket.io發送請求
1. 簡介
首先是Socket.IO的官方網站:http://socket.io
官網非常簡潔,甚至沒有API文檔,只有一個簡單的「How to use」可以參考。因為Socket.IO就跟官網一樣簡潔好用易上手。
那麼Socket.IO到底是什麼呢?Socket.IO是一個WebSocket庫,包括了客戶端的js和伺服器端的nodejs,它的目標是構建可以在不同瀏覽器和移動設備上使用的實時應用。它會自動根據瀏覽器從WebSocket、AJAX長輪詢、Iframe流等等各種方式中選擇最佳的方式來實現網路實時應用,非常方便和人性化,而且支持的瀏覽器最低達IE5.5,應該可以滿足絕大部分需求了。
2. 安裝部署
2.1 安裝
首先安裝非常簡單,在node.js環境下只要一句:
復制代碼
代碼如下:
npm install socket.io
2.2 結合express來構建伺服器
express是一個小巧的Node.js的Web應用框架,在構建HTTP伺服器時經常使用到,所以直接以Socket.IO和express為例子來講解。
復制代碼
代碼如下:
var express = require('express')
,
app = express()
, server = require('http').createServer(app)
, io
= require('socket.io').listen(server);
server.listen(3001);
若不使用express,請參考socket.io/#how-to-use
3. 基本使用方法
主要分為伺服器端和客戶端兩段代碼,都非常簡單。
Server(app.js):
復制代碼
代碼如下:
//接上面的代碼
app.get('/', function (req,
res) {
res.sendfile(__dirname + '/index.html');});
io.sockets.on('connection', function (socket) {
socket.emit('news', {
hello: 'world' });
socket.on('other event', function (data) {
console.log(data);
});
});
首先io.sockets.on函數接受字元串"connection"作為客戶端發起連接的事件,當連接成功後,調用帶有socket參數的回調函數。我們在使用socket.IO的時候,基本上都在這個回調函數裡面處理用戶的請求。
socket最關鍵的是emit和on兩個函數,前者提交(發出)一個事件(事件名稱用字元串表示),事件名稱可以自定義,也有一些默認的事件名稱,緊接著是一個對象,表示向該socket發送的內容;後者接收一個事件(事件名稱用字元串表示),緊接著是收到事件調用的回調函數,其中data是收到的數據。
在上面的例子中,我們發送了news事件和收到了other
event事件,那麼客戶端應該會有對應的接收和發送事件。沒錯,客戶端代碼和伺服器正好相反,而且非常相似。
Client(client.js)
復制代碼
代碼如下:
<script
src="/socket.io/socket.io.js"></script>
<script>
var
socket = io.connect('http://localhost');
socket.on('news', function
(data) {
console.log(data);
socket.emit('other event', {
my: 'data' });
});
</script>
有兩點要注意的:socket.io.js路徑要寫對,這個js文件實際放在了伺服器端的node_moles文件夾中,在請求這個文件時會重定向,因此不要詫異伺服器端不存在這個文件但為什麼還能正常工作。當然,你可以把伺服器端的socket.io.js這個文件拷貝到本地,使它成為客戶端的js文件,這樣就不用每次都向Node伺服器請求這個js文件,增強穩定性。第二點是要用var
socket =
io.connect('網站地址或ip');來獲取socket對象,接著就可以使用socket來收發事件。關於事件處理,上面的代碼表示收到「news」事件後,列印收到的數據,並向伺服器發送「other
event」事件。
註:內置默認的事件名例如「disconnect」表示客戶端連接斷開,「message」表示收到消息等等。自定義的事件名稱,盡量不要跟Socket.IO中內置的默認事件名重名,以免造成不必要的麻煩。
4. 其他常用API
1).向所有客戶端廣播:socket.broadcast.emit('broadcast message');
2).進入一個房間(非常好用!相當於一個命名空間,可以對一個特定的房間廣播而不影響在其他房間或不在房間的客戶端):socket.join('your
room name');
3).向一個房間廣播消息(發送者收不到消息):socket.broadcast.to('your room name').emit('broadcast
room message');
4).向一個房間廣播消息(包括發送者都能收到消息)(這個API屬於io.sockets):io.sockets.in('another room
name').emit('broadcast room message');
5).強制使用WebSocket通信:(客戶端)socket.send('hi'),(伺服器)用socket.on('message',
function(data){})來接收。
5. 使用Socket.IO構建一個聊天室
最後,我們通過一個簡單的實例來結束本篇。用Socket.IO構建一個聊天室就是50行左右的代碼的事情,實時聊天效果也非常好。以下貼出關鍵代碼:
Server(socketChat.js)
復制代碼
代碼如下:
//一個客戶端連接的字典,當一個客戶端連接到伺服器時,
//會產生一個唯一的socketId,該字典保存socketId到用戶信息(昵稱等)的映射
var
connectionList = {};
exports.startChat = function (io) {
io.sockets.on('connection',
function (socket) {
//客戶端連接時,保存socketId和用戶名
var socketId =
socket.id;
connectionList[socketId] = {
socket:
socket
};
//用戶進入聊天室事件,向其他在線用戶廣播其用戶名
socket.on('join', function
(data) {
socket.broadcast.emit('broadcast_join',
data);
connectionList[socketId].username =
data.username;
});
//用戶離開聊天室事件,向其他在線用戶廣播其離開
socket.on('disconnect', function
() {
if (connectionList[socketId].username) {
socket.broadcast.emit('broadcast_quit', {
username:
connectionList[socketId].username
});
}
delete connectionList[socketId];
});
//用戶發言事件,向其他在線用戶廣播其發言內容
socket.on('say', function (data)
{
socket.broadcast.emit('broadcast_say',{
username: connectionList[socketId].username,
text:
data.text
});
});
})
};
Client(socketChatClient.js)
復制代碼
代碼如下:
var socket =
io.connect('http://localhost');
//連接伺服器完畢後,馬上提交一個「加入」事件,把自己的用戶名告訴別人
socket.emit('join',
{
username: 'Username hehe'
});
//收到加入聊天室廣播後,顯示消息
socket.on('broadcast_join', function (data) {
console.log(data.username + '加入了聊天室');
});
//收到離開聊天室廣播後,顯示消息
socket.on('broadcast_quit', function(data) {
console.log(data.username + '離開了聊天室');
});
//收到別人發送的消息後,顯示消息
socket.on('broadcast_say', function(data) {
console.log(data.username + '說: ' + data.text);
});
//這里我們假設有一個文本框textarea和一個發送按鈕.btn-send
//使用jQuery綁定事件
$('.btn-send').click(function(e)
{
//獲取文本框的文本
var text = $('textarea').val();
//提交一個say事件,伺服器收到就會廣播
socket.emit('say', {
username: 'Username
hehe'
text: text
});
});
⑷ 什麼是node.js
1、起源
Node.js起源於2009年3月。最初Ryan Dahl是為了構建一個高性能的文本伺服器,而node.js是他在尋找一種更高提升web伺服器性能發現的,事件驅動、非阻塞I/O這種方式能更好的提高性能。隨著Nodejs的不斷發展,Nodejs漸漸演變成一種構建網路應用的基礎框架,並發展為一個不共享任何資源的單線程、單進程系統,但包含了很適合網路的庫,這樣Nodejs就為構建大型分布式應用提供了基礎設施。他們的目標都是為了構建快速、可伸縮的網路應用平台。它自身非常簡單、採用通信協議來組織許多的Node,非常容易通過拓展來達成構建大型網路應用的目的。
2、node.js簡介
Node.js是一個可以快速構建網路服務及應用的平台。該平台的構建是基於Chrome'sJavaScriptruntime,也就是說,實際上它是對GoogleV8引擎(應用於GoogleChrome瀏覽器)進行了封裝。Nodejs不是一個js應用而是一個js運行平台。其是由C++編寫而成。但是Nodejs是一個後端的運行環境。因此你可以編寫系統級或者伺服器端的js讓Nodejs幫你執行。
3、優勢
1)node.js採用事件驅動、非同步編程,為網路服務而設計。
2)node.js採用非阻塞模式的IO處理,可以使node.js在相對低系統資源耗,擁有出色的負載能力,非常適合用作依賴其它IO資源的中間層服務。
3)node.js輕量高效,可以認為是數據密集型分布式部署環境下的實時應用系統的完美解決方案。
4)使用node.js配合CoffeeScript 寫非同步操作鏈非常便利,相比之下Tornado無論是寫命名函數的回調,還是 yield 一個 Task 都沒那麼自然。