connect():“连接被拒绝”,我不知道为什么

问题描述 投票:0回答:1

我仍然认为自己是一个 C 初学者,我正在尝试学习套接字编程,老实说我有点挣扎。

我正在尝试创建一个服务器来管理一个字符室,任意数量的客户端都可以连接到该字符室并与所有其他客户端聊天。

这里有很多代码:

服务器.c

/* Server that manages a chatroom */

// Necessary includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <errno.h>
#include <pthread.h>

// Custom header files includes
#include "func/func.h"

int main(int argc, char *argv[]) {
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // ~~~~~~~~~~ Argument validation - START ~~~~~~~~~~
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if (argc == 1) {
        printusage();
        return 1;
    }
    if (argc > 3) {
        printf("Too many arguments\n");
        printusage();
        return 1;
    }
    if (argc < 3) {
        printf("Not enough arguments\n");
        printusage();
        return 1;
    }

    int status = is_addr(argv[1]);
    if (status != 1) {
        switch (status) {
            case ELONG:
                printf("<addr> argument too long\n");
                break;
            case ECHAR:
                printf("<addr> argument contains invalid characters\n");
                break;
            case EINVALID:
                printf("<addr> argument is not a valid IPv4 or IPv6 address\n");
                break;
            default:
                printf("unknown error...\n");
                break;
        }
        return 1;

    }
    status = is_port(argv[2]);
    if (status != 1) {
        switch(status) {
            case ELONG:
                printf("<port> argument too long\n");
                break;
            case ECHAR:
                printf("<port> argument not a valid port number\n");
                break;
            default:
                printf("unknown error...\n");
        }
        return 1;
    }
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // ~~~~~~~~~~~ Argument validation - END ~~~~~~~~~~~
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    struct addrinfo hints, *res;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    status = getaddrinfo(NULL, argv[2], &hints, &res);
    if (status != 0) {
        printf("getaddrinfo(): error: %s\n", gai_strerror(status));
        return 1;
    }
    printf("getaddrinfo(): Success!\n");

    char addrstr[INET6_ADDRSTRLEN];
    int iptest = 0;
    struct sockaddr_in *ipv4;
    struct sockaddr_in6 *ipv6;
    if (strcmp(argv[1], "0") == 0) { // List of options
        printaddrs(res);
        printf("Select an address to bind to: ");
        char *id = strin(stdin);
        int count = 0;
        for (; res != NULL; res = res->ai_next) {
            if (strtol(id, NULL, 10) == count) {
                break;
            }
            count++;
        }
        if (res->ai_family == AF_INET) {
            ipv4 = (struct sockaddr_in *)res->ai_addr;
            inet_ntop(AF_INET, &ipv4->sin_addr, addrstr, sizeof(addrstr));
        } else if (res->ai_family == AF_INET6) {
            ipv6 = (struct sockaddr_in6 *)res->ai_addr;
            inet_ntop(AF_INET6, &ipv6->sin6_addr, addrstr, sizeof(addrstr));
        } else {
            printf("Invalid address family!\n");
            freeaddrinfo(res);
            return 1;
        }
        free(id);
    } else { // User entered a specific address
        strcpy(addrstr, argv[1]);
        int addrfamily = getaddrfamily(addrstr);
        if (addrfamily == AF_INET) {
            ipv4 = malloc(sizeof(struct sockaddr_in));
            printf("addrstr: %s\n", addrstr);
            inet_pton(AF_INET, addrstr, &ipv4->sin_addr);
            res->ai_addr = (struct sockaddr *)ipv4;
            res->ai_addr->sa_family = AF_INET;
            res->ai_family = AF_INET;
            res->ai_addrlen = sizeof(struct sockaddr_in);
            iptest = 4;
        } else if (addrfamily == AF_INET6) {
            ipv6 = malloc(sizeof(struct sockaddr_in6));;
            inet_pton(AF_INET6, addrstr, &ipv6->sin6_addr);
            res->ai_addr = (struct sockaddr *)ipv6;
            res->ai_addr->sa_family = AF_INET6;
            res->ai_family = AF_INET6;
            res->ai_addrlen = sizeof(struct sockaddr_in6);
            iptest = 6;
        } else {
            printf("Invalid address family!\n");
            freeaddrinfo(res);
            return 1;
        }
    }
    printf("~~~~~~~~~~~~~~~~~~~~\n");
    printf("Server address: %s\n", addrstr);
    printf("Server port: %s\n", argv[2]);

    int serverfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (serverfd == -1) {
        printf("socket(): error: %s\n", strerror(errno));
        terminate(res, ipv4, ipv6, iptest);
        return 1;
    }
    printf("socket(): Success!\n");

    if (bind(serverfd, res->ai_addr, res->ai_addrlen) == -1) {
        printf("bind(): error: %s\n", strerror(errno));
        terminate(res, ipv4, ipv6, iptest);
        return 1;
    }
    printf("bind(): Success!\n");

    if (listen(serverfd, 16) == -1) {
        printf("listen(): error: %s\n", strerror(errno));
        terminate(res, ipv4, ipv6, iptest);
        return 1;
    }
    printf("Server listening on port %s...\n", argv[2]);

    int *clientfds;     // array of connected clients (file descriptors)
    pthread_t accept_t; // thread for accepting new connections
    pthread_t *read_t;  // array of threads for reading from existing connections
    pthread_t *write_t; // array of threads for writing to existing connections
    serverinfo server = {serverfd, NULL, 0};

    pthread_create(&accept_t, NULL, handle_new, (void *)&server);

    pthread_join(accept_t, NULL);
    terminate(res, ipv4, ipv6, iptest);
    return 0;
}

