Socket.io 404 错误 - 客户端未连接
Socket.io 404 Error - Client not connecting
将在我的本地环境中运行良好的 Twitter 流应用程序部署到实时网络服务器时,我收到与 socket.io 相关的 404 错误。我看到这里已经有很多类似的问题,但没有找到解决我面临的问题的答案。
问题描述:
- 一旦我启动 server.js,它就会正确地将 'Listening on Port 3000' 记录到控制台(终端)
- 但是,消息“客户端已连接...”没有被触发
- 在浏览器控制台中,我看到错误消息“GET https://exampledomain.com/socket.io/?EIO................(etc) - 404 - XHR 加载失败”
我已经尝试了以下更改但没有成功,其中包括:
- 将 server.js 代码从
const http = require('http')
更改为 const https = require('https')
- 将端口从 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)
我怀疑这与这两个位置中使用的路径有关:
- 在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
})
- 在 client/index.html 中:
const socket = io()
//
还有什么可能导致上述错误?我很感激任何帮助。谢谢!
在专业开发人员的帮助下,我终于能够解决问题,所以我想 post 解决方案。
两个要点是:
- 向任何 IP 开放端口 #3000(因为不知道客户端将从哪个 IP 地址访问该站点)
- 除节点外还安装 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>
将在我的本地环境中运行良好的 Twitter 流应用程序部署到实时网络服务器时,我收到与 socket.io 相关的 404 错误。我看到这里已经有很多类似的问题,但没有找到解决我面临的问题的答案。
问题描述:
- 一旦我启动 server.js,它就会正确地将 'Listening on Port 3000' 记录到控制台(终端)
- 但是,消息“客户端已连接...”没有被触发
- 在浏览器控制台中,我看到错误消息“GET https://exampledomain.com/socket.io/?EIO................(etc) - 404 - XHR 加载失败”
我已经尝试了以下更改但没有成功,其中包括:
- 将 server.js 代码从
const http = require('http')
更改为const https = require('https')
- 将端口从 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)
我怀疑这与这两个位置中使用的路径有关:
- 在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
})
- 在 client/index.html 中:
const socket = io()
//
还有什么可能导致上述错误?我很感激任何帮助。谢谢!
在专业开发人员的帮助下,我终于能够解决问题,所以我想 post 解决方案。
两个要点是:
- 向任何 IP 开放端口 #3000(因为不知道客户端将从哪个 IP 地址访问该站点)
- 除节点外还安装 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>