Android连接Socket时遇到错误

3
我正在尝试使用Node.js和Express.io编写简单的应用程序。在阅读了一些Express.io文档并成功连接到http://chat.socket.io之后,我找到了一个简单的示例来创建Node.js和Express.io服务器端。在命令行中运行以下代码并在浏览器中打开http://localhost:3000后,我没有收到任何错误信息。我无法找到有关在http://chat.socket.io服务器端编码的好文档。现在我想尝试从Android客户端发送请求到服务器端并使用示例,但是我遇到了连接错误:

错误:

CONNECTION ERROR

server.js:

// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('../..')(server);
var port = process.env.PORT || 3000;

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});

// Routing
app.use(express.static(__dirname + '/public'));

// Chatroom

// usernames which are currently connected to the chat
var usernames = {};
var numUsers = 0;

io.on('connection', function (socket) {
  var addedUser = false;

  // when the client emits 'new message', this listens and executes
  socket.on('new message', function (data) {
    // we tell the client to execute 'new message'
    socket.broadcast.emit('new message', {
      username: socket.username,
      message: data
    });
  });

  // when the client emits 'add user', this listens and executes
  socket.on('add user', function (username) {
    // we store the username in the socket session for this client
    socket.username = username;
    // add the client's username to the global list
    usernames[username] = username;
    ++numUsers;
    addedUser = true;
    socket.emit('login', {
      numUsers: numUsers
    });
    // echo globally (all clients) that a person has connected
    socket.broadcast.emit('user joined', {
      username: socket.username,
      numUsers: numUsers
    });
  });

  // when the client emits 'typing', we broadcast it to others
  socket.on('typing', function () {
    socket.broadcast.emit('typing', {
      username: socket.username
    });
  });

  // when the client emits 'stop typing', we broadcast it to others
  socket.on('stop typing', function () {
    socket.broadcast.emit('stop typing', {
      username: socket.username
    });
  });

  // when the user disconnects.. perform this
  socket.on('disconnect', function () {
    // remove the username from global usernames list
    if (addedUser) {
      delete usernames[socket.username];
      --numUsers;

      // echo globally that this client has left
      socket.broadcast.emit('user left', {
        username: socket.username,
        numUsers: numUsers
      });
    }
  });
});

我的Android代码:

private Socket mSocket;
{
    try {
        /* connection successful to http://chat.socket.io */
        mSocket = IO.socket("http://localhost:3000");
    } catch (URISyntaxException e) {
        Log.e("Error URI", String.valueOf(e));
        throw new RuntimeException(e);
    }
}
public void onCreate(Bundle savedInstanceState) {
    ...
    mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
    mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
    mSocket.on("new message", onNewMessage);
    mSocket.on("user joined", onUserJoined);
    mSocket.on("user left", onUserLeft);
    mSocket.on("typing", onTyping);
    mSocket.on("stop typing", onStopTyping);
    mSocket.connect();
    ...

    Button signInButton = (Button) findViewById(R.id.sign_in_button);
    signInButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            attemptLogin();
        }
    });

    mSocket.on("login", onLogin);
}

private void attemptLogin() {
    mUsernameView.setError(null);
    String username = mUsernameView.getText().toString().trim();
    if (TextUtils.isEmpty(username)) {
        mUsernameView.setError(getString(R.string.error_field_required));
        mUsernameView.requestFocus();
        return;
    }

    mUsername = username;
    mSocket.emit("add user", username);
}

Android错误:

