1. Node를 사용하여 정적 서버 구축
이것이 이 프로젝트의 기본 지원 부분입니다. html, css, gif, jpg, png, javascript, json, 일반 텍스트 등과 같은 정적 리소스 파일에 대한 액세스를 지원하는 데 사용됩니다. 여기에 MIME 유형 파일 매핑이 있습니다.
mime.js
/** * mime类型的 map * @ author Cheng Liufeng * @ date 2014/8/30 * 当请求静态服务器文件的类型 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을 입력하고 페이지가 나타나기까지의 과정을 설명하겠습니다. 사용자가 브라우저 주소 표시줄에 URL을 입력하는 경우.
다음에는 일련의 과정이 진행됩니다. 첫 번째는 도메인 이름을 해당 IP 주소로 변환하는 DNS 확인입니다. 그런 다음 브라우저와 원격 웹 서버는 TCP 3방향 핸드셰이크 협상을 통해 TCP/IP 연결을 설정합니다. 핸드셰이크에는 동기화 메시지, 동기화-응답 메시지 및 응답 메시지가 포함됩니다. 이 세 가지 메시지는 브라우저와 서버 간에 전달됩니다. 이 핸드셰이크에서 클라이언트는 먼저 통신 설정을 시도한 다음 서버가 클라이언트의 요청에 응답하고 수락하고 마지막으로 클라이언트가 요청이 수락되었다는 메시지를 보냅니다. TCP/IP 연결이 설정되면 브라우저는 연결을 통해 원격 서버에 HTTP GET 요청을 보냅니다.
원격 서버는 리소스를 찾아 HTTP 응답을 사용하여 반환합니다. HTTP 응답 상태 200은 올바른 응답을 나타냅니다. 이때 웹 서버는 리소스 서비스를 제공하고 클라이언트는 리소스 다운로드를 시작합니다. 다운로드되는 리소스에는 html 파일, css 파일, javascript 파일, 이미지 파일이 포함됩니다. 그런 다음 렌더링 트리와 DOM 트리 구축을 시작합니다. 그 동안 CSS 차단과 js 차단이 발생합니다. 따라서 맨 아래 계층에는 정적 서버 지원이 필요합니다. 여기서는 Express 프레임워크를 사용하지 않고 기본적으로 정적 서버를 구성합니다.
실제로 각 리소스 파일 요청 프로세스는 GET 요청입니다. 다음으로 클라이언트의 GET 요청(브라우저 측 또는 Linux에서는 컬 방식 사용)에 대응하는 서버 측 처리 과정을 설명하겠습니다. Get 요청이 서버로 전송된 후, 서버는 GET 요청에 따라 리소스 파일의 경로에 대응할 수 있습니다. 이 경로를 알고 나면 파일 읽기 및 쓰기를 사용하여 지정된 경로에서 리소스를 얻은 다음 클라이언트에 반환할 수 있습니다.
Node의 파일 읽기 및 쓰기 API에는 readFile 및 readFileSync가 포함되어 있지만 스트림을 사용하여 파일을 읽는 더 좋은 방법은 캐싱 및 gzip 압축을 사용할 수 있다는 것입니다.
그럼 캐싱을 어떻게 구현할까요? 일반적으로 클라이언트가 처음으로 요청을 하면 서버는 리소스 파일을 읽고 이를 클라이언트에 반환합니다. 하지만 동일한 파일을 두 번째로 요청할 때는 여전히 서버에 요청을 보내야 합니다. 서버는 Expires, 캐시 제어, If-Modified-Since 및 기타 HTTP 헤더 정보를 기반으로 이 리소스가 캐시되었는지 여부를 결정합니다. 캐시가 있으면 서버는 리소스 파일의 실제 경로에 다시 액세스하지 않습니다. 캐시된 리소스를 직접 반환합니다.
server.js
/** * 聊天室服务端 * 功能:实现了Node版的静态服务器 * 实现了缓存,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(); // } // }); } }); } }); });
//포트 3000 수신
server.listen(PORT, function() { console.log("Server is listening on port " + PORT + "!"); });
// 소켓.io 서버와 http 서버가 포트를 공유하도록 합니다
chatServer.listen(server);
둘째, 서버는 WebSocket을 사용해 채팅방 서버를 구축합니다
웹소켓을 사용하는 이유는 무엇인가요?
우리는 주류 채팅방이 클라이언트와 서버 간의 통신을 구현하기 위해 여전히 Ajax를 사용하고 있다는 것을 알고 있습니다. 폴링 메커니즘이 사용됩니다. 소위 폴링이란 클라이언트가 서버에 새로운 채팅 데이터가 있는지 확인하기 위해 가끔씩 요청을 보내는 것을 의미합니다. 새로운 데이터가 있으면 클라이언트에 반환됩니다.
웹소켓은 완전히 다릅니다. Websocket은 긴 링크를 기반으로 합니다. 즉, 클라이언트와 서버 사이에 링크가 설정되면 해당 링크는 항상 존재합니다. 전이중 통신입니다. 이때의 메커니즘은 게시-구독 모델과 다소 유사합니다. 클라이언트는 일부 이벤트를 구독하고 서버에 새 데이터가 나타나면 클라이언트에 적극적으로 푸시됩니다.
Websocket은 http 프로토콜이나 https 프로토콜이 아닌 ws 프로토콜을 사용합니다. 웹소켓을 사용하는 또 다른 이점은 많은 데이터 트래픽을 줄일 수 있다는 것입니다. 기사 시작 부분에서 3방향 핸드셰이크 프로토콜이 필요한 전통적인 일회성 리소스 요청 프로세스를 소개했으며, 각 요청 헤더는 상대적으로 큰 공간을 차지하므로 많은 트래픽을 소비합니다. Websocket에서 서로 통신하는 데 사용되는 헤더는 매우 작습니다(약 2바이트).
/** * 聊天服务。 */ 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项目方面有很大的优势。 我们这个聊天室就是基于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控制台的时候,你会发现,angular内部还是采用了ajax去异步请求资源。所以只是局部刷新。但是这种方式相对于以前的DOM节点的删除和修改已经有很大的进步了。
构建单页应用,我们需要借助于angular-route.js。这个angular子项目可以帮助我们定义路由和对应的逻辑处理控制器。利用它,我们可以实现一个单页应用。
app.js
/** * 客户端(目前只支持浏览器,将来会扩展到移动端)程序入口文件 * 创建一个模块,并且命名为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/)是一个跨平台,多种连接方式自动切换,做即时通讯方面的开发很方便,而且能和expressjs提供的传统请求方式很好的结合,即可以在同一个域名,同一个端口提供两种连接方式:request/response, websocket(flashsocket,ajax…)。
这篇文章对socket.io的使用做了详细介绍:http://www.jb51.net/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);
四、效果图
五、源码下载
ps:
1、在命令行运行
node main.js
然后在浏览器中打开index.html,如果浏览器(ff、Chrome)不支持,请升级到支持WebSocket的版本.
2、推荐node.js的IDE WebStorm
以上内容就是本文基于Angular和Nodejs搭建聊天室及多人聊天室的实现,希望大家喜欢。

JavaScript는 웹 사이트, 모바일 응용 프로그램, 데스크탑 응용 프로그램 및 서버 측 프로그래밍에서 널리 사용됩니다. 1) 웹 사이트 개발에서 JavaScript는 HTML 및 CSS와 함께 DOM을 운영하여 동적 효과를 달성하고 jQuery 및 React와 같은 프레임 워크를 지원합니다. 2) 반응 및 이온 성을 통해 JavaScript는 크로스 플랫폼 모바일 애플리케이션을 개발하는 데 사용됩니다. 3) 전자 프레임 워크를 사용하면 JavaScript가 데스크탑 애플리케이션을 구축 할 수 있습니다. 4) node.js는 JavaScript가 서버 측에서 실행되도록하고 동시 요청이 높은 높은 요청을 지원합니다.

Python은 데이터 과학 및 자동화에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 데이터 처리 및 모델링을 위해 Numpy 및 Pandas와 같은 라이브러리를 사용하여 데이터 과학 및 기계 학습에서 잘 수행됩니다. 2. 파이썬은 간결하고 자동화 및 스크립팅이 효율적입니다. 3. JavaScript는 프론트 엔드 개발에 없어서는 안될 것이며 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축하는 데 사용됩니다. 4. JavaScript는 Node.js를 통해 백엔드 개발에 역할을하며 전체 스택 개발을 지원합니다.

C와 C는 주로 통역사와 JIT 컴파일러를 구현하는 데 사용되는 JavaScript 엔진에서 중요한 역할을합니다. 1) C는 JavaScript 소스 코드를 구문 분석하고 추상 구문 트리를 생성하는 데 사용됩니다. 2) C는 바이트 코드 생성 및 실행을 담당합니다. 3) C는 JIT 컴파일러를 구현하고 런타임에 핫스팟 코드를 최적화하고 컴파일하며 JavaScript의 실행 효율을 크게 향상시킵니다.

실제 세계에서 JavaScript의 응용 프로그램에는 프론트 엔드 및 백엔드 개발이 포함됩니다. 1) DOM 운영 및 이벤트 처리와 관련된 TODO 목록 응용 프로그램을 구축하여 프론트 엔드 애플리케이션을 표시합니다. 2) Node.js를 통해 RESTFULAPI를 구축하고 Express를 통해 백엔드 응용 프로그램을 시연하십시오.

웹 개발에서 JavaScript의 주요 용도에는 클라이언트 상호 작용, 양식 검증 및 비동기 통신이 포함됩니다. 1) DOM 운영을 통한 동적 컨텐츠 업데이트 및 사용자 상호 작용; 2) 사용자가 사용자 경험을 향상시키기 위해 데이터를 제출하기 전에 클라이언트 확인이 수행됩니다. 3) 서버와의 진실한 통신은 Ajax 기술을 통해 달성됩니다.

보다 효율적인 코드를 작성하고 성능 병목 현상 및 최적화 전략을 이해하는 데 도움이되기 때문에 JavaScript 엔진이 내부적으로 작동하는 방식을 이해하는 것은 개발자에게 중요합니다. 1) 엔진의 워크 플로에는 구문 분석, 컴파일 및 실행; 2) 실행 프로세스 중에 엔진은 인라인 캐시 및 숨겨진 클래스와 같은 동적 최적화를 수행합니다. 3) 모범 사례에는 글로벌 변수를 피하고 루프 최적화, Const 및 Lets 사용 및 과도한 폐쇄 사용을 피하는 것이 포함됩니다.

Python은 부드러운 학습 곡선과 간결한 구문으로 초보자에게 더 적합합니다. JavaScript는 가파른 학습 곡선과 유연한 구문으로 프론트 엔드 개발에 적합합니다. 1. Python Syntax는 직관적이며 데이터 과학 및 백엔드 개발에 적합합니다. 2. JavaScript는 유연하며 프론트 엔드 및 서버 측 프로그래밍에서 널리 사용됩니다.

Python과 JavaScript는 커뮤니티, 라이브러리 및 리소스 측면에서 고유 한 장점과 단점이 있습니다. 1) Python 커뮤니티는 친절하고 초보자에게 적합하지만 프론트 엔드 개발 리소스는 JavaScript만큼 풍부하지 않습니다. 2) Python은 데이터 과학 및 기계 학습 라이브러리에서 강력하며 JavaScript는 프론트 엔드 개발 라이브러리 및 프레임 워크에서 더 좋습니다. 3) 둘 다 풍부한 학습 리소스를 가지고 있지만 Python은 공식 문서로 시작하는 데 적합하지만 JavaScript는 MDNWebDocs에서 더 좋습니다. 선택은 프로젝트 요구와 개인적인 이익을 기반으로해야합니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

ZendStudio 13.5.1 맥
강력한 PHP 통합 개발 환경

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기
