NodeJS 为什么socketio服务器不处理我的客户端请求?

x7yiwoj4  于 2022-12-12  发布在  Node.js
关注(0)|答案(2)|浏览(132)

我正在创建一个node.js应用程序,它有一个react客户端和一个express/socketio后端以及passport身份验证。我试图从客户端发出一个socketio事件,然后让后端响应,但似乎在发出请求到后端处理请求之间的某个时间丢失了请求。
下面是我认为与当前问题相关的所有文件。后端位于localhost:5000上,客户端位于localhost:3000上。正如我所说,在Organization.js中标记的那一行之前,一切都很顺利。但在该行处或该行之后,事件可能会被发送,但socket.js中的相应路由都没有命中。
后端启动文件- app.js

'use strict';
import express from 'express'
import { createServer } from 'http'
import path from 'path'
import { fileURLToPath } from 'url';
import logger from 'morgan'
import cookieParser from 'cookie-parser'
import index from './routes/index.js'
import session from 'express-session'
import passport from './src/auth.js'
import { Server } from 'socket.io'
import { CrashCollector } from 'crashcollector'

new CrashCollector('crash_logs', (err, exit) => {
    console.log('Cleaning up...')
    console.log('Saving some stuff...')
    // Calls callback funtion executing process.exit()
    // You can also just write process.exit() here
    exit()
})

const app = express();
const server = createServer(app)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const io = new Server(server, {
    cookie: { maxAge: 24 * 60 * 60 * 1000 },
    cors: {
        origin: 'http://localhost:3000',
        credentials: true
    }
})
const sessionMiddleware = session({
    secret: "changeit", resave: false,
    saveUninitialized: false
});


// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));

app.use(sessionMiddleware)
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session());

app.use('/', index);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
    let err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
