亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

使用Angular和Nodejs、socket.io搭建聊天室及多人聊天室

 更新時間:2015年08月21日 10:46:59   投稿:mrr  
本文主要給大家詳細介紹Angular和Nodejs、socket.io的使用,以及如何使用Angular和Nodejs、socket.io搭建聊天室及多人聊天室,需要的朋友可以來參考下

一,利用Node搭建靜態(tài)服務器

       這個是這個項目的底層支撐部分。用來支持靜態(tài)資源文件像html, css, gif, jpg, png, javascript, json, plain text等等靜態(tài)資源的訪問。這里面是有一個mime類型的文件映射。

mime.js

/**
 * mime類型的 map
 * @ author Cheng Liufeng
 * @ date 2014/8/30
 * 當請求靜態(tài)服務器文件的類型 html, css, gif, jpg, png, javascript, json, plain text, 我們會在此文件進行映射
 */
exports.types = {
 "css": "text/css",
 "gif": "image/gif",
 "html": "text/html",
 "ico": "image/x-icon",
 "jpeg": "image/jpeg",
 "jpg": "image/jpeg",
 "js": "text/javascript",
 "json": "application/json",
 "pdf": "application/pdf",
 "png": "image/png",
 "svg": "image/svg+xml",
 "swf": "application/x-shockwave-flash",
 "tiff": "image/tiff",
 "txt": "text/plain",
 "wav": "audio/x-wav",
 "wma": "audio/x-ms-wma",
 "wmv": "video/x-ms-wmv",
 "xml": "text/xml"
};

  這里面我先解釋一下從輸入網址到頁面出現的過程。 當用戶在瀏覽器地址欄里面輸入一個url的時候。

接下來會發(fā)生一系列的過程。首先是DNS解析, 將域名轉換成對應的IP地址,之后瀏覽器與遠程Web服務器通過TCP三次握手協商來建立一個TCP/IP連接。該握手包括一個同步報文,開一個同步-應答報文和一個應答報文,這三個報文在瀏覽器和服務器之間傳遞。該握手首先由客戶端嘗試建立起通信,而后服務器應答并接受客戶端的請求,最后由客戶端發(fā)出該請求已經被接受的報文。一旦TCP/IP連接建立,瀏覽器會通過該連接向遠程服務器發(fā)送HTTP的GET請求。

遠程服務器找到資源并使用HTTP響應返回該資源,值為200的HTTP響應狀態(tài)表示一個正確的響應。此時,Web服務器提供資源服務,客戶端開始下載資源。下載的資源包括了html文件,css文件,javascript文件,image文件。然后開始構建一顆渲染樹和一顆DOM樹,期間會有css阻塞和js阻塞。所以底層是需要一個靜態(tài)服務器支撐。這里面我原生構造一個靜態(tài)服務器,不采用express框架。

       事實上每一次資源文件請求的過程是一次次GET請求。下面我解釋一下客戶端(瀏覽器端或者采用linux下采用curl方式)的GET請求所對應的服務端處理過程。一次Get請求發(fā)送到服務端后,服務端可以根據GET請求對應一個資源文件的路徑。知道了這個路徑后,我們就可以采用文件讀寫的方式獲取指定路徑下的資源,然后返回給客戶端。

        我們知道Node里面的文件讀寫的API有readFile和readFileSync,但是更好的方式是采用流的方式去讀取文件,采用流的方式的優(yōu)點是可以采用緩存和gzip壓縮。 

       OK,那么如何實現緩存呢?通常情況下,客戶端第一次去請求的時候,服務端會讀取資源文件,返回給客戶端。但是第二次再去請求同樣的文件時,這個時候還是需要發(fā)送一次請求到服務端。服務端會根據Expires, cache-control, If-Modified-Since等Http頭信息判斷這個資源是否已經緩存過。如果有緩存,服務端則不會再次訪問資源文件的實際路徑。直接返回緩存的資源。

server.js