客户端.c

/* Client to connect to server.c */

// Necessary includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <pthread.h>

#include "func/func.h"

int main(int argc, char *argv[]) {
    struct addrinfo hints, *res;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;


    int status = getaddrinfo(argv[1], argv[2],&hints, &res);
    if (status != 0) {
        printf("getaddrinfo(): error: %s\n", gai_strerror(status));
        return 1;
    }
    printf("getaddrinfo(): Success!\n");

    int clientfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (clientfd == -1) {
        printf("socket(): error: %s\n", strerror(errno));
        freeaddrinfo(res);
        return 1;
    }
    printf("socket(): Success!\n");
    if (connect(clientfd, res->ai_addr, res->ai_addrlen) == -1) {
        printf("connect(): error: %s\n", strerror(errno));
        close(clientfd);
        freeaddrinfo(res);
        return 1;
    }
    printf("Connected to server!\n");
}

func.h(包含函数声明)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <errno.h>

#define ELONG -1 // addr is too long
#define ECHAR -2 // addr contains invalid characters
#define EINVALID -3 // addr is invalid for other reasons

typedef struct serverinfo {
    int si_serverfd; // file descriptor of the server
    int *si_clientfds; // array of connected clients (file descriptors)
    int si_size; // size of si_clientfds (number of connected clients))
} serverinfo;

// func/strin.c
char *strin(FILE *stream);

// func/validation.c
int is_digit(char c);
int is_ipv4(char *addr);
int is_ipv6(char *addr);
int is_addr(char *addr);
int is_port(char *port);
int getaddrfamily(char *addr);
void printusage();

// func/printaddrs.c
void printaddrs(struct addrinfo *res);

// func/terminate.c
void terminate(struct addrinfo *res, struct sockaddr_in *ipv4, struct sockaddr_in6 *ipv6, int iptest);

// func/pthread.c
void *handle_new(void *si);

func/pthread.c

/* <pthread.h> related functions */
#include "func.h"

//
void *handle_new(void *si) {
    serverinfo *server = (serverinfo *)si;
    printf("Waiting for connection...\n");
    int tempfd = accept(server->si_serverfd, NULL, NULL);
    if (tempfd == -1)
        printf("accept_t: accept(): error\n");
    printf("New connection!\n");
    server->si_size++;
    server->si_clientfds = realloc(server->si_clientfds, server->si_size * sizeof(int));
    return NULL;
}

func/printaddrs.c

#include "func.h"

void printaddrs(struct addrinfo *res) {
    struct sockaddr_in *ipv4;
    struct sockaddr_in6 *ipv6;
    char addrstr[INET6_ADDRSTRLEN];
    int count = 0;
    char ipver[5];
    for (struct addrinfo *p = res; p != NULL; p = p->ai_next) {
        if (p->ai_family == AF_INET) {
            ipv4 = (struct sockaddr_in *)p->ai_addr;
            inet_ntop(AF_INET, &ipv4->sin_addr, addrstr, sizeof(addrstr));
            strcpy(ipver, "IPv4");
            printf("%d|%s\t%s\n", count, ipver, addrstr);
        }else if (p->ai_family == AF_INET6) {
            ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            inet_ntop(AF_INET6, &ipv6->sin6_addr, addrstr, sizeof(addrstr));
            strcpy(ipver, "IPv6");
            printf("%d|%s\t%s\n", count, ipver, addrstr);
        }
        count++;
    }
}

func/validation.c

#include "func.h"

// Returns 1 if character c is a digit, otherwise returns 0
int is_digit(char c) {
    switch (c) {
        case '0' ... '9':
            return 1;
        case 'a' ... 'f':
            return 1;
        case 'A' ... 'F':
            return 1;
        default:
            return 0;
    }
}

