linux套接字:服务器在客户端之前退出

发布于 2024-09-07 22:42:59 字数 6184 浏览 9 评论 0原文

我有这个程序,只是一个简单服务器和客户端连接的框架。我会把它变成一个聊天。 (不要介意线程函数和信号..)

服务器:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>


#define BUFFERSIZE 512
#define TIMESIZE 32
#define QUIT "!quit"


// thread pou diavazei
void *readthread(void *argp);
// katharizei ligo prin kleisei to programma
void progreset();
// kleinei to prog me ctrl-c
void sigexit();


int sock, endchat;
char username1[50];
pthread_t thrread;


int main(int argc, char** argv) {
    int port, s;
    char username[50];
    struct sockaddr_in server, client;
    struct sockaddr *serverptr, *clientptr;
    unsigned int clientlen;
    char buf[BUFFERSIZE];
    int len;
    time_t sec;
    char timestr[TIMESIZE];


    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT, sigexit);


    if (argc != 3) {
        printf("Error: Wrong arguments\n");
        printf("Usage: %s <username> <port>\n", argv[0]);
        return -1;
    }

    strcpy(username, argv[1]);
    port = atoi(argv[2]);

    // ftiaxno to socket
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Failed to create socket");
        return -1;
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons((short)port);
    if (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0) {
        perror("Failed to bind socket");
        return -1;
    }

    if (listen(s, 5) != 0) {
        perror("Error in listen()");
        return -1;
    }

    clientptr = (struct sockaddr *)&client;
    clientlen = sizeof(client);

    // perimeno sindesi apo ton pelati
    printf("Accepting connections on port %d..\n", port);
    if ((sock = accept(s, clientptr, &clientlen)) < 0) {
        perror("Error in accept()");
        return -1;
    }

    // pairno to ip tou pelati
    if (getpeername(sock, (struct sockaddr *)&client, &clientlen) < 0) {
        printf("Accepted connection\n");
    } else {
        printf("Accepted connection from %s\n", inet_ntoa(client.sin_addr));
    }

    // stelno kai pairno ta usernames
    bzero(buf, sizeof(buf));
    strcpy(buf, username);
    if (write(sock, buf, sizeof(buf)) < 0) {
        perror("write1");
        return -1;
    }

    bzero(buf, sizeof(buf));
    if (read(sock, buf, sizeof(buf)) < 0) {
        perror("read1");
        return -1;
    }
    strcpy(username1, buf);

    printf("Chatting with %s..\n\n", username1);








    progreset();
    return 0;
}


void *readthread(void *argp) {
    char buf[BUFFERSIZE];
    char timestr[TIMESIZE];
    int len;
    time_t sec;
    struct tm *timeinfo;


    while (1) {

    }



    endchat = 1;
    pthread_exit(0);
}


void progreset() {
    printf("\nExiting..\n");
    close(sock);
}


void sigexit() {
    printf("test\n");
    close(sock);
    signal(SIGINT, SIG_DFL);
    kill(getpid(),SIGINT);
    printf("ok\n");

    exit(0);
}

客户端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>


#define BUFFERSIZE 512
#define TIMESIZE 32
#define QUIT "!quit"


// thread pou diavazei
void *readthread(void *argp);
// katharizei ligo prin kleisei to programma
void progreset();
// kleinei to prog me ctrl-c
void sigexit();


int sock, endchat;
char username1[50];
pthread_t thrread;