if (app.get('env') === 'development') {
    app.use(function (err, req, res, next) {
        console.log('dev error handler')
        res.status(err.status || 500);
        res.end()
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
    console.log('prod error handler')
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

app.set('port', process.env.PORT || 3000); //process.env.PORT = 5000

server.listen(app.get('port'), _ => {
    console.log(`Server listening on port ${app.get('port')}`)
})

export {
    io,
    sessionMiddleware
}

后端- socket.js

import { io, sessionMiddleware } from '../app.js'
import passport from '../src/auth.js'
import { getSequel } from '../src/database.js'
const wrap = middleware => (socket, next) =>
    middleware(socket.request, {}, next);

io.use((_, next) => {
    console.log('connection attempt')
    next()
})
io.use(wrap(sessionMiddleware));
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));

io.use((con, next) => {
    console.log('socket check', !!con.request.user)
    if (con.request.user) {
        next()
    } else {
        next(new Error('unauthorized'))
    }
});

io.on('connection', (con) => {
    console.log(`new connection ${con.id}`);
    con.on('confirm', cb => {
        cb(con.request.user.name);
    });

    con.on('eventTable', _ => {
        console.log('eventTable')
        con.emit(con.request?.user?.getEvents()?.map(e => e.toJSON()))
    })
});

客户端启动文件- index.js

import React from 'react'
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App/App';
import { io } from 'socket.io-client'
import configs from '../package.json'
const container = document.getElementById('root');
const root = createRoot(container);
export const socket = io.connect(configs.proxy, {  //configs.proxy = http://localhost:5000
    withCredentials: true
})
socket.on('connect', _ => {
    console.log('socket connect', socket.id)
})
socket.on('connect_error', _ => {
    console.log('socket connect_error')
})
socket.on('disconnect', _ => {
    console.log('socket disconnect')
})
socket.prependAny((eventName, ...args) => {
    console.log(eventName, args)
})

root.render((
    <React.StrictMode>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </React.StrictMode>
));

客户端- App.js

import React from 'react'
import { Route, Routes } from 'react-router-dom'
import './App.css'
import Login from './pages/Login'
import Home from './pages/Home'
import LoginCheck from './helper/LoginCheck'

const App = () => {
    console.log('app')
    return (
        <Routes>
            <Route exact path="/" element={<LoginCheck />} >
                <Route index element={<Home />} />
            </Route>
            <Route path="/login" element={<Login />} />
        </Routes>
    )
}

export default App;

客户端- Home.js

import React, { useState } from 'react';
import Org from './Organization'

const Home = () => {
    console.log('home')
    return localStorage.getItem('isOrg') ? (
        <Org />
    ) : (
        'Employee'
    )

}

export default Home;

客户端-组织. js

import React, { createElement, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'
import Countdown from 'ds-countdown'
import { socket } from '../../index'

const Org = () => {
    console.log('org')
        const setTable = (raffles) => {
        console.log('cb eventTable')
        JSON.parse(raffles).forEach(raffle => {
            const row = createElement('tr')
            const name = createElement('td')
            name.append(raffle.name)
            const num = createElement('td')
            num.append(raffle.num)
            const started = Date.parse(raffle.start) >= Date.now()
            const countdown =
                <><td>
                    <p style={{ color: started ? 'red' : 'green' }}>
                        {started ? 'Starts' : 'Ends'} in:
                        <div id={`timer${raffle.id}`} />
                    </p>
                </td></>
            const action = createElement('td')
            const edit = createElement('button')
            edit.addEventListener('click', e => {
                //goto edit at raffle.id
                alert('edit')
            })
            const end = createElement('button')
            end.addEventListener('click', e => {
                //set timer to 0
                alert('start/end')
            })

            action.append(edit)
            action.append(end)
            row.append(name)
            row.append(num)
            row.append(countdown)
            row.append(action)
            document.getElementById('eventTable').append(row)
            new Countdown({
                id: `timer${raffle.id}`,
                targetTime: started ?
                    raffle.end : raffle.start
            })
        })
    }

    useEffect(() => {
        console.log('useEffect')
        **socket.emit('eventTable')** //THIS IS WHERE IT BREAKS
        socket.on('eventTable', setTable)
        return () => {
            socket.off('eventTable', setTable)
        }
    })
    return (<>
        <h1 style={{ textAlign: 'center' }}>
            {localStorage.getItem('name')}
        </h1>
        <table style={{ marginLeft: 'auto', marginRight: 'auto' }}
            border="1"><caption>
            <h3>Scheduled Raffles</h3>
        </caption>
            <tbody id="eventTable">
                <tr>
                    <th>Name</th>
                    <th>Num Participants</th>
                    <th>Countdown</th>
                    <th>Action</th>
                </tr>
                <tr>
                    <td>Test</td>
                    <td>10</td>
                    <td>10:10:04</td>
                    <td><button> Edit </button> <button> End Now </button> <button> Stop </button></td>
                </tr>
            </tbody>
        </table>
    </>);
}

export default Org;

我在前端没有收到任何错误。实际上,客户端说连接已经建立,这要归功于index.js中的socket.on('connect')。socket.js文件中没有任何console.logs被激活,所以我有理由相信路由没有被命中。我还激活了socket上的Debugging,但那里的日志对我没有太大帮助:

Server listening on port 5000
  socket.io:server incoming connection with id 1wpATq-xiQ1jHttwAAAA +2s
  socket.io-parser decoded 0 as {"type":0,"nsp":"/"} +0ms
  socket.io:client connecting to namespace / +0ms
  socket.io:namespace adding socket to nsp / +0ms
  socket.io:socket socket connected - writing packet +0ms
  socket.io:socket join room f6lna-kbntjuwffpAAAB +3ms
  socket.io-parser encoding packet {"type":0,"data":{"sid":"f6lna-kbntjuwffpAAAB"},"nsp":"/"} +7ms
  socket.io-parser encoded {"type":0,"data":{"sid":"f6lna-kbntjuwffpAAAB"},"nsp":"/"} as 0{"sid":"f6lna-kbntjuwffpAAAB"} +1ms
POST /login 200 653.887 ms - 37
  socket.io-parser decoded 2["eventTable"] as {"type":2,"nsp":"/","data":["eventTable"]} +3s
  socket.io:socket got packet {"type":2,"nsp":"/","data":["eventTable"]} +3s
  socket.io:socket emitting event ["eventTable"] +0ms
  socket.io:socket dispatching an event ["eventTable"] +1ms
  socket.io-parser decoded 2["eventTable"] as {"type":2,"nsp":"/","data":["eventTable"]} +15ms
  socket.io:socket got packet {"type":2,"nsp":"/","data":["eventTable"]} +2ms
  socket.io:socket emitting event ["eventTable"] +0ms
  socket.io:socket dispatching an event ["eventTable"] +1ms
uxh89sit

uxh89sit1#

服务器端socket.emit调用应将事件名称作为第一个参数。

con.emit('eventTable', con.request?.user?.getEvents()?.map(e => e.toJSON()))
bvuwiixz

bvuwiixz2#

问题是我在初始化sockets.js路由之前启动了服务器。我不得不在后端做一些重新配置,但基本上我修复它的方法是在sockets.js中创建一个快速路由器,将所述路由器导入app.js,然后在调用server.listen(PORT)之前执行app.use('/socket', sockets)。下面是我的后端的最终状态:
新后端文件-init.js:

import express from 'express'
import { createServer } from 'http'
import session from 'express-session'
import { Server } from 'socket.io'
import { CrashCollector } from 'crashcollector'

new CrashCollector('crash_logs', (err, exit) => {
    console.log('Cleaning up...')
    console.log('Saving some stuff...')
    // Calls callback funtion executing process.exit()
    // You can also just write process.exit() here
    exit()
})

const app = express();
const server = createServer(app)
const io = new Server(server, {
    cookie: { maxAge: 24 * 60 * 60 * 1000 },
    cors: {
        origin: 'http://localhost:3000',
        credentials: true
    }
})
const sessionMiddleware = session({
    secret: "changeit", resave: false,
    saveUninitialized: false
});

export {
    server,
    app,
    io,
    sessionMiddleware
}

app.js:

'use strict';
import express from 'express'
import path from 'path'
import { fileURLToPath } from 'url';
import logger from 'morgan'
import cookieParser from 'cookie-parser'
import index from './routes/index.js'
import passport from './src/auth.js'
import { server, app, sessionMiddleware } from './init.js'
import sockets from './routes/sockets.js'

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));

