Socket.io 404 错误 - 客户端未连接

Socket.io 404 Error - Client not connecting

将在我的本地环境中运行良好的 Twitter 流应用程序部署到实时网络服务器时,我收到与 socket.io 相关的 404 错误。我看到这里已经有很多类似的问题,但没有找到解决我面临的问题的答案。

问题描述:

  1. 一旦我启动 server.js,它就会正确地将 'Listening on Port 3000' 记录到控制台(终端)
  2. 但是,消息“客户端已连接...”没有被触发
  3. 在浏览器控制台中,我看到错误消息“GET https://exampledomain.com/socket.io/?EIO................(etc) - 404 - XHR 加载失败”

我已经尝试了以下更改但没有成功,其中包括:

  1. 将 server.js 代码从 const http = require('http') 更改为 const https = require('https')
  2. 将端口从 3000 更改为其他端口以排除某个端口号已在其他地方使用,例如8001 或 3333

完整服务器代码(在public_html/server/server.js):

const http = require('http')
const path = require('path')
const express = require('express')
const socketIo = require('socket.io')
const needle = require('needle')
const config = require('dotenv').config()
const TWITTER_BEARER_TOKEN = 'xxxxxx'

const PORT = process.env.PORT || 3000

const app = express()

const server = http.createServer(app)
const io = socketIo(server)

console.log(path.resolve(__dirname, '../', 'public_html/client', 'index.html'))

app.get('/', (req, res) => {
  res.sendFile(path.resolve(__dirname, '../', 'public_html/client', 'index.html'))
  // /usr/www/users/antick/public_html/client/index.html
})


const rulesURL = 'https://api.twitter.com/2/tweets/search/stream/rules'
const streamURL = 'https://api.twitter.com/2/tweets/search/stream?tweet.fields=public_metrics&expansions=author_id,attachments.media_keys&media.fields=duration_ms,height,media_key,preview_image_url,public_metrics,type,url,width&user.fields=profile_image_url'

const rules = [{ value: 'corruption' }]

// Get stream rules
async function getRules() {
  const response = await needle('get', rulesURL, {
    headers: {
      Authorization: `Bearer ${TWITTER_BEARER_TOKEN}`
    }
  })
  return response.body
}

// Set stream rules
async function setRules() {
  const data = {
    add: rules
  }

  const response = await needle('post', rulesURL, data, {
    headers: {
      'content-type': 'application/json',
      Authorization: `Bearer ${TWITTER_BEARER_TOKEN}`
    }
  })

  return response.body
}

// Delete stream rules
async function deleteRules(rules) {

  if (!Array.isArray(rules.data)) {
    return null
  }

  const ids = rules.data.map((rule) => rule.id)

  const data = {
    delete: {
      ids: ids
    }
  }

  const response = await needle('post', rulesURL, data, {
    headers: {
      'content-type': 'application/json',
      Authorization: `Bearer ${TWITTER_BEARER_TOKEN}`
    }
  })

  return response.body
}

function streamTweets(socket) {
  const stream = needle.get(streamURL, {
    headers: {
      Authorization: `Bearer ${TWITTER_BEARER_TOKEN}`
    }
  })

  stream.on('data', (data) => {
    try {
      const json = JSON.parse(data)
      console.log(json)

      socket.emit('tweet', json)
    } catch (error) {}
  })
}

io.on('connection', async () => {
  console.log('Client connected...')

  let currentRules

  try {
    // Get all stream rules
    currentRules = await getRules()

    // Delete all stream rules
    await deleteRules(currentRules)

    // Set rules based on array above (Rules)
    await setRules()
  } catch (error) {
    console.error(error)
    process.exit(1)
  }

  streamTweets(io)
})

server.listen(PORT, () => console.log(`Listening on Port ${PORT}`))

我的客户端代码 - 仅摘录(在 public_html/client/index.html):

<div class="container">
     <div id="tweetStream"></div>
</div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.1.2/socket.io.js" integrity="sha512-iZIBSs+gDyTH0ZhUem9eQ1t4DcEn2B9lHxfRMeGQhyNdSUz+rb+5A3ummX6DQTOIs1XK0gOteOg/LPtSo9VJ+w==" crossorigin="anonymous" referrerpolicy="no-referrer">
</script>