// Returns 1 if addr is a valid IPv4 address, otherwise returns 0
int is_ipv4(char *addr) {
    int dots = 0, octets = 0;
    char number[INET6_ADDRSTRLEN] = "0";
    unsigned long len = strlen(addr) + 1; // Length of the string INCLUDING the null terminator
    for (int i = 0; i < len; i++) {
        if (addr[i] == '.' || addr[i] == '\0') {
            if (is_digit(addr[i - 1]))
                octets++;
            dots++;
            if (strtol(number, NULL, 10) > 255) {
                return 0;
            }
            strcpy(number, "0");
        }
        strcat(number, &addr[i]);
    }
    if (dots - 1 != 3 || octets != 4) {
        return 0;
    }
    return 1;
}

int is_ipv6(char *addr) {
    int colons = 0, hextets = 0;
    char number[INET6_ADDRSTRLEN] = "0";
    unsigned long len = strlen(addr) + 1; // Length of the string INCLUDING the null terminator
    for (int i = 0; i < len; i++) {
        if (addr[i] == ':' || addr[i] == '\0') {
            if (is_digit(addr[i - 1]))
                hextets++;
            colons++;
            if (strtol(number, NULL, 16) >  65535) { // FFFF (16)
                return 0;
            }
            strcpy(number, "0");
        }
        strcat(number, &addr[i]);
    }
    if (colons - 1 < 2 || colons - 1 > 7) {
        return 0;
    }
    if (colons != 7) {
        if (addr[0] != ':' && addr[strlen(addr) - 1] != ':')
            return 0;
    }
    return 1;
}

/** Returns 1 if addr is a valid IPv4 or IPv6 address, or one of the following error codes
 *  ELONG (-1) -> addr is too long
 *  ECHAR (-2) -> addr contains an invalid character
 *  EINVALID (-3) -> addr is not a valid IPv4 or IPv6 address
 */
int is_addr(char *addr) {
    int dots = 0, colons = 0;
    unsigned long len = strlen(addr);
    if (len * sizeof(char) >= INET6_ADDRSTRLEN)
        return ELONG;
    for (int i = 0; i < len; i++) {

        // If character is not a digit, colon or dot OR if there are both dots and colons present
        if ((is_digit(addr[i]) == 0 && addr[i] != '.' && addr[i] != ':') || (dots > 0 && colons > 0))
            return ECHAR;

        // Increment dots or colons if character is a dot or a colon
        if (addr[i] == '.')
            dots++;
        else if (addr[i] == ':')
            colons++;
    }
    if (dots > 0) {
        if (is_ipv4(addr)) return 1;
        else return EINVALID;
    } else if (colons > 0)  {
        if (is_ipv6(addr)) return 1;
        else return EINVALID;
    }
}

/** Returns 1 if port is a valid port number, or one of the following error codes
 *  ELONG (-1) -> port is too long
 *  ECHAR (-2) -> port contains an invalid character
 */
int is_port(char *port) {
    unsigned long len = strlen(port);
    if (len > 6)
        return ELONG;
    for (int i = 0; i < len; i++) {
        if (is_digit(port[i]) == 0)
            return ECHAR;
    }
    return 1;
}

// Returns the address family of a given !VALID! IP address (AF_INET or AF_INET6),
// or -1 in case there is no dot or colon present
int getaddrfamily(char *addr) {
    unsigned long len = strlen(addr);
    for (int i = 0; i < len; i++) {
        if (addr[i] == '.')
            return AF_INET;
        else if (addr[i] == ':')
            return AF_INET6;
    }
    return -1;
}

// prints the 'usage' message
void printusage() {
    printf("usage:\n./server <addr> <port>\n\n");
    printf("<addr> - IPv4 or IPv6 address for the server to bind to (Enter 0 for list of options)\n");
    printf("<port> - port number for the server to bind to\n");
}

代码结束 其他函数不应该很重要,只需知道在我的情况下 strin() 从 stdin 获取输入,而 Terminate() free() 是需要 free()'d 的所有内容

问题来了

当我启动 server.c...

./server 127.0.0.1 8080
getaddrinfo(): Success!
addrstr: 127.0.0.1
~~~~~~~~~~~~~~~~~~~~
Server address: 127.0.0.1
Server port: 8080
socket(): Success!
bind(): Success!
Server listening on port 8080...
Waiting for connection...

看起来效果很好...

但是当我以同样的方式启动client.c时...

./client 127.0.0.1 8080
getaddrinfo(): Success!
socket(): Success!
connect(): error: Connection refused

它只是说连接被拒绝

我尝试从头开始制作简单的回显服务器和客户端(客户端发送消息,接收相同的消息)文件来测试我的计算机端是否有问题(防火墙等),它工作得很好......

我知道我问了很多问题,而且这里有数百行代码,但如果有人有时间并且更重要的是有心情在这里帮助我,我将非常感激!

c network-programming
1个回答
0
投票

正如@yvs2014所说,我需要指定

ipv4->sin_port = htons(strtol(argv[2], NULL, 10));
(我在这里使用strtol()而不是atoi())

非常感谢大家对我的帮助!我非常感激! 你让我在 C 方面继续进步!

© www.soinside.com 2019 - 2024. All rights reserved.