Socket总结 & node搭建简单的http服务器
2016-08-25 16:30
621 查看
网络中的进程
socket解决的是网络中进程间的通信,其首要解决的就是如何在网络中找到目标进程,这就要求进程拥有唯一性的索引,方便查找连接。一台机器上,进程与进程之间通行,以PID作为唯一标识。但是在网络中,不同的机器,PID可能重复。
然而,网络层的IP 可以唯一标识主机,传输层的 tcp协议和端口号 则能够唯一标识主机中的进程。
因此: ip地址+协议+端口 = 网络中的一个进程
socket
就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说“一切皆socket”。socket位于应用层和传输层之间的一个抽象层,封装传输层的各种复杂操作,提供几个简单的接口供应用层调用。
socket的主要作用是实现在网络中 进程之间的通信。
服务端和客户端 统一使用 socket提供的api进行通信。
socket三次握手 (TCP)
client客户端调用
connect()方法时,即开始进行建立tcp连接(三次握手)中的 第一次握手(阻塞等待服务端的第二次握手)。
server服务端开始调用
accept()方法,完成 第二次握手(阻塞等待客户端的第三次握手)。
client客户端
connect()方法收到第二次握手,进行 第三次握手 ,
connect()方法完成执行。
server服务端
accept()方法收到第三次握手,完成执行。
通信
socket使用的 “ 打开 -> 读写 -> 关闭 ” 这种模式进行的操作。这种模式将socket看成一个特殊的 “文件”,提供一些函数对其进行 “IO操作”。
服务端和客户端通过读取这个中间 “文件” 来进行通信。
具体的I/O操作函数有一下几种方式
read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()
node中的socket编程
node中跟socket相关的网络模块有两个:net
http
net为最底层,以事件驱动机制将socket的接口进行封装,提供socket的相关操作。
http在net的基础上,基于http协议进行再一次封装,将请求分成http头和http体,提供基于request和response的基本操作。
net模块
net模块是node的原生模块,是对tcp层的封装(socket)。提供了两个类的定义:
net.Socket (实现node中的socket操作)
事件: ‘lookup’
事件: ‘connect’
事件: ‘data’
事件: ‘end’
事件: ‘timeout’
事件: ‘drain’
事件: ‘error’
事件: ‘close’
net.Server (基于socket,再次封装成服务端API)
事件: ‘listening’
事件: ‘connection’
事件: ‘close’
事件: ‘error’
提供了一些基于两个类的基本操作
net.createServer([options], [connectionListener])
net.connect(options, [connectionListener])
net.createConnection(options, [connectionListener])
net.connect(port, [host], [connectListener])
net.createConnection(port, [host], [connectListener])
net.connect(path, [connectListener])
net.createConnection(path, [connectListener])
服务端使用
var net = require('net'); var server = net.createServer(function(clientSocket){ ... }) server.listen(10000,'127.0.0.1',function(){ console.log('监听开始') })
客户端使用
var net = require('net'); var clientSocket = net.connect({ port:10000, host:'127.0.0.1' },function(){ console.log('tcp连接建立成功'); }) ...
其中具体的通信使用事件驱动机制。
clientSocket.on('data',function(){ ... }) ...
http模块
http模块在net的基础上,提供针对应用层http协议的专门API。http模块以request和response为基本操作单位,封装net.Socket,将获取到的数据data,分成请求头和请求体。
提供了五个类的定义:
http.Server (基于net.Server的再次封装)
事件: ‘request’
事件: ‘connection’
事件: ‘close’
事件: ‘checkContinue’
事件: ‘connect’
事件: ‘upgrade’
事件: ‘clientError’
http.ServerResponse (基于net.Socket的再次封装)
事件:’close’
http.ClientRequest (基于net.Socket的再次封装)
事件: ‘response’
事件: ‘socket’
事件: ‘connect’
事件: ‘upgrade’
事件: ‘continue’
http.Agent
http.IncomingMessage
服务端使用
var http = require('http'); var server = http.createServer(function(request,response){ ... }) // 或者 var server = new http.Server(); server.on('request',function(request,response){ ... }) server.listen(10000,'127.0.0.1',function(){ console.log('监听开始') })
客户端使用
var http = require('http'); var request = http.request({ host : '127.0.0.1', method : 'GET', path: '/' .. }) request.on('response',function(response){ ... })
url模块
url模块常常配合http模块,对url进行处理。url.parse(string).query | url.parse(string).pathname | | | | | ------ ------------------- http://localhost:8888/start?foo=bar&hello=world --- ----- | | | | querystring(string)["foo"] | | querystring(string)["hello"]
通过url模块,可以创建一个路由机制
var http = require('http'); var url = require('url'); var querystring = require('querystring'); var routeHandle = {}; routeHandle['/'] = function(response){ ... } routeHandle['/upload'] = function(response){ ... } http.createServer(function(request,response){ var pathname = url.parse(request.url).pathname; route(routeHandle,pathname,request,response); }).listen(10000,'127.0.0.1'); function route(handle,pathname,request,response){ if (typeof handle[pathname] === 'function') { handle[pathname](response); } else { console.log("该路由没有处理器: " + pathname); } }
静态文件处理
一个完整的http服务器,除了几个特定的url路径处理之外(即后台action),还应该对静态文件请求做处理。var http = require('http'); var url = require('url'); var path = require('path'); var fs = require('fs'); var MIME = { "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" } ... function route(handle,pathname,request,response){ // path.extname(relPath) 获取文件后缀名 if(path.extname(pathname)){ // 静态文件 处理 doStaticFile(pathname,response); } else{ // action 处理 ... } } function doStaticFile(relPath,response){ fs.exists(relPath,function(exists){ if(!exists){ response.writeHead(404, {'Content-Type': 'text/plain'}); response.write("请求的路径不存在:" + relPath); response.end(); }else{ fs.readFile(relPath,'binary',function(err,file){ if(err){ // 服务器异常 response.writeHead(500, {'Content-Type': 'text/plain'}); response.end(); }else{ // 返回静态文件 var suffix = path.extname(realPath); // 由于extname返回值包含”.”,所以通过slice方法来剔除掉”.” var mime = MIME[suffix.slice(1)] || 'text/plain'; response.writeHead(200, {'Content-Type': mime}); response.write(file, "binary"); response.end(); } }) } }) } http.createServer(function(request,response){ var pathname = url.parse(request.url).pathname; route(routeHandle,pathname,request,response); }).listen(10000,'127.0.0.1');
这里就基本实现了一个http服务器,但在静态文件的处理上还有所欠缺。
对于静态资源的请求,每次都是通过读取文件,当请求过多时,会损坏硬盘IO。
因此需要使用缓存,从而减轻硬盘的IO损坏。
参考博文:
简单理解Socket - Samaritans - 博客园
Socket通信原理和实践 - 张勤一 - 博客频道 - CSDN.NET
深入浅出NodeJS——数据通信,NET模块运行机制 | 小胡子哥的个人网站
net模块中文API
http模块中文API
详解Node.js的http模块之http模块概述 - IT笔录
Http模块 – JavaScript 标准参考教程(alpha)
用NodeJS打造你的静态文件服务器 - CNode技术社区
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- java-模拟tomcat服务器
- Linux socket 初步
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 最后一次说说闭包
- Ajax
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 设计模式---状态模式在web前端中的应用
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法