int main(int argc, char** argv) {
    int port;
    char username[50];
    struct sockaddr_in server, client;
    struct sockaddr *serverptr, *clientptr;
    unsigned int serverlen;
    struct hostent *rem;
    char buf[BUFFERSIZE];
    int len;
    time_t sec;
    char timestr[TIMESIZE];


    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT, sigexit);


    if (argc != 4) {
        printf("Error: Wrong arguments\n");
        printf("Usage: %s <username> <ip address> <port>\n", argv[0]);
        return -1;
    }

    strcpy(username, argv[1]);
    port = atoi(argv[3]);

    // ftiaxno to socket
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Failed to create socket");
        return -1;
    }
    rem = gethostbyname(argv[2]);
    server.sin_family = AF_INET;
    bcopy((char*)rem->h_addr, (char*)&server.sin_addr, rem->h_length);
    server.sin_port = htons((short)port);
    serverptr = (struct sockaddr *)&server;
    serverlen = sizeof(server);

    // kano connect me to server
    if (connect(sock, serverptr, serverlen) < 0) {
        perror("Failed to connect");
        return -1;
    }

    // pairno kai stelno ta usernames
    bzero(buf, sizeof(buf));
    if (read(sock, buf, sizeof(buf)) < 0) {
        perror("read1");
        return -1;
    }
    strcpy(username1, buf);

    bzero(buf, sizeof(buf));
    strcpy(buf, username);
    if (write(sock, buf, sizeof(buf)) < 0) {
        perror("write1");
        return -1;
    }

    printf("Chatting with %s..\n\n", username1);







    sleep(1);










    progreset();
    return 0;
}


void *readthread(void *argp) {
    char buf[BUFFERSIZE];
    char timestr[TIMESIZE];
    int len;
    time_t sec;
    struct tm *timeinfo;

    while (1) {
    }
    endchat = 1;
    pthread_exit(0);
}


void progreset() {
    printf("\nExiting..\n");
    close(sock);
}


void sigexit() {
    printf("test\n");
    close(sock);
    signal(SIGINT, SIG_DFL);
    kill(getpid(),SIGINT);
    printf("ok\n");

    exit(0);
}

在 main() 函数中,服务器只是等待连接,然后退出。 客户端在连接休眠1秒后结束。

当我运行这个时,比如 ./server server 1234 然后./client 客户端 localhost 1234 两者都正常退出,但是当我第二次运行服务器时,它说无法绑定套接字:地址已在使用中。

怎么了? 服务器必须总是在客户端之后退出吗?

额外问题:我希望每个程序有两个线程,一个用于读取,一个用于写入。它们可以在同一个套接字上运行吗?

非常感谢

I have this prog, just a skeleton for a simple sever and client connection. I will make it a chat.
(dont mind the thread func and signals..)

server:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>


#define BUFFERSIZE 512
#define TIMESIZE 32
#define QUIT "!quit"


// thread pou diavazei
void *readthread(void *argp);
// katharizei ligo prin kleisei to programma
void progreset();
// kleinei to prog me ctrl-c
void sigexit();


int sock, endchat;
char username1[50];
pthread_t thrread;


int main(int argc, char** argv) {
    int port, s;
    char username[50];
    struct sockaddr_in server, client;
    struct sockaddr *serverptr, *clientptr;
    unsigned int clientlen;
    char buf[BUFFERSIZE];
    int len;
    time_t sec;
    char timestr[TIMESIZE];


    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT, sigexit);


    if (argc != 3) {
        printf("Error: Wrong arguments\n");
        printf("Usage: %s <username> <port>\n", argv[0]);
        return -1;
    }

    strcpy(username, argv[1]);
    port = atoi(argv[2]);

    // ftiaxno to socket
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Failed to create socket");
        return -1;
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons((short)port);
    if (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0) {
        perror("Failed to bind socket");
        return -1;
    }

    if (listen(s, 5) != 0) {
        perror("Error in listen()");
        return -1;
    }

    clientptr = (struct sockaddr *)&client;
    clientlen = sizeof(client);

    // perimeno sindesi apo ton pelati
    printf("Accepting connections on port %d..\n", port);
    if ((sock = accept(s, clientptr, &clientlen)) < 0) {
        perror("Error in accept()");
        return -1;
    }

    // pairno to ip tou pelati
    if (getpeername(sock, (struct sockaddr *)&client, &clientlen) < 0) {
        printf("Accepted connection\n");
    } else {
        printf("Accepted connection from %s\n", inet_ntoa(client.sin_addr));
    }

    // stelno kai pairno ta usernames
    bzero(buf, sizeof(buf));
    strcpy(buf, username);
    if (write(sock, buf, sizeof(buf)) < 0) {
        perror("write1");
        return -1;
    }

    bzero(buf, sizeof(buf));
    if (read(sock, buf, sizeof(buf)) < 0) {
        perror("read1");
        return -1;
    }
    strcpy(username1, buf);

    printf("Chatting with %s..\n\n", username1);








    progreset();
    return 0;
}


