【问题标题】:Connection to socket refused连接到套接字被拒绝
【发布时间】:2015-12-28 13:20:51
【问题描述】:

我有简单的 TCP 服务器程序。在我用CTRL+C 关闭它并在某些情况下再次重新启动后,telnet 客户端向我报告"connection refused"。为什么它并不总是发生,而只是在某些情况下发生?命令ps 没有显示任何关于我的程序的信息。

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>

void printHex(char *bts);

int main() {
    char str[100];
    int listen_fd, comm_fd;
    struct sockaddr_in servaddr;
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    bzero( &servaddr, sizeof(servaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htons(INADDR_ANY);
    servaddr.sin_port = htons(22000);
    printf("binding\n");
    bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    printf("listening\n");
    listen(listen_fd, 10);

    printf("accepting\n");    
    comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);
    printf("accep done\n");

    int cn = 0;

    while(1) {
        bzero(str, 100);
        printf("will read\n");
        int br = read(comm_fd, str, 100);
        printf("read done\n");
        if (br > 0) {
            printHex(str);
        } else if (br <= 0) {
            comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);
            printf("accep done\n");
        }
        printf("error %d \n", br);
   }
}

void printHex(char *bts) {
    char *s = bts;
    int i = 0;
    do {
        printf("%02X ", (unsigned char) *bts);
    } while (*++bts !=0);
    printf("%s\n",s);
}

【问题讨论】:

  • 您的代码中没有错误检查。也许这将是一个开始?
  • 补充@Roddy 所说的:你怎么知道你对bind()listen() 的调用有效?
  • OT: "do ... while (*++bts !=0);": 你不能依赖read() 读取0,至少只要客户端不只有 发送0s。在套接字上调用 read() 可能会返回介于 1 和第三个参数指定的最大值之间的任意字节数。
  • 关闭打开的套接字,清理是必不可少的。抓住中断这样做。
  • 添加到@alk 的评论; printHex 应该采用字节计数参数来告诉它要转储多少字节,而不是依赖缓冲区中的零终止符。

标签: c linux sockets


【解决方案1】:

您应该检查系统调用的所有返回值:

int res;

res = bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (res == -1)
  // handle error
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listed_fd == -1)
  // handle error

您很可能会发现您的代码在 bind 系统调用上失败,这意味着您不能在相同的 IP 地址、协议和端口上重用套接字。您可以使用setsockopt 调用更改此设置:

// allow to rebind
int reuseaddr_on = 1;
res = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,
            &reuseaddr_on, sizeof(reuseaddr_on));              
if (res < 0)
{
    stderr("Setting of SO_REUSEADDR on server's"     
                            "socket failed");                         
}

你也应该在完成后关闭一个套接字。

while(1)
{
  //  your loop
}
res = close(comm_fd);
if (res == -1)
  // handle error

正如 Linux 程序员手册在专门用于近距离通话的页面上所说:

注意事项

不检查 close() 的返回值是一个常见但严重的编程错误。很有可能 上一次 write(2) 操作的错误首先在最后的 close() 中报告。关闭文件时不检查返回值可能会导致静默 损失 数据。

【讨论】:

  • @downvoter 你能解释一下你的反对意见吗?谢谢。
【解决方案2】:

始终检查系统调用的返回值。 在这种情况下,

    bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));

EADDRINUSE 失败,因为您在绑定之前没有在套接字上设置SO_REUSEADDR

【讨论】:

    【解决方案3】:

    当你按CTRL-C并重新启动时,绑定成功了吗?

    bind(listen_fd, (struct sockaddr *) &amp;servaddr, sizeof(servaddr));

    您需要检查bind call 的状态。这很可能是由于连接已在服务器中移动到TIME_WAIT 状态而失败。随之而来的是客户端中的错误“连接被拒绝”。

    如果要在CTRL-C 之后立即运行服务器时,bind 会成功,请在您的套接字选项中使用SO_REUSEADDR。只要没有其他程序绑定到该端口,bind 就会成功。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-25
      • 2015-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-23
      相关资源
      最近更新 更多