/**
 * 聊天室服務端
 * 功能:實現了Node版的靜態(tài)服務器
 * 實現了緩存,gzip壓縮等
 * @ author Cheng Liufeng
 * @ date 2014/8/30
 */
 // 設置端口號
var PORT = 3000;
// 引入模塊
var http = require('http');
var url = require('url');
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');
// 引入文件
var mime = require('./mime').types;
var config = require('./config');
var chatServer = require('./utils/chat_server');
var server = http.createServer(function (req, res) {
 res.setHeader("Server","Node/V8");
 // 獲取文件路徑
 var pathName = url.parse(req.url).pathname;
 if(pathName.slice(-1) === "/"){
 pathName = pathName + "index.html"; //默認取當前默認下的index.html
 }
 // 安全處理(當使用Linux 的 curl命令訪問時,存在安全隱患)
 var realPath = path.join("client", path.normalize(pathName.replace(/\.\./g, "")));
 // 檢查文件路徑是否存在
 path.exists(realPath, function(exists) {
 // 當文件不存在時的情況, 輸出一個404錯誤
 if (!exists) {
  res.writeHead(404, "Not Found", {'Content-Type': 'text/plain'});
  res.write("The request url" + pathName +" is not found!");
  res.end();
 } else {   // 當文件存在時的處理邏輯
  fs.stat(realPath, function(err, stat) {
    // 獲取文件擴展名
  var ext = path.extname(realPath);
  ext = ext ? ext.slice(1) : "unknown";
  var contentType = mime[ext] || "text/plain";
  // 設置 Content-Type
  res.setHeader("Content-Type", contentType);
  var lastModified = stat.mtime.toUTCString();
  var ifModifiedSince = "If-Modified-Since".toLowerCase();
  res.setHeader("Last-Modified", lastModified);
  if (ext.match(config.Expires.fileMatch)) {
   var expires = new Date();
   expires.setTime(expires.getTime() + config.Expires.maxAge * 1000);
   res.setHeader("Expires", expires.toUTCString());
   res.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge);
  }
  if (req.headers[ifModifiedSince] && lastModified == req.headers[ifModifiedSince]) {
   res.writeHead(304, "Not Modified");
   res.end();
  } else {
   // 使用流的方式去讀取文件
   var raw = fs.createReadStream(realPath);
   var acceptEncoding = req.headers['accept-encoding'] || "";
   var matched = ext.match(config.Compress.match);
   if (matched && acceptEncoding.match(/\bgzip\b/)) {
   res.writeHead(200, "Ok", {'Content-Encoding': 'gzip'});
   raw.pipe(zlib.createGzip()).pipe(res);
   } else if (matched && acceptEncoding.match(/\bdeflate\b/)) {
   res.writeHead(200, "Ok", {'Content-Encoding': 'deflate'});
   raw.pipe(zlib.createDeflate()).pipe(res);
   } else {
   res.writeHead(200, "Ok");
   raw.pipe(res);
   }

//下面是普通的讀取文件的方式,不推薦

//   fs.readFile(realPath, "binary", function(err, data) {
//   if(err) {
//    // file exists, but have some error while read
//    res.writeHead(500, {'Content-Type': 'text/plain'});
//    res.end(err);
//   } else {
//    // file exists, can success return
//    res.writeHead(200, {'Content-Type': contentType});
//    res.write(data, "binary");
//    res.end();
//   }
//   });
  }
  });
 }
 });
});

//監(jiān)聽3000端口

server.listen(PORT, function() {
 console.log("Server is listening on port " + PORT + "!");
});

// 讓socket.io服務器和http服務器共享一個端口