void *readthread(void *argp) {
    char buf[BUFFERSIZE];
    char timestr[TIMESIZE];
    int len;
    time_t sec;
    struct tm *timeinfo;


    while (1) {

    }



    endchat = 1;
    pthread_exit(0);
}


void progreset() {
    printf("\nExiting..\n");
    close(sock);
}


void sigexit() {
    printf("test\n");
    close(sock);
    signal(SIGINT, SIG_DFL);
    kill(getpid(),SIGINT);
    printf("ok\n");

    exit(0);
}

client

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>


#define BUFFERSIZE 512
#define TIMESIZE 32
#define QUIT "!quit"


// thread pou diavazei
void *readthread(void *argp);
// katharizei ligo prin kleisei to programma
void progreset();
// kleinei to prog me ctrl-c
void sigexit();


int sock, endchat;
char username1[50];
pthread_t thrread;


int main(int argc, char** argv) {
    int port;
    char username[50];
    struct sockaddr_in server, client;
    struct sockaddr *serverptr, *clientptr;
    unsigned int serverlen;
    struct hostent *rem;
    char buf[BUFFERSIZE];
    int len;
    time_t sec;
    char timestr[TIMESIZE];


    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT, sigexit);


    if (argc != 4) {
        printf("Error: Wrong arguments\n");
        printf("Usage: %s <username> <ip address> <port>\n", argv[0]);
        return -1;
    }

    strcpy(username, argv[1]);
    port = atoi(argv[3]);

    // ftiaxno to socket
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Failed to create socket");
        return -1;
    }
    rem = gethostbyname(argv[2]);
    server.sin_family = AF_INET;
    bcopy((char*)rem->h_addr, (char*)&server.sin_addr, rem->h_length);
    server.sin_port = htons((short)port);
    serverptr = (struct sockaddr *)&server;
    serverlen = sizeof(server);

    // kano connect me to server
    if (connect(sock, serverptr, serverlen) < 0) {
        perror("Failed to connect");
        return -1;
    }

    // pairno kai stelno ta usernames
    bzero(buf, sizeof(buf));
    if (read(sock, buf, sizeof(buf)) < 0) {
        perror("read1");
        return -1;
    }
    strcpy(username1, buf);

    bzero(buf, sizeof(buf));
    strcpy(buf, username);
    if (write(sock, buf, sizeof(buf)) < 0) {
        perror("write1");
        return -1;
    }

    printf("Chatting with %s..\n\n", username1);







    sleep(1);










    progreset();
    return 0;
}


void *readthread(void *argp) {
    char buf[BUFFERSIZE];
    char timestr[TIMESIZE];
    int len;
    time_t sec;
    struct tm *timeinfo;

    while (1) {
    }
    endchat = 1;
    pthread_exit(0);
}


void progreset() {
    printf("\nExiting..\n");
    close(sock);
}


void sigexit() {
    printf("test\n");
    close(sock);
    signal(SIGINT, SIG_DFL);
    kill(getpid(),SIGINT);
    printf("ok\n");

    exit(0);
}

in the main() func, the server just waits for connection, and then exits.
the client, after the connection sleeps for 1 sec, and then ends.

when I run this, like ./server server 1234
and then ./client client localhost 1234
both exit normally, but when i run the server the second time, it says Failed to bind socket: Address already in use.

what is wrong?
must the server exit always after the client?

bonus Q: I want to have two threads per program, one to read and one to write. Can they operate on the same socket?

thank you very much

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甜心小果奶 2024-09-14 22:42:59

确保在退出之前在套接字上调用 closesocket(s)。
在打开之前也可以尝试setsockopt SO_REUSEADDR。

这里是一个关于 Linux 套接字的很好的教程。

当然,如果你发布一些代码,那就更容易了。

您可以在同一个套接字上读取和写入。

