1.Server.js
const http = require("http");
const PORT = 8080;
const HOST = "localhost";
const WAIT_TIME =15;
//REF https://www.hacksparrow.com/express-js-https-server-client-example.html
//REF http://www.jianshu.com/p/ab2741f78858
/*
[A] HTTP module function
①http.METHODS
②http.STATUS_CODE
③http.createServer([requestListener]) //创建一个sever
//return http.Server
④http.get(options[,callback)//设置method为get,且自动调用req.end()
//return http.clientRequest
⑤http.request(options[,callback])//创建一个client
//return http.clientRequest 是可写流
var options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
[B] HTTP module class[http.ClientRequest | ]
①http.ClientRequest[Writable Stream interface + EventEmitter]
-This object is created internally and returned from http.request().
-add "responde" event on the request object
//当收到响应头部的时候将会触发"response"事件,
//响应时间有个http.IncomingMessage参数
//针对"response"事件,可以listen for "data" event
//如果添加了"response"事件,必须消费响应对象的data,直到数据处理完成才会触发"end"事件
-"response" event[http.IncomingMessage] 当请求的response被接收时触发。 该事件只触发一次。
-req.write(chunk[,encoding][,callback]) //可以以Stream的形式多次发送body(可以设置header ['Transfer-Encoding', 'chunked'] )
//return request
-req.end([data][,encoding][,callback]) //相当于调用wirte发送数据后再end
-"abort" event 当请求已被客户端中止时触发。 该事件仅在首次调用 abort() 时触发。
-"aborted" event 当请求已被服务器中止且网络 socket 已关闭时触发。
-"connect" event 通过代理http访问数据[response(http.IncomingMessage),socket(net.socket),head(Buffer)]每当服务器响应一个带有 CONNECT 方法的请求时触发。 如果该事件未被监听,则接收到 CONNECT 方法的客户端会关闭它们的连接。
-"continue" event 当服务器发送了一个 100 Continue 的 HTTP 响应时触发,通常因为该请求包含 Expect: 100-continue。 这是客户端应发送request body的指令
-"socket" event[net.Socket] 当 socket 被分配给request后触发。
-"upgrade" event[response(http.IncomingMessage),socket(net.socket),head(Buffer)] 每当服务器响应一个upgrade请求时触发。 如果该事件未被监听,则接收到升级请求头的客户端会关闭它们的连接。
-req.abort()
-req.setTimeout(timeout[,callback]) //设置request超时
-req.setSocketKeepAlive([enable][,initDelay] //设置request保持长连接
-req.setNoDelay([noDelay]) //设置即可发送数据,不缓存
②http.ClientResponse[Writable Stream interface[自己实现] + EventEmitter]
////作为http.Server()request事件的第二个参数
-This object is created internally by an HTTP server--not by the user.
-"close" event 表明在 response.end() 被调用或能够刷新之前,底层连接被终止了。
-"finish" event 当响应头和主体的最后一部分已被交给操作系统通过网络进行传输时,触发该事件。 这并不意味着客户端已接收到任何东西。
//这个事件被触发后,响应对象上不再触发其他任何事件
res.addTrailers(headers) //消息发送后添加头信息,Trailers will only be emitted if chunked encoding is used for the response
res.writeHead(200, { 'Content-Type': 'text/plain','Trailer': 'Content-MD5' });
res.write(fileData);
res.addTrailers({'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667'});
res.end();
-res.writeHead(statusCode[,statusMessage][,headers) //发送response header to the request.
该方法在消息中只能被调用一次,且必须在 response.end() 被调用之前调用。
如果在调用该方法之前调用 response.write() 或 response.end(),则隐式或可变的消息头会被计算并调用该函数。
当消息头已使用 response.setHeader() 设置,它们会被与其他消息头合并传给 response.writeHead(),带消息头的 response.writeHead() 有更高优先级。
var body = 'hello world';
response.writeHead(200, {'Content-Length': Buffer.byteLength(body),
'Content-Type': 'text/plain' });
-res.write(chunk[,encoding][,callback]) This sends a chunk of the response body可以被多次调用
-res.end([data][,encoding][,callback]) 对于每个响应,response.end() 方法必须被调用。告诉服务器res被发送完成
-res.finished //boolean代表res是否被发送完成
-res.getHeader("name") //获取指定name的头内容
var contentType = response.getHeader('content-type');
-res.headersSent //boolean如果消息头被发送了就是true
-res.removeHeader("name")//从隐式发送中移除一个头
-res.sendDate 当为true时会自动添加日期消息头
-res.setHeader("name","value")设置响应的消息头
response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
-res.setTimeout(msecs,callback) 设置socket的超时时间
-res.statusCode 返回给客户端的状态码
-res.statusMessage 返回给客户端的状态信息
③http.IncomingMessage[可读流接口,可由http.Server或http.ClientRequest创建,作为第一个参数传给request或response事件]
//作为http.Server()request事件的第一个参数
-"data" event 请求体数据到来时被触发。提供一个chunk参数,表示接收的数据
-"end" event 请求体数据传送完毕时被触发,此后不会再有数据了
-"close" event用户请求结束时触发,用户强制终止传输也是用close不是end事件,表明底层连接被关闭,每次响应只发生一次
-"aborted" event请求被客户端终止且socket关闭时触发
-msg.headers
-msg.httpVersion
-msg.method
-msg.url
-msg.statusCode
-msg.rawHeaders
-msg.rawTrailers
-msg.setTimeout(msecs,callback)
-msg.statusMessage
-msg.socket
-msg.trailers
④http.Server[继承net.Server,额外添加了一些事件]
-"close" event 服务器关闭时触发
-"connection" event [socket]一个新的TCP流被创建时触发
-"request" event [req(msg),res(res)]每次接到一个请求时触发
-"connect" event[req(msg),socket(net.Scoket),head(Buffer)] //客户端请求CONNECT方法是被触发
-"upgrade" event[req(msg),socket(net.Scoket),head(Buffer)] //客户端请求UPGRADE时被触发
-server.close([callback]) //停止服务端接收服务
-server.listen([port][,hostname][,backlog][,callback]) //绑定监听端口
-server.listening //boolean 服务器是否在监听
-server.maxHeadersCount //最大头数量
-server.setTimeout(msecs,calllback) //设置超时,默认是两分钟
-server.timeout //默认是两分钟
*/
//listener for "request" event
//req is http.IncomingMessage[readableStream]
//res is http.clientResponse[writableStream]
//return http.Server
const server = http.createServer((req,res)=>{
//console.log 基础信息
console.log(`\nreq.headers:${JSON.stringify(req.headers)}`);
console.log(`req.url:${req.url}`);
//console.log(`req.statusCode:${req.statusCode}`);//valid for response
console.log(`req.method:${req.method}`);
console.log(`req.httpVersion:${req.httpVersion}`);
let reqData = "";
//msg有data,end,close事件
req.on("data",(chunk)=>{
reqData +=chunk;
});
req.on("end",()=>{
console.log(`${new Date().getTime()} The request sends data completed`);
console.log(`${new Date().getTime()} The reqData:${reqData}`);
//准备回传数据给client
let resBody= {
"name":"www.baidu.com",
"age":25,
"scholl":"Sichuan University",
"city":"America",
"title":"Senior SoftEngineer",
"tax":"090-33546"
};
//设置响应头,优先级高于writeHead
res.writeHead(200,{
"Content-Type":"application/json",
"Set-Cookie":["account=ryan","password=A123"],
"Conten-Length":Buffer.byteLength(JSON.stringify(resBody))
});
//回传数据
res.write(JSON.stringify(resBody));
// res.write(JSON.stringify(resBody));
//每个响应必须调用res.end()
console.log(`${new Date().getTime()} Prepared to exec res.end()`);
//如果这句话被屏蔽,15s后将会自动触发req监听的close事件
res.end();
});
req.on("close",()=>{
console.log(`${new Date().getTime()} IncomingMessage close`);
});
});
//listener for "error"
server.on("error",(err)=>{
if(err.code =="EADDRINUSE"){
console.log(`Errors:${err.message}`);
setTimeout(()=>{
//触发server的close事件,但是直到所有连接结束,才会关闭服务。
server.close();
// grab a random port
server.listen(()=>{
console.log("opened server on ",server.address());
});
})
}else{
console.log(err.message);
}
},10000);
//listener for "listening"
server.listen(PORT,HOST,()=>{
//查看服务器是否在监听
console.log(`Server.listening:${server.listening}`);
//查看服务器的监听port,host,family
console.log("Opened server on ",server.address());
//设置服务器的最大连接数
server.maxConnections = 4;
//设置服务器的最大header数量
server.maxHeadersCount = 10;
//查看最大头数量
console.log(`Server.maxHeadersCount:${server.maxHeadersCount}`);
//设置timeout时间
server.timeout = 1000*WAIT_TIME;
});
//listener for "close"
server.on("close",()=>{
console.log("Server has closed.");
})
2.Client.js
const http = require("http");
const querystring = require("querystring");
const WAIT_TIME =2;
//将一个对象序列化为查询字符串"msg=Hello Server From Http Client"
var postData1 = querystring.stringify({
"msg":"Hello Server From Http Client"
});
//请求中的第一个参数
var options = {
hostname:"localhost",
port:8080,
path:"/",
method:"POST",
headers:{
"content-Type":"application/json",
"content-Length":Buffer.byteLength(postData1)
}
};
//http.request() returns an instance of the http.ClientRequest[Writable]
//listener for the 'response' event as a one time
//callback arguments http.IncomingMessage
const req =http.request(options,(res)=>{
console.log(`STATUS:${res.statusCode}`);
console.log(`HEADERS:${JSON.stringify(res.headers)}`);
var resData="";
//msg有data,end,close事件
res.on("data",(chunk)=>{
resData +=chunk;
});
res.on("end",()=>{
console.log("The request receive data completed");
console.log(`${new Date().getTime()} The resData:${resData}`);
});
res.on("close",()=>{
console.log(`${new Date().getTime()} IncomingMessage close`);
});
});
//write data to request body
//The ClientRequest instance is a writable stream.
// If one needs to upload a file with a POST request,
// then write to the ClientRequest object.
req.write(postData1);
req.end();
//设置响应超时时间
req.setTimeout(1000*WAIT_TIME,()=>{
console.log(`${new Date().getTime()} Server beyond ${WAIT_TIME}s to return data`);
});
//监听请求的错误
req.on("error",(err)=>{
console.log(`Errors:${err.message}`);
});
3.正常的执行结果
[Server.js]
Server.listening:true
Opened server on { address: '127.0.0.1', family: 'IPv4', port: 8080 }
Server.maxHeadersCount:10
req.headers:{"content-type":"application/json","content-length":"41","host":"localhost:8080","connection":"close"}
req.url:/
req.method:POST
req.httpVersion:1.1
1489919108333 The request sends data completed
1489919108334 The reqData:msg=Hello%20Server%20From%20Http%20Client
1489919108339 Prepared to exec res.end()
[Client.js]
STATUS:200
HEADERS:{"content-type":"application/json","set-cookie":["account=ryan","password=A123"],"conten-length":"128","date":"Sun, 19 Mar 2017 10:25:08 GMT","connection":"close","transfer-encoding":"chunked"}
The request receive data completed
1489919108349 The resData:{"name":"www.baidu.com","age":25,"scholl":"Sichuan University","city":"America","title":"Senior SoftEngineer","tax":"090-33546"}
4.断点在Server.js的177行res.end(),观察Client.js中是否触发超时
[Server.js]
Server.listening:true
Opened server on { address: '127.0.0.1', family: 'IPv4', port: 8080 }
Server.maxHeadersCount:10
req.headers:{"content-type":"application/json","content-length":"41","host":"localhost:8080","connection":"close"}
req.url:/
req.method:POST
req.httpVersion:1.1
1489918606073 The request sends data completed
1489918606073 The reqData:msg=Hello%20Server%20From%20Http%20Client
1489918606079 Prepared to exec res.end()
[Client.js]
STATUS:200
HEADERS:{"content-type":"application/json","set-cookie":["account=ryan","password=A123"],"conten-length":"128","date":"Sun, 19 Mar 2017 10:23:49 GMT","connection":"close","transfer-encoding":"chunked"}
1489919031123 Server beyond 2s to return data