chatServer.listen(server);

  二,服務端利用WebSocket構建聊天室服務端

        為什么采用websocket?

         我們知道現在主流的聊天室還是采用ajax去實現客戶端和服務端的通信。采用的是一種輪詢的機制。所謂輪詢,就是客戶端每隔一段時間就去發(fā)送一次請求,詢問服務端,看看服務端有沒有新的聊天數據,如果有新的數據,就返回給客戶端。 

        Websocket則完全不同。 websocket是基于長鏈接。就是客戶端和服務端一旦建立鏈接之后,這個鏈接就會一直存在。 是一種全雙工的通信。 這個時候的機制有點類似發(fā)布-訂閱模式。 客戶端會訂閱一些事件,一旦服務端有新的數據出現,會主動推送給客戶端。

       websocket采用的是ws協議,不是http協議或者https協議。另外采用websocket的另一個好處就是可以減少很多數據流量。文章開頭,我已經介紹了傳統的一次資源請求過程,需要三次握手協議,而且每次請求頭所占空間比較大,這樣會很費流量。而Websocket里面的互相溝通的Header是很小的-大概只有 2 Bytes。

/**
 * 聊天服務。
 */
var socketio = require('socket.io');
var io;
var guestNumber = 1;   //初始用戶名編號
var nickNames = {};   // 昵稱列表
var namesUsed = [];   //使用過的用戶名
var currentRoom = {};   //當前聊天室
function assignGuestName(socket, guestNumber, nickNames, namesUsed) {
 var name = 'Guest' + guestNumber;
 nickNames[socket.id] = name;
 socket.emit('nameResult', {
 success: true,
 name: name
 });
 namesUsed.push(name);
 return guestNumber + 1;
}
function joinRoom(socket, room) {
 socket.join(room);
 currentRoom[socket.id] = room;
 socket.emit('joinResult', {room: room});
 socket.broadcast.to(room).emit('message', {
 text: nickNames[socket.id] + 'has joined ' + room + '.'
 });
}
function handleMessageBroadcasting(socket) {
 socket.on('message', function(message) {
 socket.broadcast.to(message.room).emit('message', {
  text: nickNames[socket.id] + ':' + message.text
 });
 });
}
exports.listen = function(server) {
 io = socketio.listen(server);
 io.set('log level', 1);
 // 定義每個用戶的連接處理
 io.sockets.on('connection', function(socket) {
 // 分配一個用戶名
 guestNumber = assignGuestName(socket, guestNumber, nickNames, namesUsed);
 // 將用戶加入聊天室Lobby里
 joinRoom(socket, 'Lobby');
 //處理聊天信息
 handleMessageBroadcasting(socket, nickNames);
 //handleNameChangeAttempts(socket, nickNames, namesUsed);
 //handleRoomJoining(socket);
 //handleClientDisconnection(socket, nickNames, namesUsed);
 });
};

三,利用Angular搭建聊天室客戶端

        為什么使用Angular?

         作為一款前端MVC框架,Angular.js無疑是引人注目的。模塊化,雙向數據綁定,指令系統,依賴注入。而且Angular內置jquerylite,這讓熟悉jQuery語法的同學很容易上手。

當然,個人認為, angular在構建一個單頁應用和crud項目方面有很大的優(yōu)勢。 我們這個聊天室就是基于SPA(single page application)的目的。

index.html

<!DOCTYPE html>
<html ng-app="chatApp">
<head>
 <meta name="viewport" content="width=device-width, user-scalable=no">
</head>
<body ng-controller="InitCtrl">
 <div ng-view></div>
 <script src="lib/angular.js"></script>
 <script src="lib/angular-route.js"></script>
 <script src="lib/socket.io.js"></script>
 <script src="app.js"></script>
 <script src="controllers/InitCtrl.js"></script>
</body>
</html>

怎樣構建一個單頁應用?單頁應用的原理?

        先談談單頁應用的原理。所謂單頁,并不是整個頁面無刷新。當你審查一下google chrome的console控制臺的時候,你會發(fā)現,angular內部還是采用了ajax去異步請求資源。所以只是局部刷新。但是這種方式相對于以前的DOM節(jié)點的刪除和修改已經有很大的進步了。

     構建單頁應用,我們需要借助于angular-route.js。這個angular子項目可以幫助我們定義路由和對應的邏輯處理控制器。利用它,我們可以實現一個單頁應用。

