How to make React/Socket.IO app reachable from outside - javascript

I'm struggling to make my app (React + Node.js + Socket.IO) running in local, reachable from outside.
I expose my 3000 port via tunneling (ngrok) but my socket connection listens on 3001.
After having my port exposed port I obtain a Url, which used from another pc not on my network, makes my React App reachable but without the socket functionalities.
If I try to make socket listening on 3000 the whole app stops working on local aswell.
I'm new on this so I puzzles me how to make it work
The final goal is to host the React App from my PC, using it to chat via browser from the same PC with another one not on the same network.
I just want to make it work from my pc, so no external hosting from third parts.
Any idea?
Index.js /server
const express = require("express");
const app = express();
const http = require("http");
const { Server } = require("socket.io");
const cors = require("cors");
app.use(cors());
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});
io.on("connection", (socket) => {
console.log(`User Connected: ${socket.id}`);
socket.on("join_room", (data) => {
socket.join(data);
});
socket.on("send_message", (data) => {
socket.to(data.room).emit("receive_message", data);
});
});
server.listen(3001, () => {
console.log("SERVER IS RUNNING");
});
App.js (React) /client
import "./App.css";
import io from "socket.io-client";
import { useEffect, useState } from "react";
const socket = io.connect("http://localhost:3001");
function App() {
//Room State
const [room, setRoom] = useState("");
// Messages States
const [message, setMessage] = useState("");
const [messageReceived, setMessageReceived] = useState("");
const joinRoom = () => {
if (room !== "") {
socket.emit("join_room", room);
}
};
const sendMessage = () => {
socket.emit("send_message", { message, room });
};
useEffect(() => {
socket.on("receive_message", (data) => {
setMessageReceived(data.message);
});
}, [socket]);
return (
<div className="App">
<input
placeholder="Room Number..."
onChange={(event) => {
setRoom(event.target.value);
}}
/>
<button onClick={joinRoom}> Join Room</button>
<input
placeholder="Message..."
onChange={(event) => {
setMessage(event.target.value);
}}
/>
<button onClick={sendMessage}> Send Message</button>
<h1> Message:</h1>
{messageReceived}
</div>
);
}
export default App;

