Socket 对象

net模块有两个类,一个是Socket和Server,下面先讲解Socket。

net.Socket实现了stream.Duplex双工接口,具有了读写的能力。

创建Socket实例

第一种方法 new net.Socket()
var net = require("net");
var socket = new net.Socket();

创建之后也不能做什么,因为没有连接到服务器端,所以需要socket.connect方法建立个连接。

var net = require("net");
var socket = new net.Socket();
socket.connect(8124,function(){
    console.log(socket.address())
})

socket.connect(port, [host], [connectListener]) 方法是连接服务器的方法,port表示连接到服务器的端口号;host[可选]参数是服务器地址,默认是localhost;connectListener[可选]参数是个'connect'事件监听器,当连接建立后会触发'connect'事件,但当连接建立失败时,会触发‘error’事件。

第二种方法 net.connect / net.createConnection

net.connect(port, [host], [connectListener])#

net.createConnection(port, [host], [connectListener])

这两个方法一模一样,名字不同而已,前面我们已经看过一个net.connect例子了,和第一个方法参数相同,不过多讲解。

第三种方法 被动创建

其实就是服务器端有一个客户端连进来时,会在服务器端创建一个对应的socket(口)。

net.createServer([connectionListener])方法在例子中见过,下面详细讲解。

connectionListener(socket)是“connection”事件监听器,当有客户端连接进来时,会触发“connection”事件,而这个监听器的socket参数就是被动创建的,是对应连接进来的客户端,当然这是可选参数。

connectionListener是可选参数,如果省略了又如何得到socket呢?答案很简单,看下面代码:

var server = net.createServer();
server.on("connection",function(socket){
    // 这个的作用和 [connectionListener]的作用一样。
})

在此一笔带过,在下一节介绍net.Server类时会详细讲解。以上是建立Socket对象的三种方式。

Socket对象的属性

socket.remoteAddress 属性是远程socket的地址。
socket.remotePort 远程socket的端口号。
socket.localAddress 本地socket地址。
socket.localPort 本地socket端口号。
socket.bytesRead 接收到的字节数。

这里的字节量是累积的。

socket.bytesWritten  发送出去的字节数。

这里的字节量是累积的。

虽然这些属性很简单易懂,我们还是为它做个程序。

var net = require('net');
var server = net.createServer();
server.on("connection",function(socket) {
    console.log("远程socket端口:" + socket.remotePort);
    console.log("远程socket地址:" + socket.remoteAddress);
    console.log("本地socket端口:" + socket.localPort);
    console.log("本地socket地址:" + socket.localAddress);
    socket.on("data",function(data){
        console.log(data.toString());
        console.log("接受到字节量:" + socket.bytesRead);
        socket.write("send byte ... ")
        console.log("发送的字节量:"+ socket.bytesWritten);
    })
})

server.listen(8124, function() {
  console.log('服务器已启动!');
});

socket.setEncoding

参看“流”一章,有详细说明。

socket.write(data, [encoding], [callback])

data参数,要发送出去的数据,data可以是字符串类型或Buffer类型。

encoding[可选]参数,编码方式,默认utf8,当data是字符串类型时有效。

callback[可选]参数,写入成功后被调用。

socket.end([data], [encoding]);

如果有个data参数,那么相当于:

socket.write(data,[encoding],function(){
   socket.end();
})

socket.end()就是发出FIN,至于FIN信号,可以理解为告诉另一端socket结束了,然后自己也会结束生命周期,而另一端会触发"end"事件。

socket.destory()

这个方法也是结束生命周期,不过是残酷的被kill了,服务器端不会触发‘end’事件,因为没有发出FIN信号就自杀了,所以另一端会触发"error",本来正常上班,连请假都没有就自杀了,真是可悲。

socket.pause()

这个方法是暂停接受读取数据的动作,也就暂时停止触发socket.on("data"...)事件了。

socket.resume()

恢复读取动作。

【暂停】

这个不是知识点,而是前面说了几个知识点,那又有什么用呢,还是纸上谈兵,应该遵循一个原则,看一些理论,就要动手实践一番。

下面是一个程序,先开发个服务器端和客户端,服务器端接受一个客户的信息,当信息量达到一定bytes数量时就会kill杀了,呵呵。这个game很不错,一起动手开发吧。

服务器代码:

var net = require('net');
var server = net.createServer();
server.on("connection",function(socket) {

    socket.on("data",function(data){
        // 当接受的数据大于1M时,会杀死socket。
        if(socket.bytesRead > 1000*1000){
            socket.pause();
            socket.write("数据量太大,3秒内自动关闭连接!");
            setTimeout(function(){
                socket.destroy();
            },3000);
        }
    })

})

server.listen(8124, function() {
  console.log('服务器已启动!');
});

客户端代码:

var net = require('net');
var fs = require("fs");
// 这个文件很大
var filedata = fs.readFileSync(__dirname+"/1.pdf");

var socket = net.connect(8124,"localhost",
    function() { 
        socket.write(filedata);
    }
);