app.js 

/**
 * 客戶端(目前只支持瀏覽器,將來會擴展到移動端)程序入口文件
 * 創(chuàng)建一個模塊,并且命名為chatApp
 * 配置路由,實現單頁應用(single page application)
 */
 var chatApp = angular.module("chatApp", ['ngRoute']);
 // 路由配置
 chatApp.config(function($routeProvider) {
 $routeProvider.when('/', {
  templateUrl : 'views/init.html',
  controller: 'InitCtrl'
 })
 .when('/init', {
  templateUrl : 'views/init.html',
  controller: 'InitCtrl'
 });
 });

客戶端聊天界面的代碼邏輯如下

InitCtrl.js

/**
 * # InitCtrl
 */
angular.module('chatApp').controller('InitCtrl', function($scope) {
 var socket = io.connect('http://127.0.0.1:3000');
 socket.on('nameResult', function(result) {
 var message;
 if (result.success) {
  message = 'you are now known as ' + result.name + '.'; 
  console.log('message=', message);
  document.getElementById('guestname').innerHTML = message;
 } else {
  message = result.message;
 }
 });
 socket.on('joinResult', function(result) {
 document.getElementById('room').innerHTML = result.room;
 });
 $scope.sendMessage = function() {
 var message = {
  room: 'Lobby',
  text: document.getElementById('user_input').value
 };
 socket.emit('message', message);
 };
 socket.on('message', function(message) {
 var p = document.createElement('p');
 p.innerHTML = message.text;
 document.getElementById('message').appendChild(p);
 });
});

基于node.js和socket.io搭建多人聊天室

剛學node.js,想著做點東西練練手。網上的東西多而雜,走了不少彎路,花了一天時間在調代碼上。參考網上的一篇文章,重寫了部分代碼,原來的是基于基于node-websocket-server框架的,我沒用框架,單單是socket.io。

一、基本功能

1、用戶隨意輸入一個昵稱即可登錄

2、登錄成功后

1) 對正在登錄用戶來說,羅列所有在線用戶列表,羅列最近的歷史聊天記錄

2) 對已登錄的用戶來說,通知有新用戶進入房間,更新在線用戶列表

3、退出登錄

1)支持直接退出

2) 當有用戶退出,其他所有在線用戶會收到信息,通知又用戶退出房間,同時更新在線用戶列表

4、聊天

1) 聊天就是廣播,把信息廣播給所有連接在線的用戶

5、一些出錯處理

1) 暫時簡單處理了系統邏輯錯誤、網絡出錯等特殊情況的出錯提示

問題:功能不完善,有bug(退出后,新用戶重新登錄,還是原來的用戶) 。抽空完善吧

二、技術介紹

socket.io(官網:http://socket.io/)是一個跨平臺,多種連接方式自動切換,做即時通訊方面的開發(fā)很方便,而且能和expressjs提供的傳統請求方式很好的結合,即可以在同一個域名,同一個端口提供兩種連接方式:request/response, websocket(flashsocket,ajax…)。

這篇文章對socket.io的使用做了詳細介紹:http://chabaoo.cn/article/71361.htm

《用node.js和Websocket做個多人聊天室吧》http://www.html5china.com/HTML5features/WebSocket/20111206_3096.html

三、注意事項

(1)客戶端這樣引用socket.io.js:

<script src="/socket.io/socket.io.js"></script>

可能會加載失?。ㄎ以谶@里耗了不少時間)

可以改為:

<script src="http://ip:port/socket.io/socket.io.js"></script>

(對應服務器的ip地址和端口號,比如說localhost和80端口)

(2)實現廣播的時候,參考官網的寫法,竟然不起作用,如:

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
 socket.broadcast.emit('user connected');
 socket.broadcast.json.send({ a: 'message' });
});

