Node.js高级编程读书笔记 - 3 网络编程
Outline
- 3.4 构建TCP服务器
- 3.5 构建HTTP服务器
- 3.6 构建TCP客户端
- 3.7 创建HTTP请求
- 3.8 使用UDP
- 3.9 用TLS/SSL保证服务器的安全性
- 3.10 用HTTPS保证HTTP服务器的安全性
3.4 构建TCP服务器
TCP服务器对象通过require("net").createServer()创建,它是一个事件发射器(event emitter),发射事件包括:listening, connection, close, error。
TCP server socket的生命周期这里不再阐述,可以去看APUE、UNP。
a sample: tcp_chat_server.js
var net = require("net");
var server = net.createServer();
var port = 4001;
// keep track of all client connections
var sockets = [];
// event: listening
server.on("listening", function(){
console.log("Server is listening on port", port);
})
// event: client socket connect
server.on("connection", function(socket){
console.log("get a new connection");
sockets.push(socket);
// read data
socket.on("data", function(data){
console.log("get data: ", data.toString());
// multicast the data to all the other clients
sockets.forEach(function(clientSocket){
if(clientSocket !== socket){
clientSocket.write(data);
}
});
});
// handle client connection closed event
socket.on("close", function(){
console.log("connection closed");
var index = sockets.indexOf(socket);
sockets.splice(index, 1);
});
});
// event: server errors
server.on("error", function(error){
console.log("Server error: ", error.message);
});
// event: server closed
server.on("close", function(){
console.log("Server closed");
})
// listen to port
server.listen(port);
socket对象和一些socket选项
server.on("connection", function(socket){...生成的socket对象也是一个事件发射器,其发射事件包括: data, close, error, timeout等。同时socket对象也是可读可写的流对象。
设置socket选项:
(1) timeout
socket.setTimeout(60000, function(){
socket.end("idle timeout, bye");
});
(2) keepAlive
socket.setKeepAlive(true, 10000);// 10s
(3) nodelay
socket.setNoDelay(true);// switch off Nagle algorithm
3.5 构建HTTP服务器
请求信息查看
http_request_information.js
/**
renderer all information of HTTP requests with request body
*/
var http = require("http");
var util = require("util");
var port = 8888;
http.createServer(function(request, response){
var result = "";
result += "url=" + request.url + "\n";
result += "method=" + request.method + "\n";
// inspect object's attributes
result += "headers=" + util.inspect(request.headers) + "\n";
result += "...HEADER END...";
// process request body
request.on("data", function(data){
result += data.toString();
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(result);
response.end("...BODY END...");
});
}).listen(port, function(){
console.log("listening on port: ", port);
});
响应:

一个静态文件访问HTTP服务器
HTTP分块编码允许服务器持续向客户断发送数据,而不需要指定发送数据的大小,即响应头部总是Transfer-Encoding: chuncked,除非指定了Content-Length。
static_file_server.js
/**
demonstration of HTTP Server: providing static file resources
*/
var http = require("http");
var path = require("path");
var fs = require("fs");
var port = 8888;
var server = http.createServer();
// handle HTTP requests
// callback parameters: http.IncomingMessage, http.ServerResponse
server.on("request", function(request, response){
var file = path.normalize("."+request.url);
console.log("request file: " + file);
// generate server error response
function generateServerError(error){
console.log("Error: ", error);
response.writeHead(500);
response.end("Internal Server Error");
}
fs.exists(file, function(exists){
if(exists){
fs.stat(file, function(error, stat){
if(error){
generateServerError(error);
}
if(stat.isDirectory()){
response.writeHead(403);
response.end("Directory Access Forbidden");
} else{
var readStream = fs.createReadStream(file);
readStream.on("error", generateServerError);
response.writeHead(200);
readStream.pipe(response);
}
});
} else{
response.writeHead(404);
response.end("Not Found.");
}
});
});
// binding port
server.listen(port, function(){
console.log("listening on port: ", port);
});
响应:

头部信息:

3.6 构建TCP客户端
TCP客户端对象connection通过require("net").createConnection(<port>, [<host>])创建,它也是一个事件发射器和可读可写流。发射事件包括:connect、error、data、close等。
一些方法:
// 关闭连接
connection.end("Bye", "utf8");
// 向stdout输出服务器响应
connection.pipe(process.stdout, {end: false});
tcp_retry_client.js
/**
demonstration of periodly retried TCP client
*/
var net = require("net");
var port = 4001;
// a flag represent client want to quit
var quitFlag = false;
var connection;
var tryTimeout = 3000; //3s
var hasRetriedTimes = 0;
var maxRetryTimes = 10;
//open current process's stdin, prepare to write to connection
process.stdin.resume();
// listen on previous stdin's 'data' event
process.stdin.on("data", function(data){
if(data.toString().trim().toLowerCase() === "quit"){
quitFlag = true;
console.log("quitting...");
connection.end();//close the connection
} else{
connection.write(data);// write to connection
}
});
// the logic of retry to connection
(function connect(){
function retryConnect(){
if(hasRetriedTimes >= maxRetryTimes){
throw new Error("Has retried "+ maxRetryTimes + " time, giving up.");
}
hasRetriedTimes += 1;
setTimeout(connect, tryTimeout);
}
// create the connection: localhost
connection = net.createConnection(port);
// listen to connection's events
connection.on("connect", function(){
hasRetriedTimes = 0;
console.log("try connect to server");
});
connection.on("error", function(error){
console.log("Error occurs when connect to server: ", error);
});
connection.on("close", function(){
if(!quitFlag){
console.log("connection lost, try to reconnect");
retryConnect();
}
});
// write server response to stdout
connection.pipe(process.stdout, {end: false});
}());//call it immediately
3.7 创建HTTP请求
built-in http.request() and shortcut methods
http_request.js: usage of http.request
/**
demonstration of http.request()
*/
var http = require("http");
var options = {
host: "www.google.com",
port: 80,
method: "POST",
path: "/upload",
headers: {}
};
var request = http.request(options, function(response){
console.log(response.statusCode);// or use uitl.inspect()
console.log(response.httpVersion);
console.log(response.headers);
// 1 parse body as string
// response.setEncoding("utf8");
// response.on("data", function(data){
// console.log(data);
// });
// 2 parse body as stream
var aWriteStream = require("fs").createWriteStream("./local.txt");
response.pipe(aWriteStream);
}).on("error", function(error){
console.log("Error: ", e);
});
// write request body
request.write("data1\n");
request.write("data2\n");
request.end();// must call this
http_get.js: usage of shortcut methods
/**
demonstration of http.get()
*/
var http = require("http");
var options = {
host: "www.google.com",
port: 80,
method: "GET",
path: "/index.html",
headers: {}
};
// http.get(): call response.end() automatically
http.get(options, function(response){
console.log(response.statusCode);// status code
console.log(response.headers);// headers
// access body
response.setEncoding("utf8");
response.on("data", function(data){
console.log("Body="+data);
});
//console.log(response);
}).on("error", function(error){
console.log("Error: ", e);
});
http_agent.js: maintain uderling socket pool
/**
demonstration of http.Agent to maintain socket pool
*/
var http = require("http");
// agent options
var agentOptions = {
maxSockets: 10// override the default 5
}
var options = {
host: "www.google.com",
port: 80,
method: "POST",
path: "/upload",
headers: {},
//agent: false// false means donot use the socket pool
agent: new http.Agent(agentOptions)// define the specific agent
};
var request = http.request(options, function(response){
console.log(response.statusCode);// or use uitl.inspect()
console.log(response.httpVersion);
console.log(response.headers);
response.setEncoding("utf8");
response.on("data", function(data){
console.log(data);
});
}).on("error", function(error){
console.log("Error: ", e);
}).end();
使用request模块
安装
npm install request
http_server.js: the HTTP server used to test with request
/**
a server used to test with `request` module
*/
var port = 4001;
require("http").createServer(function(request, response){
function echo(){
response.writeHead(200, {"Content-Type": "text/plain", "Cookie": "a=4"});
response.end(JSON.stringify({// JSON is a built-in Object
url: request.url,
method: request.method,
headers: request.headers
}));
}
console.log(require('util').inspect(request.headers, { depth: null }));
// dispatch url handlers
switch (request.url) {
case "/redirect":
console.log("incoming[1]: /redirect");
response.writeHead("301", {"Location": "/", "Cookie": "a=1"});
response.end();
break;
case "/print/body":
console.log("incoming[2]: /print/body");
response.writeHead("200", {"Cookie": "a=2"});
request.setEncoding("utf8");
var body = "";
request.on("data", function(data){
body += data;
});
request.on("end", function(){
response.end(JSON.stringify(body));
});
break;
case "/images/peace.jpg":
console.log("incoming[3]: "+request.url);
response.writeHead("200", {"Cookie": "a=3"});
require("fs").createReadStream("./images/peace.jpg").pipe(response);
break;
default:
console.log("incoming[4]: "+request.url);
echo();
break;
}
}).listen(port, function(){
console.log("listening on: "+port);
});
request_simple.js: request module simple usage
/**
demonstration of `request` module simple usage
*/
var request = require("request");
var util = require("util");
//var url = "http://localhost:4001/abc/index.html";
var url = "http://localhost:4001/redirect";
/*
some shortcut method: get, post, put, del
*/
request(url, function(error, response, body){
if(error) throw error;
console.log(util.inspect({
error: error,
response: {
statusCode: response.statusCode
},
body: JSON.parse(body)
}));
});
request_options: request modules' options usage
/**
demonstration of `request` options usage
*/
var request = require("request");
var util = require("util");
var body = {
a: 1,
b: 2
}
var options = {
url: "http://localhost:4001/print/body",
method: "GET",
headers: {
"My-Header": "myHeaderValue"// customed header
},
//form: body // form data usage or using:
json: body // json wrapped request body
};
request(options, function(error, response, body){
if(error) throw error;
console.log(util.inspect({
error: error,
response: {
statusCode: response.statusCode,
headers: response.headers
},
body: JSON.parse(body)
}, { depth: null }));
})
request_stream.js: request module with stream usage
/**
demonstration of stream transfer in `request` module
*/
var fs = require("fs");
var request = require("request");
// 1 download image
var writeStream = fs.createWriteStream("./images/download.jpg");
request.get("http://localhost:4001/images/peace.jpg").pipe(writeStream);
// 2 upload image from download
var source = request.get("http://localhost:4001/images/peace.jpg");
var target = request.post("http://localhost:4001/images/peace.jpg");
source.pipe(target);
request_cookie.js: request with cookie usage(tough-cookie implementation)
/**
deminstartion of `request`'s cookie usage,
REF: https://github.com/request/request, and https://www.npmjs.com/package/tough-cookie
*/
var request = require("request");
var util = require("util");
// 1 use cookie globally, default is false
//request = request.defaults({jar: true});
// 2 use cookie in all requests
var url = "http://localhost:4001/echo";
request(url, function(error, response, body){
if(error) throw error;
//get the cookie from HTTP server
var j = request.jar();
// var cookie = request.cookie('key1=value1');
var cookie = response.headers.cookie;
if(cookie === null){
cookie="key1=value1";
}
j.setCookie(cookie, url);
request = request.defaults({jar:j})
request("http://localhost:4001/print/body");
});
3.8 使用UDP
UDP服务端/客户端对象通过require("dgram").createSocket("udp4")创建,服务端对象需要绑定bind(port[, host])。
udp_server.js
/**
demonstration of a Echo UDP Server
*/
var dgram = require("dgram");
var serverSocket = dgram.createSocket("udp4");
var host = "127.0.0.1";
var port = 4002;
serverSocket.on("message", function(message, peerInfo){
// get peer connection informations: host and port
console.log("get message [%s] from peer: %s,%d", message, peerInfo.address, peerInfo.port);
//echo
serverSocket.send(message, 0, message.length, peerInfo.port, peerInfo.address);
});
// binding to port
serverSocket.bind(port, host);
// listen to 'listening' event
serverSocket.on("listening", function(){
console.log("Listening to port: ", port);
});
udp_client.js
#!/usr/bin/node
/**
demonstration of a TCP client, with command line usage:
$ ./udp_client.js <host> <port>
*/
var dgram = require("dgram");
// read the command line arguments
var host = process.argv[2];//CAUTION real parameter start with index 2
var port = parseInt(process.argv[3], 10);
// create the client udp socket
var clientSocket = dgram.createSocket("udp4");
process.stdin.resume();//prepare to read the stdin
process.stdin.on("data", function(data){
clientSocket.send(data, 0, data.length, port, host);
});
clientSocket.on("message", function(message){
console.log("get message: %s", message.toString());
});
console.log("send data to %s:%d with entring something: ", host, port);
多播
书上说的模糊不清,这里参考了两个文档,基本上可以说明多播的使用:NodeJS UDP Multicast How to、UDP/Datagram Sockets。
udp_multicast_server.js
/**
demonstration of a UDP Server, providing multicast features
*/
var dgram = require("dgram");
var serverSocket = dgram.createSocket("udp4");
var multicastAddress = "230.185.192.108";
var port = 4002;
var destinationPort = 4003;
serverSocket.on("listening", function () {
var address = serverSocket.address();
console.log("server listening " + address.address + ":" + address.port);
});
// https://nodejs.org/api/dgram.html#dgram_udp_datagram_sockets
// always set to async since v0.10
serverSocket.bind(port, function(){
serverSocket.setBroadcast(true);
serverSocket.setMulticastTTL(128);
serverSocket.addMembership(multicastAddress); // set multicast memberships
});
// ideas ref from http://stackoverflow.com/questions/14130560/nodejs-udp-multicast-how-to
setInterval(broadcastNew, 3000);
function broadcastNew() {
var message = new Buffer("Hello, " + Math.random()*10);
serverSocket.send(message, 0, message.length,
destinationPort,//destination port
multicastAddress);
console.log("Sent " + message.toString() + " to the wire...");
//server.close();
}
udp_multicast_client.js
#!/usr/bin/node
/**
demonstration of a TCP client, with command line usage:
$ ./udp_client.js 127.0.0.1 4003
and providing multicast features
*/
var dgram = require("dgram");
var multicastAddress = "230.185.192.108";
// read the command line arguments
var host = process.argv[2];//CAUTION real parameter start with index 2
var port = parseInt(process.argv[3], 10);
// create the client udp socket
var clientSocket = dgram.createSocket("udp4");
clientSocket.on('listening', function () {
var address = clientSocket.address();
console.log('UDP Client listening on ' + address.address + ":" + address.port);
clientSocket.setBroadcast(true)
clientSocket.setMulticastTTL(128);
clientSocket.addMembership(multicastAddress, host);//~
});
clientSocket.bind(port);
clientSocket.on("message", function(message){
console.log("get message: %s", message.toString());
});
3.9 用TLS/SSL保证服务器的安全性
TODO
3.10 用HTTPS保证HTTP服务器的安全性
TODO
Node.js高级编程读书笔记 - 3 网络编程的更多相关文章
- JavaScript、jQuery、HTML5、Node.js实例大全-读书笔记3
技术很多,例子很多,只好慢慢学,慢慢实践!!现在学的这本书是[JavaScript实战----JavaScript.jQuery.HTML5.Node.js实例大全] JavaScript.jQuer ...
- JavaScript、jQuery、HTML5、Node.js实例大全-读书笔记2
技术很多,例子很多,只好慢慢学,慢慢实践!!现在学的这本书是[JavaScript实战----JavaScript.jQuery.HTML5.Node.js实例大全] JavaScript.jQuer ...
- Node.js 开发指南-读书笔记
1. Node.js 使用了单 线程.非阻塞的事件编程模式 Node.js 最大的特点就是采用异步式 I/O 与事件驱动的架构设计.对于高并发的解决方 案,传统的架构是多线程模型,也就是为每个业务逻辑 ...
- JavaScript、jQuery、HTML5、Node.js实例大全-读书笔记1
技术很多,例子很多,只好慢慢学,慢慢实践!!现在学的这本书是[JavaScript实战----JavaScript.jQuery.HTML5.Node.js实例大全] 第 3 章 用 JavaScri ...
- node.js开发指南读书笔记(1)
3.1 开始使用Node.js编程 3.1.1 Hello World 将以下源代码保存到helloworld.js文件中 console.log('Hello World!'); console.l ...
- 读书笔记_python网络编程3(5)
5. 网络数据与网络错误 应该如何准备需要传输的数据? 应该如何对数据进行编码与格式化? Py程序需要提供哪些类型的错误? 5.1. 字节与字符串 PC与网卡都支持将字节作为通用传输单元.字节将8比特 ...
- 读书笔记_python网络编程3_(2)
2.UDP 2.0.数据包表示较短的信息,大小通常不会超过几千字节,在浏览器与服务器进行会话/电子邮件客户端与ISP的邮件服务器进行会话时,这些独立而小型的数据包是如何组成会话的呢? 2.0.1.IP ...
- 读书笔记_python网络编程3_(1)
0.前言 代码目录: https://github.com/brandon-rhodes/fopnp/tree/m/py3 0.1.网络实验环境:理解客户端与服务器是如何通过网络进行通信的 每台机器通 ...
- JavaScript、jQuery、HTML5、Node.js实例大全-读书笔记4
5.2.2 让瀑布流动起来 打好基建之后,就需要写JavaScript代码.首先如果数据不够显示一屏幕的情况,就用新数据来补足它,在补充的时候是根据4列中最矮的那一个为优先补充,因为高矮尺寸一般只有在 ...
随机推荐
- django admin下拉列表不显示值,显示为object的处理
问题:模板中创建form表单中的下拉列表, 前台打开页面显示object,而不是值,如图: 尝试了多种办法无果,最后解决了,处理办法是修改models.py,原来的model: class Techn ...
- iOS-浅谈runtime运行时机制-runtime简单使用(转)
转自http://www.cnblogs.com/guoxiao/p/3583432.html 由于OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtim ...
- C library function - freopen()
Description The C library function FILE *freopen(const char *filename, const char *mode, FILE *strea ...
- 视频播放器之————JW Player参数详解
JW Player参数详解 1,安装 下载后,你可以得到一个例子,当用文本或HTML编辑器打开的时候,你可以发现swf是用一段短小的 javascript嵌入到页面上的.这个Javascript是Ge ...
- iOS Question
Q1: dyld: Library not loaded: @rpath/libswiftCore.dylib 1. 退出 Xcode2. 重启电脑3. 找到 这个 DerivedData 文件夹 删 ...
- CEF使用的几个注意点
CEF为chrome浏览器的切入其他浏览器中的轻量级框架. 开发的客户端的时候,这是作为界面显示的首先,可以增强客户的易变性,可塑性. 在开发的过程中(侧重于C,C++解决),遇到的几个问题,以及自己 ...
- 转-Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案
前言 刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下.有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文件夹说 ...
- 每周一荐:学习ACE一定要看的书
作 者:david++发布时间:2012/06/08 09:02文章地址:http://game-lab.org/?p=320 近两个月都在学习ACE,一个超级强大,也超级复杂的网络框架库.对ACE的 ...
- ReferentialConstraint 中的依赖属性映射到由存储生成的列
ReferentialConstraint 中的依赖属性映射到由存储生成的列 这个问题是由于从表中的外键关系建立错误(可能是由于误改),查看从表的所有外键关系,即可找到问题所在. 问题: 什么是从表? ...
- Objective-C( block的使用)
block block用来保存一段代码 block的标志:^ block跟函数很像:可以保存代码.有返回值.有形参.调用方式跟调用方法一样 block内部可以访问外面的变量 默认情况下,block内部 ...