app.use(sessionMiddleware)
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session());

app.use('/', index);
*app.use('/sockets', sockets)* //This is the bit I was missing

// catch 404 and forward to error handler
app.use(function (req, res, next) {
    let err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
if (app.get('env') === 'development') {
    app.use(function (err, req, res, next) {
        console.log('dev error handler')
        res.status(err.status || 500);
        res.end()
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
    console.log('prod error handler')
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

app.set('port', process.env.PORT || 3000);

server.listen(app.get('port'), _ => {
    console.log(`Server listening on port ${app.get('port')}`)
})

sockets.js:

import { io, sessionMiddleware } from '../init.js'
import passport from '../src/auth.js'
import { getSequel } from '../src/database.js'
import { Router } from 'express'
const router = Router()
const wrap = middleware => (socket, next) =>
    middleware(socket.request, {}, next);

io.use((_, next) => {
    console.log('connection attempt')
    next()
})
io.use(wrap(sessionMiddleware));
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));

io.use((con, next) => {
    console.log('socket check', !!con.request.user)
    if (con.request.user) {
        next()
    } else {
        next(new Error('unauthorized'))
    }
});

io.on('connection', (con) => {
    console.log(`new connection ${con.id}`);
    con.on('confirm', cb => {
        cb(con.request.user.name);
    });

    con.on('eventTable', cb => {
        con.request.user.getEvents()
        .then(events => events.map(e => e.JSON()))
//I switched to a callback here, rather than emitting a new event
        .then(mappedArray => cb(mappedArray)) 
    })
});

export default router

相关问题