后來看了這個:http://stackoverflow.com/questions/7352164/update-all-clients-using-socket-io

改為以下才起作用:

io.sockets.emit('users_count', clients);

四、效果圖

五、源碼下載

  Nodejs多人聊天室(點擊此處下載源碼)

ps:

1、在命令行運行

node main.js

然后在瀏覽器中打開index.html,如果瀏覽器(ff、Chrome)不支持,請升級到支持WebSocket的版本.

2、推薦node.js的IDE WebStorm

以上內容就是本文基于Angular和Nodejs搭建聊天室及多人聊天室的實現,希望大家喜歡。

相關文章

  • ionic js 復選框 與普通的 HTML 復選框到底有沒區(qū)別

    ionic js 復選框 與普通的 HTML 復選框到底有沒區(qū)別

    本文通過實例給大家演示ionic js 復選框 與普通的 HTML 復選框到底有沒區(qū)別的相關知識,非常不錯具有參考借鑒價值,感興趣的朋友一起學習吧
    2016-06-06
  • bootstrap輸入框組代碼分享

    bootstrap輸入框組代碼分享

    Bootstrap 支持的另一個特性,輸入框組,輸入框組擴展自表單控件.下面小編給大家介紹bootstrap輸入框組的代碼,非常不錯具有參考借鑒價值,感興趣的朋友一起學習吧
    2016-06-06
  • JavaScript實現Flash炫光波動特效

    JavaScript實現Flash炫光波動特效

    JavaScript寫的炫光波動效果,看到一些Flash效果不錯,用JS也模擬一下,還有很多不完善的地方,給各位參考參考。
    2015-05-05
  • JS解密入門 最終變量劫持

    JS解密入門 最終變量劫持

    看到我的前幾篇文章的朋友應該知道,前面的是10進制,直覺解就行了,不過下面有個處理函數,你用10進制解密出來之后還要去分析函數的功能,很不合算。
    2008-06-06
  • 原生js實現日期聯動

    原生js實現日期聯動

    日期聯動算是一個比較常見的功能了,隨便度娘一下,你就能找到N多代碼,今天給大家介紹的是個人比較常用,代碼很簡潔,高效,這里推擠給大家。
    2015-01-01
  • 用js通過url傳參把數據從一個頁面?zhèn)鞯搅硪粋€頁面

    用js通過url傳參把數據從一個頁面?zhèn)鞯搅硪粋€頁面

    如果是傳到新頁面的話,你網站基于什么語言開發(fā)直接用get或者post獲取,然后輸出到這個層
    2014-09-09
  • JavaScript中函數(Function)的apply與call理解

    JavaScript中函數(Function)的apply與call理解

    這篇文章主要介紹了JavaScript中函數(Function)的apply與call理解,本文講解了JavaScript函數調用分為4中模式以及通過apply和call實現擴展和繼承兩方面,需要的朋友可以參考下
    2015-07-07
  • 詳解ES6之用let聲明變量以及l(fā)et loop機制

    詳解ES6之用let聲明變量以及l(fā)et loop機制

    本篇文章主要介紹了詳解ES6之用let聲明變量以及l(fā)et loop機制,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • 利用JS如何計算字符串所占字節(jié)數示例代碼

    利用JS如何計算字符串所占字節(jié)數示例代碼

    因為最近項目有個需求要用js計算一串字符串寫入到localStorage里所占的內存,所以便有了這篇文章,下面這篇文章主要給大家介紹了關于利用JS如何計算字符串所占字節(jié)數的相關資料,需要的朋友可以參考下。
    2017-09-09
  • 微信小程序自定義組件傳值 頁面和組件相互傳數據操作示例

    微信小程序自定義組件傳值 頁面和組件相互傳數據操作示例

    這篇文章主要介紹了微信小程序自定義組件傳值 頁面和組件相互傳數據操作,結合實例形式分析了微信小程序常見傳值操作相關實現技巧,需要的朋友可以參考下
    2019-05-05

最新評論