Let's start with a quick review of your situation:
React app is running on port 3000 on your local computer ;
NodeJS app with WebSocket server is running on port 3001 on your local computer.
You want your app to work on your local network, therefore both the React App and the WebSocket server must be available.
If you try to run both on the same port it will fail (two different applications can't listen on the same port).
The idea then is to ngrok both the WebSocket server and the React App launching ngrok for localhost:3000 et localhost:3001 (you will need to adapt CORS and React WebSocket URL though).
Which raise a second problem, I suppose you can't use ngrok with two endpoints (localhost:3000 and localhost:3001) on the free tier.
A simple solution: you can tell React CRA and NodeJS to accept request from network using 0.0.0.0 as host:
for CRA it's the HOST variable (https://create-react-app.dev/docs/advanced-configuration/)
for NodeJS: you can use the host argument to server.listen.
You will still need to adapt the CORs and REACT app configuration though.

Related

How to make a socket.io connection between two different interfaces?

I'm actually trying to make a real-time connection between two different apps. I've found a bunch of tutorials about how to make a chat using socket.io, but that doesn't really help me since it's just the same app duplicated in multiple windows.
I'm making a pick & ban overlay for League of Legends in local development. My first thought was to display the empty overlay on one hand and create an interface to manually update it on the other hand. Socket.io seems to be the right thing to use in my case since it can provide new data without having to reload the component.
This is what I wrote in both apps :
const express = require('express');
const socket = require('socket.io');
// App setup
const app = express();
const server = app.listen(4200, function () {
console.log('Listening to requests on port 4200')
});
// Static files
app.use(express.static('public'));
// Socket setup
const io = socket(server);
io.on('connection', function (socket) {
console.log('Made socket connection', socket.id);
socket.on('change', function (data) {
io.sockets.emit('change', data);
});
});
But I fail to connect them as they have to listen to the same port. What am I doing wrong?
(Forgive my bad English and lack of syntax, I'm doing my best here. :p)
I am certainly not an expert on network programming, but as far as I know you need to have one listening app (backend) and another one to connect to it (client). And you define what happens with all the data (messages) that backend recieves (for example sending the messages it recieves to all the clients in the same chat room).
If I am correct to assume you are trying to connect two listening apps?
simple google search of "nodejs socket server client example" revealed this https://www.dev2qa.com/node-js-tcp-socket-client-server-example/ might wanna take your research in this direction
u can try something like this way
var express = require('express');
var socket = require('socket.io');
// App setup
var app = express();
var server = app.listen(8080, () => {
console.log('App started')
})
// Static file
app.use(express.static('public'))
// Socket SetUp
var io = socket(server);
io.on('connection', socket => {
console.log('made the connection')
socket.on('chat',data => {
io.sockets.emit('chat',data)
});
socket.on('typing',data => {
socket.broadcast.emit('typing',data);
});
})
create another file and
var socket = io.connect('http://localhost:8080')
// Elenment
var message = document.getElementById('message');
handle = document.getElementById('handle');
btn = document.getElementById('send');
output = document.getElementById('output');
feedback = document.getElementById('feedback');
// Emit Events
btn.addEventListener('click', () => {
socket.emit('chat', {
message: message.value,
handle: handle.value
})
})
message.addEventListener('keypress', () => {
socket.emit('typing', handle.value)
})
socket.on('chat',data => {
feedback.innerHTML = '';
output.innerHTML += '<p><strong>' + data.handle +': </strong>' +
data.message + '</p>'
})
socket.on('typing', data => {
feedback.innerHTML = '<p><emp>' + data + ' is typing a message... </emp></p>'
})
details are given here node socket chat app
Ok, figured it out. Here's how it works using express and vue together :
First, setup socket.io in your express server js file :
const express = require('express')
const { Server } = require('socket.io')
const http = require('http')
const app = express()
const server = http.createServer(app)
const io = new Server(server, {
cors: {
origin: '*',
methods: ['GET', 'POST', 'REMOVE']
}
})
const PORT = process.env.PORT || 8080
io.on('connection', (socket) => {
console.log('New socket user')
socket.on('SEND_MESSAGE', data => {
console.log('received message in back')
io.emit('MESSAGE', data)
})
})
server.listen(PORT, () => { console.log(`Server started on port : ${PORT}`)})
As you can see we received from the client "SEND_MESSAGE" and we trigger MESSAGE from the server to forward the information to all the clients. The point I was missing is that we bind SEND_MESSAGE on the socked created from the connection but we emit from the io server.
Now you vue part :
import io from 'socket.io-client'
export default {
data() {
return {
messages: [],
inputMessage: null,
socket: io('http://localhost:8080')
}
},
mounted() {
this.socket.on('MESSAGE', data => {
this.messages.push(data)
})
},
methods: {
sendMessage() {
const message = {
senderID: this.myID,
message: this.inputMessage,
sendAt: new Date()
}
this.socket.emit('SEND_MESSAGE', message)
this.inputMessage = null
},
},
}

How to reach my backend API in a web host?

I need to access my backend API to send info from a contact form for an email, I deployed my app in a webhost called Kinghost and it gave me two urls the first is generically mywebaddr.com:port-number and the second is mywebaddr.com/site.
I have tried to use both addresses with the function route in the end just like I did in localhost, that in order to work I used http://localhost:4000/contact for example, but it didn't work...
this is my request:
const baseUrl = 'http://mywebsiteurl.com/contact'
const initialState = {
message: {
name: '',
email: '',
subject: '',
main: ''
},
}
export default class ContactUs extends Component {
state = { ...initialState }
reset = () =>{
this.setState({message: initialState.message})
}
send = () => {
const message = this.state.message
const url = baseUrl
console.log(message)
axios.post(url, message)
.then(this.reset())
.then(alert('Message successfully sent!'))
}
this is my index.js (backend)
const express = require('express')
const app = express()
const consign = require('consign')
const port = 4005
consign()
.then('./config/middlewares.js')
.then('./api')
.then('./config/routes.js')
.into(app)
app.listen(port, () => {
console.log(port)
})
my middlewares.js contains cors
const bodyParser = require('body-parser')
const cors = require('cors')
module.exports = app => {
app.use(bodyParser.json())
app.use(cors())
}
Actually, I don't think it's because of my code itself once I can execute everything perfectly in localhost, but somehow I can't get through with the correct URL
I'm new to node and I can't guess what am I doing wrongly, so if someone can help me I'd be really thankful :)
This is not a question for stack-overflow as your issue is not with the application but is with your network.
Anyhow, your API application is running on port 4005. Make sure the port is open with your hosting provider. while your at it make sure your port 4000 is open as well.
after you confirm your firewall settings ill update my answer if your still facing issues.

Hundreds of polling requests coming from socket.io, app really slow

I'm creating a real time chat app with socket.io, express and React.
My issue is that I get hundreds of polling requests until my browser basically crashes, I have no idea why.
I've tried to put a polling duration, a close timeout, a heartbeat interval, I've checked, both my socket.io and socket.io-client are on the same version.. I've tried everything could find on the web but nothing works.
I'm sure it's just a stupid little mistake that I just can't find, if you could help that would be great, thanks!
Here's my code :
import express from "express";
import socketio from 'socket.io';
import path from 'path';
import ioCookieParser from 'socket.io-cookie-parser'
import http from 'http';
const app = express()
const port = process.env.PORT || 8000
app.set("port", port)
const httpServer = new http.Server(app);
const io = socketio(httpServer);
io.use(ioCookieParser(secret));
io.on('connection', function (client) {
const userId = client.request.signedCookies._session;
const clients = new Map();
client.on('login', () => {
clients.set(userId, { client })
console.log("clients :", clients)
})
client.on('message', (message) => {
User.findById(userId, function(err, obj) {
if(err) {
console.log(err);
return null
}
let currentUser = obj["email"];
client.broadcast.emit("received", { message, currentUser });
Connect.then(db => {
console.log("connected correctly to the server");
let chatMessage = new Chat({ message: message, sender: currentUser});
chatMessage.save();
});
})
})
client.on('error', function (err) {
console.log('received error from client:', client.id)
console.log(err)
})
});
Here is an example of a request :
GET localhost:8000 /socket.io/?EIO=3&transport=polling&t=Mideit5&sid=OxvoE0uJbi9DZyk-AAt8 xhr
Thanks!
My issue was that, in the React component, I was declaring :
const socket = io.connect('http://localhost:8000')
inside the component.
I've moved this constant outside of the component and now the issue is solved!

IBM Watson WebSocket Connection failure. HTTP authentication failed; no valid credentials avaliable

I am working on a speech-to-text web app using the IBM Watson Speech to text API. The API is fetched on the click of a button. But whenever I click the button. I get the above-mentioned error. I Have stored my API key and URL in a .env file.
I tried a lot but keep on getting this error. Please Help me out as I am new to all this.
I got server.js from the Watson Github Repo
Server.js
'use strict';
/* eslint-env node, es6 */
const env = require('dotenv');
env.config();
const express = require('express');
const app = express();
const AuthorizationV1 = require('watson-developer-cloud/authorization/v1');
const SpeechToTextV1 = require('watson-developer-cloud/speech-to-text/v1');
const TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');
const vcapServices = require('vcap_services');
const cors = require('cors');
// allows environment properties to be set in a file named .env
// on bluemix, enable rate-limiting and force https
if (process.env.VCAP_SERVICES) {
// enable rate-limiting
const RateLimit = require('express-rate-limit');
app.enable('trust proxy'); // required to work properly behind Bluemix's reverse proxy
const limiter = new RateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
delayMs: 0 // disable delaying - full speed until the max limit is reached
});
// apply to /api/*
app.use('/api/', limiter);
// force https - microphone access requires https in Chrome and possibly other browsers
// (*.mybluemix.net domains all have built-in https support)
const secure = require('express-secure-only');
app.use(secure());
}
app.use(express.static(__dirname + '/static'));
app.use(cors())
// token endpoints
// **Warning**: these endpoints should probably be guarded with additional authentication & authorization for production use
// speech to text token endpoint
var sttAuthService = new AuthorizationV1(
Object.assign(
{
iam_apikey: process.env.SPEECH_TO_TEXT_IAM_APIKEY, // if using an RC service
url: process.env.SPEECH_TO_TEXT_URL ? process.env.SPEECH_TO_TEXT_URL : SpeechToTextV1.URL
},
vcapServices.getCredentials('speech_to_text') // pulls credentials from environment in bluemix, otherwise returns {}
)
);
app.use('/api/speech-to-text/token', function(req, res) {
sttAuthService.getToken(function(err, token) {
if (err) {
console.log('Error retrieving token: ', err);
res.status(500).send('Error retrieving token');
return;
}
res.send(token);
});
});
const port = process.env.PORT || process.env.VCAP_APP_PORT || 3002;
app.listen(port, function() {
console.log('Example IBM Watson Speech JS SDK client app & token server live at http://localhost:%s/', port);
});
// Chrome requires https to access the user's microphone unless it's a localhost url so
// this sets up a basic server on port 3001 using an included self-signed certificate
// note: this is not suitable for production use
// however bluemix automatically adds https support at https://<myapp>.mybluemix.net
if (!process.env.VCAP_SERVICES) {
const fs = require('fs');
const https = require('https');
const HTTPS_PORT = 3001;
const options = {
key: fs.readFileSync(__dirname + '/keys/localhost.pem'),
cert: fs.readFileSync(__dirname + '/keys/localhost.cert')
};
https.createServer(options, app).listen(HTTPS_PORT, function() {
console.log('Secure server live at https://localhost:%s/', HTTPS_PORT);
});
}
App.js
import React, {Component} from 'react';
import 'tachyons';
//import WatsonSpeech from 'ibm-watson';
var recognizeMic = require('watson-speech/speech-to-text/recognize-microphone');
class App extends Component {
onListenClick = () => {
fetch('http://localhost:3002/api/speech-to-text/token')
.then(function(response) {
return response.text();
}).then(function (token) {
var stream = recognizeMic({
token: token, // use `access_token` as the parameter name if using an RC service
objectMode: true, // send objects instead of text
extractResults: true, // convert {results: [{alternatives:[...]}], result_index: 0} to {alternatives: [...], index: 0}
format: false // optional - performs basic formatting on the results such as capitals an periods
});
stream.on('data', function(data) {
console.log('error 1')
console.log(data);
});
stream.on('error', function(err) {
console.log('error 2')
console.log(err);
});
//document.querySelector('#stop').onclick = stream.stop.bind(stream);
}).catch(function(error) {
console.log('error 3')
console.log(error);
});
}
render() {
return(
<div>
<h2 className="tc"> Hello, and welcome to Watson Speech to text api</h2>
<button onClick={this.onListenClick}>Listen to Microphone</button>
</div>
);
}
}
export default App
Since the only code you show is fetching an authorisation token then I guess that that is what is throwing the authentication failure. I am not sure how old the code you are using is, but the mechanism you are using was used when the STT service credentials are userid / password. The mechanism became unreliable when IAM keys started to be used.
Your sample is still using watson-developer-cloud, but that has been superseded by ibm-watson. As migrating the code to ibm-watson will take a lot of rework, you can continue to use watson-developer-cloud.
If do you stick with watson-developer-cloud and you want to get hold of a token, with an IAM Key then use:
AuthIAMV1 = require('ibm-cloud-sdk-core/iam-token-manager/v1'),
...
tokenService = new AuthIAMV1.IamTokenManagerV1({iamApikey : apikey});
...
tokenService.getToken((err, res) => {
if (err) {
...
} else {
token = res;
...
}
});

socket.io connect issue

I keep getting this GET /socket.io/?EIO=3&transport=polling&t=MfRfeJD 404 4.438 ms - 149 error and I don't know where it's coming from.
I'm trying to integrate a live chat into my application using react, socket.io and express and I keep getting this not found error with the sockets. I'm not sure if the problem is on the client or server side. It appears to be trying to continuously poll the server, but is getting 404's back. That sounds like socket.io isn't running, but it all looks okay to me. It may also have something to do with paths, but I don't really know. I've tried adding different route to the io like "http://localhost:5000/" but still it still can't find the socket.
I get the page to show up and when I click send the message shows up but I can't get the sockets to connect.
In app.js
const express = require('express');
const http = require('http')
const bodyParser = require('body-parser')
const socketIo = require('socket.io')
var app = express();
const server = http.createServer(app)
const io = socketIo(server)
var PORT = process.env.PORT || 5000;
app.post('/', (req, res) => {
const { Body, From} = req.body
const message = {
body: Body,
from: From.slice(8),
}
io.emit('message', message)
res.send(`
<Response>
<Message>Thanks for texting!</Message>
</Response>
`)
})
io.on('connection', socket => {
socket.on('message', body => {
socket.broadcast.emit('message', {
body,
from: socket.id.slice(8)
})
})
})
server.listen(PORT);
In Chat.js
import React from "react";
import io from "socket.io-client";
class Chat extends React.Component {
constructor (props) {
super(props)
this.state = { messages: [] }
}
componentDidMount () {
this.socket = io('http://localhost:5000/')
this.socket.on('message', message => {
this.setState({ messages: [message, ...this.state.messages] })
})
}
handleSubmit = event => {
const body = event.target.value
if (event.keyCode === 13 && body) {
const message = {
body,
from: 'Me'
}
this.setState({ messages: [message, ...this.state.messages] })
this.socket.emit('message', body)
event.target.value = ''
}
}
render () {
const messages = this.state.messages.map((message, index) => {
return <li key={index}><b>{message.from}:</b>{message.body} </li>
})
return (
<div>
<h1>Admin Chat</h1>
<input type='text' placeholder='Enter a message...' onKeyUp={this.handleSubmit} />
{messages}
</div>
)
}
}
export default Chat;
404 is clearly saying no such page
149 will be the line number of the failure, your importing other code so it can be on any of the other code that the line 149 exists
i do see a maybe in app.js and the path
"app.post('/', (req, res) => {" Refers to an absolute path
try changing "http://localhost:5000/"
to "http://localhost:5000" or "http://localhost/:5000"
it looks like the "/" on the end puts the 5000 in the path not the port
--- EDIT --- on closer look at GET /socket.io/?EIO=3&transport=polling&t=MfRfeJD
if chat.js is running on the client and connecting to http://localhost:5000 than;
http://localhost/socket.io/?EIO=3&transport=polling&t=MfRfeJD would be the attempted connection
it looks like client is trying to connect back to itself.
how do you have the client / server setup?
if they are separate machines this would be looking for a non existing url.
either way is happening in the socket.io library.

Categories

Resources