1.简单介绍下TCP/IP
TCP/IP是互联网相关协议的集合,分为以下四层:应用层、传输层、网络层、数据链路层。
分成四层的好处是,假如只有一层,某个地方需要改变设计时,就必须把所有整体替换掉,而分层之后只需把变动的层替换掉即可。
2. 服务端编程
node提供了net模块来实现tcp编程。主要分为服务端编程和客户端编程两部分,先来写服务端的:
监听客户端连接
先引入net模块,再通过 net.createServer 创建一个TCP服务器,服务器可以监听一个connection事件。
net.createServer([options][, connectionListener])
-
options
allowHalfOpen
表明是否允许半开的 TCP 连接。默认值: false
。pauseOnConnect
表明是否应在传入连接上暂停套接字。默认值: false
。
connectionListener
自动设置为 'connection'
事件的监听器
我们先不传参数:
var net = require('net'); //创建tcp服务器 var server = net.createServer(); //监听connect事件 server.on('connection', (socket) => { socket.on('data',(data)=>{ console.log(data.toString(), 'data') }) }) //设置监听端口 server.listen(8989); //设置监听时的回调函数 server.on('listening', (res)=>{ console.log('in listen...') })
connection事件当有数据发送过来时会被触发。监听函数的参数是一个socket,用户可以使用它与客户端进行交互。
通过socket的data事件可以打印出发送过来的数据,在浏览器中打开 http://localhost:8989,浏览器会将请求头的信息发送到server,所以输出结果如下:
上面代码中,connection事件可以传入到createServer中,listen和listening可以合并,写成下面这种形式:
//第二种写法 var net = require('net'); //创建tcp服务器 var server = net.createServer((socket) => { socket.on('data', (data) => { console.log(data.toString(), 'data..') }) }); //设置监听端口 server.listen(8989, (res) => { console.log('in listen...') });
除了listening和connect事件外,server还有如下事件:
- close:TCP服务器关闭的时候触发,回调函数没有参数
- error:TCP服务器发生错误的时候触发,回调函数的参数为error对象
接收数据和发送数据
通过socket的data事件接收数据,write方法发送数据
socket.write(data[, encoding][, callback])
//创建一个TCP服务器 var server = net.createServer((socket) => { //'connect'事件的回调函数 console.log('客户端已连接'); socket.on('end', () => { console.log('客户端已断开'); }) //接收来自客户端的数据 socket.on('data', (data) => { console.log(data.toString(), 'data'); var readSize = socket.bytesRead; console.log('the size of data is ' + readSize); }) //向客户端写入数据 socket.write('hello\r\n'); //设置连接最大数量,可不设 server.maxConnection = 3; server.getConnections(function (err, count) { console.log('the count of clieent is ' + count); }); }) server.listen(8989, () => { console.log('服务器已启动'); //获取地址信息 var address = server.address(); //获取地址端口 console.log('the port of server is ' + address.port); console.log('the address of server is ' + address.address); console.log('the ip family of server is ' + address.family); })
我们先不写客户端的代码,先用telnet工具来当作客户端来测试上面代码
打开命令行工具输入 telnet localhost 8989
可以看到 socket.write('hello\r\n') 这句话已经生效了,服务端向客户端返回了hello,但这样肯定还不够,
3. 客户端编程
客户端编程比服务端简单,因为不用监听端口。只要把数据从指定的端口发出去就可以了。
net.createConnection(options[, connectListener]) 用来创建一个socket。第一个参数必填,要写发送的端口号,第二个参数是这个socket的 'connect'事件的回调函数
//创建一个客户端 var client = net.connect({ port: 8989 }, () => { console.log('连接到服务器'); //向服务端发送数据 client.write('hello,i am from client'); }) //监听事件,当服务端发送数据过来时会触发该事件 client.on('data', (data) => { console.log(data, 'data'); client.end() }) client.on('end', () => { console.log('已从服务器断开'); });
上面代码的client就是一个socket,所以可以监听data事件来获取服务端发送来的数据。然后socket也可以通过write来向服务端发送数据
4. 一个小的登录系统
利用上面的知识,可以实现一个小型的登录系统:
/*server.js*/ var net = require('net'); //这儿用一个对象来模拟数据库,保存用户名和密码 var user = { admin: '123', test: '333', lucy: '222', } //临时变量保存用户输入的内容 var username = ''; var server = net.createServer((socket)=>{ console.log('服务器已连接'); socket.write('请输入用户名:'); socket.on('data',(data)=>{ //通过stdin输入的内容是buffer,需要转成字符串且清除空格 data = data.toString().trim(); if(!username){ if(user[data]){ socket.write('请输入密码:'); username = data; }else{ socket.write('用户名不对,请输入用户名:'); } }else { if(user[username] === data){ socket.write('登录成功'); }else { socket.write('密码不对,请输入密码:'); } } }); socket.on('close',data=>{ console.log(data); }) }) server.listen(8899,()=>{ console.log('服务器监听8899端口中') })
/*client.js*/ var net = require('net'); process.stdin.resume(); var client = net.createConnection({port: 8899},()=>{ //process.stdin可以获取到用户的输入 process.stdin.on('data',input=>{ client.write(input) }) }) //这儿接收服务端的返回数据 client.on('data',data=>{ console.log(data.toString()); if(data.toString()==='登录成功'){ process.exit(); } })