node tcp服务

TCP(Transmission Control Protocol传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

应用层向TCP层发送用8位字节表示的数据流,TCP把数据流分区成适当的消息段。之后TCP把结果包传送给IP层,继续向后传输。

TCP为了保证不发生丢包,就给每一个包一个序号,同时序号也保证了传送到接收端的包的按序接收。接收端对已成功接收到的包发回一个相应的确认(ACK);如果发送端在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已经丢失将会被重新重传。TCP用一个校验和函数来检验数据是否有误;在发送和接收时都要计算校验和,从而保证了可靠的数据传输。

// server.js
const types = require('./types')
const net = require('net')

const server = net.createServer()

/**
 * @type {Array<net.Socket>}
 */
let users = []

server.on('connection', socket => {

  socket.on('data', data => {
    const msgObj = JSON.parse(data.toString().trim())
    switch (msgObj.type) {
      case types.login:
        // 昵称重复 
        if (users.find(item => item.nickname === msgObj.nickname)) {
          return socket.write(JSON.stringify({
            type: types.login,
            success: false,
            msg: '昵称重复'
          }))
        }
        // 登录成功
        socket.nickname = msgObj.nickname
        users.push(socket)
        socket.write(JSON.stringify({
          type: types.login,
          success: true,
          msg: '登录成功',
          nickname: msgObj.nickname,
          sumUser: users.length
        }))
        // 广播上线通知
        users.forEach(user => {
          if (user !== socket) {
            const logMsg = {
              type: types.log,
              message: `${msgObj.nickname}进入聊天室,当前在线用户${users.length}`
            }
            user.write(JSON.stringify(logMsg))
          }
        })
        break;
      case types.broadcast:
        const broadcastMsg = {
          type: types.broadcast,
          message: msgObj.message,
          nickname: socket.nickname
        }
        users.forEach(item => {
          item.write(JSON.stringify(broadcastMsg))
        })
        break;

      case types.p2p:
        const user = users.find(item => item.nickname == msgObj.nickname)
        if (!user) {
          const p2pMsg = {
            type: types.p2p,
            success: false,
            message: '用户不存在'
          }
          return socket.write(JSON.stringify(p2pMsg))
        }
        const p2pMsg = {
          type: types.p2p,
          success: true,
          message: msgObj.message,
          nickname: socket.nickname
        }
        user.write(JSON.stringify(p2pMsg))
        break;

      default:
        break;
    }
  })

  socket.on('end', () => {
    const userIndex = users.findIndex(item => item.nickname === socket.nickname)
    if (userIndex !== -1) {
      const offLineUser = users[userIndex]
      users.splice(userIndex, 1)
      // 广播离开
      users.forEach(user => {
        const logMsg = {
          type: types.log,
          message: `${offLineUser.nickname}离开聊天室,当前在线用户${users.length}`
        }
        user.write(JSON.stringify(logMsg))
      })
    }
  })
})

server.listen(3000, () => {
  console.log('server is listening...')
})
// client.js
const types = require('./types')
const net = require('net')

// 登录名
let nickname = null

const client = net.createConnection({
  port: 3000,
  host: 'localhost'
})

client.on('connect', () => {
  console.log('连接成功')
  process.stdout.write('请输入昵称:')
  process.stdin.on('data', data => {
    data = data.toString().trim()
    if (!nickname) {
      nickname = data
      const msgObj = {
        type: types.login,
        nickname: data
      }
      return client.write(JSON.stringify(msgObj))
    }

    const matches = /^@(\w+)\s(.+)$/.exec(data)
    // 符合@xxx xxx 私发
    if (matches) {
      // p2p 
      const p2pMsg = {
        type: types.p2p,
        nickname: matches[1],
        message: matches[2]
      }
      return client.write(JSON.stringify(p2pMsg))
    }
    // 群聊
    const broadcastMsg = {
      type: types.broadcast,
      message: data
    }
    client.write(
      JSON.stringify(broadcastMsg)
    )
  })
})

client.on('data', data => {
  data = JSON.parse(data.toString().trim())
  switch (data.type) {
    case types.login:
      if (data.success) {
        console.log(`${data.msg},当前在线人数:${data.sumUser}`)
        nickname = data.nickname
      } else {
        console.log(`登陆失败${data.msg}`)
        nickname = null
        process.stdout.write('请输入昵称:')
      }
      break;

    case types.broadcast:
      console.log(`${data.nickname}:${data.message}`)
      break;

    case types.p2p:
      if (!data.success) {
        // 失败
        return console.log(`发送失败:${data.message}`)
      }
      console.log(`${data.nickname}对你说${data.message}`)
      break;

    case types.log:
      console.log(data.message)
      break;
    default:
      console.log('未知消息类型')
      break;
  }
})
贡献者: huxiguo