E/AndroidRuntime﹕ FATAL EXCEPTION: EventThread
    java.lang.IllegalArgumentException: delay < 0: -432345566375051264
            at java.util.Timer.schedule(Timer.java:457)
            at com.github.nkzawa.socketio.client.Manager.reconnect(Manager.java:497)
            at com.github.nkzawa.socketio.client.Manager.access$2000(Manager.java:20)
            at com.github.nkzawa.socketio.client.Manager$8$1$1.call(Manager.java:519)
            at com.github.nkzawa.socketio.client.Manager$1$3.call(Manager.java:282)
            at com.github.nkzawa.emitter.Emitter.emit(Emitter.java:117)
            at com.github.nkzawa.engineio.client.Socket.onError(Socket.java:754)
            at com.github.nkzawa.engineio.client.Socket.access$800(Socket.java:29)
            at com.github.nkzawa.engineio.client.Socket$4.call(Socket.java:293)
            at com.github.nkzawa.emitter.Emitter.emit(Emitter.java:117)
            at com.github.nkzawa.engineio.client.Transport.onError(Transport.java:63)
            at com.github.nkzawa.engineio.client.transports.PollingXHR.access$100(PollingXHR.java:19)
            at com.github.nkzawa.engineio.client.transports.PollingXHR$6$1.run(PollingXHR.java:126)
            at com.github.nkzawa.thread.EventThread$2.run(EventThread.java:75)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
            at java.lang.Thread.run(Thread.java:838)

你的服务器端口号不是 7076 吗?客户端正在尝试连接到端口号 3000。我不确定,但这可能是个问题。 - jonDoe
@jonDoe 我忘记粘贴代码后进行编辑了,先生。我的问题不是端口。 - DolDurma
@jonDoe帖子已更新,请求您审核。 - DolDurma
1个回答

4
我会把责任归咎于这个:
mSocket = IO.socket("http://localhost:3000");

我假设你的node.js服务器不是在安卓上运行,而是在电脑上运行。因此,在安卓上测试时,你试图连接回端口3000到安卓本身 - 因为localhost链接到设备本身。
如果你在服务器和安卓上使用相同的本地网络,你应该检查你的电脑IP,并将其放入localhost的位置。如果你的服务器有公共IP,你可能想要使用它。
编辑
换句话说,根据你的评论:你的PC IP是192.168.1.5。由于这是一个内部IP,你的安卓必须连接到与你的PC相同的子网络,因为你能够出现连接错误。基于这一点,我认为你需要在安卓的地址栏中输入http://192.168.1.5/,以访问PC正在提供的页面。假设如此,一个不变的是 - "我的安卓代码"脚本正在你的安卓上运行。因此,需要一个正确的主机来代替localhost192.168.1.5。无法确定你的安卓是否阻止了3000端口,但从安卓的角度来看,localhost是不合适的,只要你不是在那个设备上运行你的nodejs服务器。
此外,这种更改可能不会立即影响移动设备上的浏览器缓存。
查看你的代码,我认为你也会遇到一些用户使用相同用户名的问题。是的,听起来很奇怪,但是用户可能想要在浏览器中打开几个选项卡,连接到同一个socket服务器。一旦这样做,你的usernamesnumUsers变量将会损坏。
只要应用程序是单一实例专用的(例如:player@game),我会使用
usernames[username] = socket

为了将套接字存储在一旁,能够发布跨玩家相关事件,避免迭代所有已打开的套接字。

另外,为了聊天目的,您可能希望允许用户同时连接几个浏览器选项卡。通常我是这样存储所有套接字的:

if (!users[user]) {
    users[user] = {
        sockets: [socket]
    };
    console.log(sprintf('[%s] [CONNECTED] User %s', Date(), user));

} else {
    users[user].sockets.push(socket);
}

你可能会有所不同,可能基于聊天、频道等。将sockets推入一边,监听器允许我在同一个节点脚本文件中运行独立的UDP服务器。其目的是能够通过所有已打开的选项卡监视/阻止/警报单个用户,即使他们分散在两个不同的浏览器上。


@Mahdi.Pishguy 我同意这个观点。 - jonDoe
@yergo Nodejs服务器正在运行,我对此没有问题,我的PC的IP地址是192.168.1.5,更新后没有任何更改。对于我来说,“localhost”和“192.168.1.5”在获取错误连接方面都是相同的。 - DolDurma
@Mahdi.Pishguy 扩展了我的回答。希望对你有帮助。 - yergo
@yergo先生,非常感谢,我的问题已经解决了 :) 我可以在其他问题上得到帮助吗? - DolDurma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接