我怀疑您收到“地址使用错误”(EADDRINUSE)
您可以使用bind API函数将地址(接口和端口)绑定到套接字端点。您可以在服务器设置中使用此功能来限制可能进行传入连接的接口。您还可以在客户端设置中使用此功能来限制用于传出连接的接口。
Bind 最常见的用途是将端口号与服务器关联并使用通配符地址 (INADDR_ANY),这允许将任何接口用于传入连接。
绑定时经常遇到的问题是尝试绑定已在使用的端口。陷阱是可能不存在活动套接字,但仍然不允许绑定到端口(bind 返回 EADDRINUSE),这是由 TCP 套接字 TIME_WAIT 状态引起的。此状态使套接字在关闭后保持两到四分钟。
退出 TIME_WAIT 状态后,套接字将被删除,并且地址可以毫无问题地反弹。
等待 TIME_WAIT 完成可能很烦人,特别是如果您正在开发套接字服务器并且需要停止服务器以进行更改,然后重新启动它。幸运的是,有一种方法可以绕过 TIME_WAIT 状态。您可以将 SO_REUSEADDR 套接字选项应用于套接字,以便可以立即重用该端口。
在绑定地址之前,我使用 SO_REUSEADDR 选项调用setsockopt。为了启用地址重用,我将整数参数 (on) 设置为 1(否则,您可以将其设置为 0 以禁用地址重用)。

使用 SO_REUSEADDR 套接字选项避免“地址正在使用”错误 应用

int sock, ret, on;
struct sockaddr_in servaddr;

/* Create a new stream (TCP) socket */
sock = socket( AF_INET, SOCK_STREAM, 0 ):

/* Enable address reuse */
on = 1;
ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

/* Allow connections to port 8080 from any available interface */
memset( &servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
servaddr.sin_port = htons( 45000 );

/* Bind to the address (interface/port) */
ret = bind( sock, (struct sockaddr *)&servaddr, sizeof(servaddr) );

SO_REUSEADDR 套接字选项后,绑定 API 函数将始终允许立即重用该地址。

Make sure you call closesocket(s) on the socket before you exit.
Also try setsockopt SO_REUSEADDR before you open.

Here is a good tutorial on Linux Sockets.

Of course if you posted some code it would make it easier.

You can read and write on the same socket.

I suspect your getting "Address in use error" (EADDRINUSE)
You can use the bind API function to bind an address (an interface and a port) to a socket endpoint. You can use this function in a server setting to restrict the interfaces from which incoming connections are possible. You can also use this function from a client setting to restrict the interface that should be used for an outgoing connection.
The most common use of bind is to associate a port number with a server and use the wildcard address (INADDR_ANY), which allows any interface to be used for incoming connections.
The problem commonly encountered with bind is attempting to bind a port that's already in use. The pitfall is that no active socket may exist, but binding to the port is still disallowed (bind returns EADDRINUSE), which is caused by the TCP socket TIME_WAIT state. This state keeps a socket around for two to four minutes after its close.
After the TIME_WAIT state has exited, the socket is removed, and the address can be rebound without issue.
Waiting for TIME_WAIT to finish can be annoying, especially if you're developing a socket server and you need to stop the server to make changes and then restart it. Luckily, there's a way to get around the TIME_WAIT state. You can apply the SO_REUSEADDR socket option to the socket, such that the port can be reused immediately.
Prior to binding an address, I call setsockopt with the SO_REUSEADDR option. To enable address reuse, I set the integer argument (on) to 1 (otherwise, you can set it to 0 to disable address reuse).

Avoiding the "Address In Use" error using the SO_REUSEADDR socket option

int sock, ret, on;
struct sockaddr_in servaddr;

/* Create a new stream (TCP) socket */
sock = socket( AF_INET, SOCK_STREAM, 0 ):

/* Enable address reuse */
on = 1;
ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

/* Allow connections to port 8080 from any available interface */
memset( &servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
servaddr.sin_port = htons( 45000 );

/* Bind to the address (interface/port) */
ret = bind( sock, (struct sockaddr *)&servaddr, sizeof(servaddr) );

After you have applied the SO_REUSEADDR socket option, the bind API function will always permit immediate reuse of the address.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文