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 algorithm3.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 immediately3.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 thishttp_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 requesthttp_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列中最矮的那一个为优先补充,因为高矮尺寸一般只有在 ... 
随机推荐
- 微信 xml 转 Map
			String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; xml + ... 
- JMeter进行简单的数据库(mysql)压力测试
			1.点击测试计划,再点击“浏览”,把JDBC驱动添加进来: 注:JDBC驱动一般的位置在java的安装地址下,路径类似于: \java\jre\lib\ext 文件为:mysql-connect ... 
- 一步一步学习underscore的封装和扩展方式
			前言 underscore虽然有点过时,这些年要慢慢被Lodash给淘汰或合并. 但通过看它的源码,还是能学到一个库的封装和扩展方式. 第一步,不污染全局环境. ES5中的JS作用域是函数作用域. 函 ... 
- PHP 小方法之 二维数组排序
			if (! function_exists ( 'multi_array_sort' )) { function multi_array_sort($arr, $keys, $type = 'desc ... 
- Deep Learning 1_深度学习UFLDL教程:Sparse Autoencoder练习(斯坦福大学深度学习教程)
			1前言 本人写技术博客的目的,其实是感觉好多东西,很长一段时间不动就会忘记了,为了加深学习记忆以及方便以后可能忘记后能很快回忆起自己曾经学过的东西. 首先,在网上找了一些资料,看见介绍说UFLDL很不 ... 
- Easy UI
			首先去Easy UI官网下载离线包 导入要用的js模块 <!DOCTYPE html> <html> <head lang="en"> < ... 
- python中lambda表达式应用
			对于简单的函数,也存在一种简便的表示方式,即:lambda表达式 #普通函数1 def func(a): return a+1 print 'test1_func0:',func(1000)4#lam ... 
- Linux下安装国际版QQ (转)
			原文链接:http://www.linuxidc.com/Linux/2016-09/134923.htm 说明:一开始,我在Ubuntu 16.04下安装的QQ版本是Wineqq2013SP6-20 ... 
- Oracle创建定时器
			--创建日志信息表(测试表) create table T_LOG( id number, datetime DATE); --插入测试数据insert into t_log values (1,'2 ... 
- Java—类的封装、继承与多态
			一.类和对象 1.类 类是数据以及对数据的一组操作的封装体. 类声明的格式: 类声明 { 成员变量的声明: 成员方法的声明及实现: } 1.1 声明类 [修饰符] class 类<泛型> ... 