socket.on('data', function(data) {
  // 服务器发来的信息
  console.log(data.toString());
});

当1.pdf文件大于1M时,客户端会接收到一个警告“数据量太大,3秒内自动关闭连接!”,三秒后真的被踢出来了,郁闷!呵呵。

fuck通过这个程序,可以加深一下编程功力,下面继续纸上谈兵吧。

socket.setTimeout(timeout, [callback]);

这个方法可以设定空闲超时,timeout是毫秒为单位的,如果设置timeout = 0,那么将撤销先前设置的超时,callback是添加个超时监听器,当有'timeout'事件时会被调用,这是可选的,可以使用 socket.on("timeout",callback)方式进行监听。

不要误会,设定这个空闲超时只能产生'timeout'事件,但是不会有类似切断连接行为。

ok,开发个程序,但客户端空闲10秒钟就会给服务器强行断开。代码如下:

服务器代码:

var net = require('net');
var server = net.createServer();
server.on("connection",function(socket) {
    console.log("警告:10秒空闲就会被踢出!")
    socket.setTimeout(10*1000, function(){
       console.log("踢出!!");
       socket.destroy();
    })        
})

server.listen(8124, function() {
  console.log('服务器已启动!');
});

客户端代码:

var net = require("net");
var socket = net.connect(8124);
socket.on("data",function(data){
    console.log(data.toString());
})

socket.address()

返回一个对象,类似 { address: '127.0.0.1', family: 'IPv4', port: 53068 }

socket对象的事件

socket对象会产生一些事件。

data事件

当接收到数据时会产生这个事件,事件监听器会接收到数据。

socket.on("data",function(data){
  // data是一个Buffer对象。
})
end事件

另一端socket调用了end() 方法时,会产生这个事件。

timeout事件

前面已详细说明,就是当socket空闲超时的时候,产生的事件。

drain事件

当调用socket.write方法时,会发送数据,内部有个缓冲区,每次清空缓冲区后都会产生drain事件。

error事件

但有错误产生时,会产生这个事件,一旦产生这个事件,内部也会产生“close”事件。book

socket.on("error",function(err){
   // ......
})
close事件
socket.on("error",function(had_error){

})

had_error是个布尔值,如果是true就表示传输错误造成的关闭。

Server对象是等待有连接进入的管家,当客户端把一个管线丢给服务器,服务器接到后就建立了连接,管线两端都有口,也就是socket,管线是中空的,所以服务器和客户端可以相互发送数据和接收数据。


下面详细介绍Server对象的各个细节。

net.createServer([connectionListener])

创建服务器的方法,connectionListener[可选]是Server对象产生‘connection’事件的监听器。这里可以指定,也可以通过 server.on("connection",connectionListener)指定。

var server = net.createServer();
server.on("connection",function(socket) {

})

server.listen(port, [host], [backlog], [callback])

即使调用net.createServer 建立了Server对象,但还需要调用 server.listen 启动服务器的监听方法。

port 参数,是打开一个服务器端的监听端口,如果是0,表示由系统分配一个随机端口。

host[可选]参数,允许连入的客户端主机,可以是ip或域名。

backlog[可选]参数,表示同时访问的最大数,默认511。

callback[可选]参数,表示server对象产生“listening”事件的监听器,“listening”事件的产生表示server已经准备妥当,要接受客户端请求了。 

server.close([callback])

god这个方法可以保留现有的连接,拒绝新的连接建立。

callback[可选],是监听服务器的“close”事件的监听器,调用server.close不能产生close事件。

close事件的产生有几种情况,第一种情况是当产生error事件时也会产生close事件,第二种情况是调用过server.close方法后,已有的连接都断开连接后,会产生close事件。

server.address()

这个方法很简单,和socket.address()一样。

server.getConnections(callback)

这是异步方法,得到当前连接数,callback(err,count)回调函数,count是当前连接数量。

server对象产生的事件

listening事件

当服务器准备好接受客户端请求时,产生此事件。

connection事件

有新连接进入时,产生此事件。

close事件

god服务器关闭时,会产生此事件。

error事件

当发生错误时产生error事件,同时也会产生close事件。

检查IP有效性

net.isIP(input)

检查input字符串是否是IP,return 0 表示无效,return 4 表示IPv4,return 6表示IPv6。

net.ipIPv4(input)

检查input是否是IPv4 ,return true/false。

net.ipIPv6(input)

检查input是否是IPv6 ,return true/false。

var net = require("net");
console.log(net.isIP("12.444.22.2")); // 0
console.log(net.isIP("112.11.22.122")); // 4
console.log(net.isIP("fe80::dc15:8005:801c:82bb")); // 6
console.log(net.isIPv4("22.212.12.122")); // true
console.log(net.isIPv6("22.212.12.122")); // false
console.log(net.isIPv6("fe80::dc15:8005:801c:82bb")); // true

【小结】

Socket就是一个管道两端的口,一个管道当然要有两个socket,无论关闭任何一端的socket,对应的也就kill了。