<script>
        const tweetStream = document.getElementById('tweetStream')
        const socket = io()
        //const socket = io.connect('https://www.exampledomain.com:3000/');
        const tweets = []

        socket.on('connect', () => {
            console.log('Connected to server...')
        })

        socket.on('tweet', (tweet) => {

// (code continues here)

我怀疑这与这两个位置中使用的路径有关:

  1. 在server/server.js:
app.get('/', (req, res) => {
  res.sendFile(path.resolve(__dirname, '../', 'public_html/client', 'index.html'))
  // resolves to:   /usr/www/users/antick/public_html/client/index.html
})
  1. 在 client/index.html 中:const socket = io() //

还有什么可能导致上述错误?我很感激任何帮助。谢谢!

在专业开发人员的帮助下,我终于能够解决问题,所以我想 post 解决方案。

两个要点是:

  1. 向任何 IP 开放端口 #3000(因为不知道客户端将从哪个 IP 地址访问该站点)
  2. 除节点外还安装 pm2 以持续流式传输推文

完整的结果代码如下所示:

A) 服务器 (index.js):

const http = require('http')
const path = require('path')
const express = require('express')
const socketIo = require('socket.io')
const needle = require('needle')
const config = require('dotenv').config()
const TOKEN = process.env.TWITTER_BEARER_TOKEN
const PORT = process.env.PORT || 3000

const app = express()

const server = http.createServer(app)
const io = socketIo(server)

app.get('/', (req, res) => {
    res.sendFile(path.resolve(__dirname, '../', 'client', 'index.html'))
})


const rulesURL = 'https://api.twitter.com/2/tweets/search/stream/rules'

const streamURL = 'https://api.twitter.com/2/tweets/search/stream?tweet.fields=public_metrics&expansions=author_id,attachments.media_keys&media.fields=duration_ms,height,media_key,preview_image_url,public_metrics,type,url,width&user.fields=profile_image_url'

const rules = [{ value: 'searchterm' }]

// Get stream rules
async function getRules() {
    const response = await needle('get', rulesURL, {
        headers: {
            Authorization: `Bearer ${TOKEN}`
        }
    })
    return response.body
}

// Set stream rules
async function setRules() {
    const data = {
        add: rules 
    }

    const response = await needle('post', rulesURL, data, {
        headers: {
            'content-type': 'application/json',
            Authorization: `Bearer ${TOKEN}`
        }
    })

    return response.body
}

// Delete stream rules
async function deleteRules(rules) {

    if(!Array.isArray(rules.data)) {
        return null 
    }

    const ids = rules.data.map((rule) => rule.id)

    const data = {
        delete: {
            ids: ids 
        } 
    }

    const response = await needle('post', rulesURL, data, {
        headers: {
            'content-type': 'application/json',
            Authorization: `Bearer ${TOKEN}`
        }
    })

    return response.body
}

function streamTweets (socket) {
    const stream = needle.get(streamURL, {
        headers: {
            Authorization: `Bearer ${TOKEN}`
        }
    })

    stream.on('data', (data) => {
        try {
            const json = JSON.parse(data)
            console.log(json)
            console.log(json.includes.media[0])

            socket.emit('tweet', json)
        } catch (error) {}
    })
}

io.on('connection', async () => {
    console.log('Client connected...')

    let currentRules

    try {
        // Get all stream rules
        currentRules = await getRules()

        // Delete all stream rules
        await deleteRules(currentRules)

        // Set rules based on array above (Rules)
        await setRules()
    } catch (error) {
        console.error(error)
        process.exit(1)
    }

    streamTweets(io)
})

server.listen(PORT, () => console.log(`Listening on Port ${PORT}`))

B) 客户 (index.html):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1"
      crossorigin="anonymous"
    />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
      integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="
      crossorigin="anonymous"
    />
    <title>Real-Time Tweet Stream</title>
  </head>
  <body>
    <nav class="navbar navbar-dark bg-dark">
      <div class="container">
        <a href="#" class="navbar-brand">Real-Time Tweet Stream</a>
      </div>
    </nav>

    <div class="container">
      <div id="tweetStream"></div>
    </div>

    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.4/socket.io.js"
      integrity="sha512-aMGMvNYu8Ue4G+fHa359jcPb1u+ytAF+P2SCb+PxrjCdO3n3ZTxJ30zuH39rimUggmTwmh2u7wvQsDTHESnmfQ=="
      crossorigin="anonymous"
    ></script>

    <script>
      const tweetStream = document.getElementById('tweetStream')
      const socket = io('IP.IP.IP.IP:3000'  , { transports : ['websocket'] } ) // webserver IP address

      const tweets = []

      socket.on('connect', () => {
        console.log('Connected to server...')
      })

      socket.on('tweet', (tweet) => {
        // console.log(tweet)
        const tweetData = {
          id: tweet.data.id,
          text: tweet.data.text,
          username: `@${tweet.includes.users[0].username}`,
        }

        const tweetEl = document.createElement('div')
        tweetEl.className = 'card my-4'
        tweetEl.innerHTML = `
            <div class="card-body">
                <h5 class="card-title">${tweetData.text}</h5>
                <h6 class="card-subtitle mb-2 text-muted">${tweetData.username}</h6>
                
                <a class="btn btn-primary mt-3" href="https://twitter.com/${tweetData.username}/status/${tweetData.id}">
                    <i class="fab fa-twitter"></i> Go To Tweet    
                </a>
            </div>
        `

        tweetStream.appendChild(tweetEl)

      })
    </script>
  </body>
</html>