今天发现要做后端,要了解通信,肯定也要懂TPC啊。瞬间觉得当年没有认真上课真的是悔恨啊。简单了解了一下TCP
TCP/IP
全名为传输控制协议,所谓的协议就是双方进行数据传输的一种格式。在OSI模型中(物理层、数据链结层、网络层、传输层、会话层、表示层、应用层)属于传输层协议。是面向连接的协议。
只有在会话形成之后,服务器端和客户端之间才能互相发送数据。在创建会话的过程中,服务器端和客户端分别提供一个套接字,这两个套接字共同形成了一个连接。服务器端和客户端则通过套接字实现两者之间连接的操作。
TCP/IP协议栈主要分为四层:应用层、传输层、网络层、数据链路层。

那具体下来什么是TCP/IP呢?
- TCP/IP 是供已连接因特网的计算机进行通信的通信协议。
- TCP/IP 指传输控制协议/网际协议 (Transmission Control Protocol / Internet Protocol)。
- TCP/IP 定义了电子设备(比如计算机)如何连入因特网,以及数据如何在它们之间传输的标准。
Node HTTP服务器构建字Node TCP,http.server继承字net.server。
TCP特性
- 面向字节:TCP 对字符及字符编码完全不知。不同编码会导致传输的字节数不同。
- 可靠性:TCP 是基于底层不可靠的服务。TCP 使用确认和超时来实现可靠性要求的。
- 流控制:TCP 用流控制来确保两点之间传输数据的平衡。
- 拥堵控制:TCP 有一种内置的机制能够控制数据包的延迟率及丢包率不会太高。
node中的TCP
net.createServer()是一个EventEmitter实例,他的自定义事件有以下几种:
- listening:在调用 server.listen() 绑定端口是触发。
- connection:每个客户端套接字连接到服务器端时触发。
- close:当服务器关闭时触发。
- error:当侦听到异常的时候触发。
建立连接之后也会有很多连接事件。
- data:当一端调用 write() 发送数据时触发。
- end:当连接中的任意一端发送了 FIN 数据时触发。
- connect:套接字与服务器连接成功时触发。
- drain:当一端调用 write() 发送数据时触发。
- error:当宜昌发生时触发。
- close:当套接字完全关闭时触发。
- timeout:当一段时间连接不再活跃时触发。
聊天室
下面是基于TCP实现了一个简单的网络聊天室,只要在一个网段里,就可以相互发送一些消息。创建文件:chat.js。
// 引入net模块
var net = require('net');
var count = 0, // 用于统计加入聊天室的人数
users = {}; // 统计聊天用户
// 发送消息的方法
function pushMessage(userName, message){
for(var x in users){
if(x !== userName){
users[x].write(message);
}
}
}
// 创建一个net连接
var server = net.createServer(function(connet){
// 当前用户名
var userName;
// 设置字符类型
connet.setEncoding('utf8');
// 统计人数
++count;
// 一些话
connet.write('\033[91m 欢迎来到karyn聊天室 \033[39m \n');
connet.write('\033[91m 目前聊天室中有' + count + '人 \033[39m \n');
connet.write('\033[91m 请输入你的名字: \033[39m');
// 监听输入的数据
connet.on('data', function(data){
// 去掉输入时的回车,这个是mac的回车
data = data.replace('\r\n', '');
// 默认第一次输入的是用户名
if(!userName){
// 用户名验证重复
if(users[data]) {
connet.write('\033[91m 名字和聊天室中有重复,请重新命名 \033[39m \n');
return;
}else{
// 给其他人发送消息
userName = data;
users[data] = connet;
pushMessage(userName, '\033[91m 欢迎' + userName + '加入聊天室 \033[39m \n')
}
}else{
// 给其他人发送消息
pushMessage(userName, '\033[91m ' + userName + ' : \033[39m' + data + '\n');
}
});
// 关闭聊天室
connet.on('close', function(data){
--count;
delete users[userName]
});
});
// 监听 2000 端口
server.listen(2000, function(){
console.log('\033[91m 正在监听2000端口 \033[39m');
});
启动服务,启动服务之后相当于建立了聊天室服务器,服务启动了。
node chat.js
加入聊天室。本质是连接服务器。根据代码提示就可以加入聊天了。
telnet 127.0.0.1 2000
UDP
UDP又称用户数据包协议。同样是属于网络传输层,UDP是面向事务的。无须建立连接,资源消耗低,处理快速且灵活。但是不可靠,偶尔会有一两处丢包。比较适合应用在音频和视频上。
var dgram = require('dgram'),
server = dgram.createSocket('udp4');
server.on('message', function(msg, rinfo){
console.log('server got:' + msg + 'from' + rinfo.address + ':' + rinfo.port);
})
server.on('listening', function(){
var address = server.address();
console.log('server listening ' + address.address + ':' + address.port);
})
server.bind(3333);
node中的UDP
- message:当 UDP 套接字侦听网卡端口后,接受到消息时触发事件。
- listening:当 UDP 套接字开始侦听时触发该事件。
- close:调用 close() 方法时触发该事件,
- error:当监听到异常时触发该事件
HTTP
超文本传输协议。HTTP构建在TCP之上,属于应用层协议。在HTTP的两端是服务器和浏览器,即B/S模式。
一次简单的请求连接
var http = require('http');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World');
}).listen(3333);
通过curl来连接,并分析报文。
// DNS 查询
Rebuilt URL to: http://127.0.0.1:3333/
Hostname was NOT found in DNS cache
// 三次握手
About to connect() to 127.0.0.1 port 3333 (#0)
Trying 127.0.0.1...
connected
Connected to 127.0.0.1 (127.0.0.1) port 3333 (#0)
// 发送请求报文
GET / HTTP/1.1
User-Agent: curl/7.37.1
Host: 127.0.0.1:3333
Accept: */*
// 向客户端发送响应内容,包括响应头和响应体
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Tue, 29 Sep 2015 08:01:37 GMT
Connection: keep-alive
Transfer-Encoding: chunked
//结束会话
Connection #0 to host 127.0.0.1 left intact
node简单的HTTP实例:
var http = require('http');
http.createServer(function(req, res){
res.writeHead(200,{
'Content-Type': 'text/html'
});
res.write('hello');
setTimeout(function(){
res.end('world');
},2000)
}).listen(2000);
上面这个就是一个最简单的请求回溯,监听的端口是2000,启动服务之后,访问127.0.0.1:2000地址就可以触发上面这个方法。req是请求的对象,res是回溯的对象。res.end方法代表完成并回溯数据。上面也有一些特点。首先可以看到,我们在header中标明返回类型。当然还可以声明其他header。在res.write之后接上res.end,直至res.end之后才会发出去回溯。
node中的HTTP服务继承自TCP服务器,采用事件的驱动方式,不为每个连接创建额外的线程和进程。保持低的内存占用,所以实现高并发。
HTTP服务器也是一个EventEmitter实例。
- connection:客户端和服务器端需要建立底层的 TCP 连接,这个连接建立时触发一次。
- request:当请求数据发送到服务器端,在解析出 HTTP 请求头后,将会触发该事件。
- close:当已有连接断开时触发该事件。
- checkContinue:在发送较大数据时并不会直接发送,而是先发送一个头过来。服务器触发该事件,如果非拒绝接受数据则客户端继续发送数据。第一次发送不会触发 request 事件。重新发起请求时才会触发 request 事件。
- connect:当客户端发起 CONNECT 请求时触发。
- upgrade:当客户端要求升级连接的协议时触发。
- clientError:连接的客户端触发 error 事件时触发该事件。
HTTP客户端构建,可以通过http.request(options, connect)发出请求。
var http = require('http');
var options = {
hostname: '127.0.0.1',
port: 3333,
path: '/',
method: 'GET'
},
req = http.request(options, function(res){
console.log('STATUS:' + res.statusCode);
console.log('HEADERs:' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function(chunk){
console.log(chunk);
})
})
req.end();
options 的参数项
host:服务器的域名或 IP 地址。
hostname:服务器名称。
port:服务器端口号。
localAddress: 建立网络连接的本地网卡。
socketPath:套接字的路径。
method:HTTP 请求方法。
path:请求路径。
headers:请求头对象。
auth:Basic 认证。
HTTP 客户端对应着一些事件:
response:与服务器端 request 事件对应的客户端在请求发出后得到相应时触发。
socket:当底层连接池中建立的连接分配给当前请求对象时触发。
connect:当服务器端回溯为 200 时触发。
upgrade:客户端向服务器端发起 upgrade 请求时触发。
continue:试图发送较大数据量时触发。
RESTful类web服务
- PUT 表示新建一个资源
- POST 表示更新一个资源
- GET 表示查看一个资源
- DELETE 表示删除一个资源
查询字符串
下面这个一种将url的字符串变为query json对象的方法。但是这种方法有一种缺陷,会丢弃hash后面所有的参数
var url = require('url'),
querystring = require('querystring'),
query = querystring.parse(url.parse('your url').query);
console.log(query);
// 写了个简单的获取 url 和 hash 的方法,即使是 hash 后面的 query 词,也会获取出来
function getQueryAndHash(url){
var query = {},
hash = [],
querys = url.split('?');
querys.map(function(item, index, array){
var hashs = item.split('#');
if(hashs.length > 1){
var _hashs = hashs.slice(1);
_hashs.map(function(_item, _index, _array){
if(_item){
hash.push(_item);
}
})
}
hashs[0].split('&').map(function(_item, _index, _array){
var _query = _item.split('=');
if(_query[1]){
if(!query[_query[0]]){
query[_query[0]] = _query[1];
}else{
var _data = query[_query[0]];
if(typeof(_data) === 'string'){
query[_query[0]] = [_data, _query[1]];
}else{
query[_query[0]].push(_query[1])
}
}
}
});
});
return {
hash: hash,
query: query
}
}