如何了解客户的插座已终止 [英] How to know if the client has terminated in sockets

查看:169
本文介绍了如何了解客户的插座已终止的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设,我有一个连接的socket写这篇code后..

  IF((SD =接受(socket_d,(结构sockaddr *)及client_addr,&安培;阿伦))小于0)
{
    PERROR(接受失败\\ n);
    出口(1);
}

我怎么能知道在服务器端客户端已退出。

我的整个程序实际上做以下..


  • 接受来自客户端的连接

  • 启动一个新线程,从特定的客户端读取消息,然后广播这条消息给所有连接的客户端。

如果你想看到整个code ...在这整个code。我还多了一个问题所困扰,每当我杀死按Ctrl + C客户端,我的服务器端突然终止..这将是很好的如果任何人都可以提出的问题是什么..

 的#include< SYS / types.h中>
#包括LT&; SYS / socket.h中>
#包括LT&; netinet / in.h中>
#包括LT&; ARPA / inet.h>
#包括LT&;&netdb.h中GT;
#包括LT&;&stdio.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;
#包括LT&;&signal.h中GT;
#包括LT&;&errno.h中GT;
#包括LT&;&pthreads.h中GT;/ *常量* /
#定义DEFAULT_PORT 10000
#定义LISTEN_QUEUE_LIMIT 6
#定义TOTAL_CLIENTS 10
#定义CHAR_BUFFER 256/ *全局变量* /
INT current_client = 0;
INT connected_clients [TOTAL_CLIENTS]
EXTERN INT错误号;无效* Client_Handler的(无效* socket_d);INT主(INT ARGC,CHAR *的argv [])
{
    结构SOCKADDR_IN SERVER_ADDR; / *结构来保存服务器的地址* /
    INT socket_d; / *监听套接字描述符* /
    INT口; / *协议端口号* /
    INT请将option_value; / *需要的setsockopt * /
    工业贸易署的pthread_t [TOTAL_CLIENTS]
    端口=(argc个→1)的atoi(argv的[1]):DEFAULT_PORT;    / *套接字服务器地址结构* /
    memset的((字符*)及SERVER_ADDR,0,sizeof的(SERVER_ADDR));
    server_addr.sin_family = AF_INET; / *设置家庭网络* /
    server_addr.sin_addr.s_addr = INADDR_ANY; / *设置本机IP地址​​ /
    server_addr.sin_port = htons((u_short)端口); / *设置端口* /    / *创建插座* /
    如果((socket_d =插座(PF_INET,SOCK_STREAM,0))℃,){
        fprintf中(标准错误,套接字创建失败\\ n);
        出口(1);
    }    / *使监听套接字的端口可重复使用* /
    如果(setsockopt的(socket_d,SOL_SOCKET,SO_REUSEADDR,(字符*)及请将option_value,
                的sizeof(请将option_value))≤; 0){
        fprintf中(标准错误,setsockopt的失败\\ n);
        出口(1);
    }    / *绑定本地地址的socket * /
    如果(绑定(socket_d,(结构sockaddr *)及SERVER_ADDR,sizeof的(SERVER_ADDR))小于0){
        fprintf中(标准错误,绑定失败\\ n);
        出口(1);
    }    / *指定请求队列的大小* /
    如果(听(socket_d,LISTEN_QUEUE_LIMIT)小于0){
        fprintf中(标准错误,倾听失败\\ n);
        出口(1);
    }    memset的(connected_clients,0,sizeof的(INT)* TOTAL_CLIENTS);    为(;;)
    {
        结构SOCKADDR_IN client_addr; / *结构来保存客户端的地址* /
        INT阿伦= sizeof的(client_addr); / * *地址长度/
        诠释SD; / *连接套接字描述符* /        如果((SD =接受(socket_d,(结构sockaddr *)及client_addr,&安培;阿伦))小于0)
        {
            PERROR(接受失败\\ n);
            出口(1);
        }
        其他的printf(\\ n我从(%s和%D)\\ n连接,INET_NTOA(client_addr.sin_addr),ntohs和(client_addr.sin_port));        如果(在pthread_create(安培;!TID [current_client],NULL,(void *的)Client_Handler的,(无效*)SD)= 0)
        {
            PERROR(pthread_create的错误);
            继续;
        }
        connected_clients [current_client] = SD;
        current_client ++; / *递增客户编号* /
    }    返回0;
}无效* Client_Handler的(无效* connected_socket)
{
    诠释SD;
    SD =(INT)connected_socket;
    对于(;)
    {
        ssiz​​e_t供N;
        字符缓冲区[CHAR_BUFFER]
        对于(;)
        {
            如果(N =读取(SD,缓冲区,sizeof的(字符)* CHAR_BUFFER)== -1)
            {
                PERROR(错误从客户读);
                了pthread_exit(1);
            }
            INT I = 0;
            对于(i = 0; I< current_client;我++)
            {
                如果(写(connected_clients [I],缓冲液,的sizeof(char)的* CHAR_BUFFER)== - 1)
                    PERROR(错误消息发送到客户端,而多播);
            }
        }
    }
}

我的客户端是这样的(马爷是无关的,而回答我的问题)

 的#include<&stdio.h中GT;
#包括LT&; SYS / types.h中>
#包括LT&; SYS / socket.h中>
#包括LT&; netinet / in.h中>
#包括LT&;&netdb.h中GT;
#包括LT&;&string.h中GT;
#包括LT&;&stdlib.h中GT;无效的错误(字符* MSG)
{
    PERROR(MSG);
    出口(0);
}无效* listen_for_message(void *的FD)
{
    INT的sockfd =(INT)FD;
    INT N;
    字符缓冲区[256];
    bzero(缓冲液,256);
    的printf(你的留言:);
    fflush(标准输出);
    而(1)
    {
        N =读(的sockfd,缓冲液,256);
        如果(正℃,)
            错误(ERROR从套接字读取);
        如果(正== 0)了pthread_exit(1);
        的printf(\\ n消息发送:%sYOUR消息:,缓冲区);
        fflush(标准输出);
    }
}INT主(INT ARGC,CHAR *的argv [])
{
    INT的sockfd,PORTNO,N;
    结构SOCKADDR_IN serv_addr;
    结构hostent *服务器;
    的pthread_t read_message;
    字符缓冲区[256];
    如果(argc个3;){
        fprintf中(标准错误,使用率%s的主机端口\\ n,argv的[0]);
        出口(0);
    }
    PORTNO =的atoi(argv的[2]);
    的sockfd =插座(AF_INET,SOCK_STREAM,0);
    如果(的sockfd℃,)
        错误(错误打开插座);
    服务器=的gethostbyname(的argv [1]);
    如果(服务器== NULL){
        fprintf中(标准错误,错误,没有这样的主机\\ n);
        出口(0);
    }
    bzero((字符*)及serv_addr,sizeof的(serv_addr));
    serv_addr.sin_family = AF_INET;
    BCOPY((字符*)服务器 - > h_addr,
            (字符*)及serv_addr.sin_addr.s_addr,
            服务器 - >长度h_length);
    serv_addr.sin_port = htons(PORTNO);
    如果(连接(的sockfd,&安培; serv_addr,sizeof的(serv_addr))小于0)
        错误(连接错误);
    bzero(缓冲液,256);
    如果(在pthread_create(安培; read_message,NULL,(void *的)listen_for_message,(无效*)的sockfd)!= 0)
    {
        PERROR(错误创建线程);
    }
    而(1)
    {
        与fgets(缓冲区,255,标准输入);
        N =写(的sockfd,缓冲,256);
        如果(正℃,)
            错误(写入插座);
        bzero(缓冲液,256);
    }
    返回0;
}


解决方案

接受套接字上的连接,你的的recv()之后,将返回0或-1特殊案例。

摘录(3)手册页​​:


  

如果成功完成,的recv()
  应当返还消息的长度
  在字节。如果没有消息可用
  要被接收和对端具有
  执行顺序关机的recv()
  应返回0,否则-1应
  返回,并设置errno以指示
  错误。


所以,如果您的客户端退出摆好,你会从的recv()在某些时候得到0。如果连接被莫名其妙地丢失,还可能得到-1,并检查相应的errno会告诉你,如果连接已失去了一些其他错误发生的。查看更多详细信息在的recv(3)手册页​​

编辑:

我看到你正在使用阅读()。不过,同样的规则与的recv()适用。

努力的write()给你的客户时,您的服务器也失败。如果您的客户端断开连接的write()将返回-1,错误号可能会被设置为 EPIPE 。此外,为您处理,并杀了他,如果你不阻止/忽略这个信号 SIGPIPE 信号会被发送。而你没有,我看,这就是为什么你的服务器时,客户端presses按Ctrl-C终止。按Ctrl-C终止客户端,因此客户端关闭套接字,并让您的服务器的的write()失败。

请参阅mark4o的答案的还有什么可能出错很好详细的解释。

Suppose, I have a connected socket after writing this code..

if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
{
    perror("accept failed\n");
    exit(1);
}

How can I know at the server side that client has exited.

My whole program actually does the following..

  • Accepts a connection from client
  • Starts a new thread that reads messages from that particular client and then broadcast this message to all the connected clients.

If you want to see the whole code... In this whole code. I am also struggling with one more problem that whenever I kill a client with Ctrl+C, my server terminates abruptly.. It would be nice if anyone could suggest what the problem is..

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

/*CONSTANTS*/
#define DEFAULT_PORT 10000 
#define LISTEN_QUEUE_LIMIT 6
#define TOTAL_CLIENTS 10
#define CHAR_BUFFER 256

/*GLOBAL VARIABLE*/
int current_client = 0;
int connected_clients[TOTAL_CLIENTS];
extern int errno;

void *client_handler(void * socket_d);

int main(int argc, char *argv[])
{
    struct sockaddr_in server_addr;/* structure to hold server's address*/
    int    socket_d;             /* listening socket descriptor       */
    int    port;           /* protocol port number              */
    int    option_value;   /* needed for setsockopt             */
    pthread_t tid[TOTAL_CLIENTS];
    port = (argc > 1)?atoi(argv[1]):DEFAULT_PORT;

    /* Socket Server address structure */
    memset((char *)&server_addr, 0, sizeof(server_addr)); 
    server_addr.sin_family = AF_INET;               /* set family to Internet */
    server_addr.sin_addr.s_addr = INADDR_ANY;       /* set the local IP address */
    server_addr.sin_port = htons((u_short)port);    /* Set port */

    /* Create socket */
    if ( (socket_d = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        fprintf(stderr, "socket creation failed\n");
        exit(1);
    }

    /* Make listening socket's port reusable */
    if (setsockopt(socket_d, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value, 
                sizeof(option_value)) < 0) {
        fprintf(stderr, "setsockopt failure\n");
        exit(1);
    }

    /* Bind a local address to the socket */
    if (bind(socket_d, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        fprintf(stderr, "bind failed\n");
        exit(1);
    }

    /* Specify size of request queue */
    if (listen(socket_d, LISTEN_QUEUE_LIMIT) < 0) {
        fprintf(stderr, "listen failed\n");
        exit(1);
    }

    memset(connected_clients,0,sizeof(int)*TOTAL_CLIENTS);

    for (;;)
    {
        struct sockaddr_in client_addr;    /* structure to hold client's address*/
        int    alen = sizeof(client_addr); /* length of address                 */
        int    sd;                /* connected socket descriptor */

        if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
        {
            perror("accept failed\n");
            exit(1);
        }
        else printf("\n I got a connection from (%s , %d)\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));

        if (pthread_create(&tid[current_client],NULL,(void *)client_handler,(void *)sd) != 0)
        {
            perror("pthread_create error");
            continue;
        }
        connected_clients[current_client]=sd;
        current_client++; /*Incrementing Client number*/
    }

    return 0;
}

void *client_handler(void *connected_socket)
{
    int sd;
    sd = (int)connected_socket;
    for ( ; ; ) 
    {
        ssize_t n;
        char buffer[CHAR_BUFFER];
        for ( ; ; )
        {
            if (n = read(sd, buffer, sizeof(char)*CHAR_BUFFER) == -1)
            {
                perror("Error reading from client");
                pthread_exit(1);
            }
            int i=0;
            for (i=0;i<current_client;i++)
            {
                if (write(connected_clients[i],buffer,sizeof(char)*CHAR_BUFFER) == -1)
                    perror("Error sending messages to a client while multicasting");
            }
        }
    }
}

My client side is this (Maye be irrelevant while answering my question)

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

void error(char *msg)
{
    perror(msg);
    exit(0);
}

void *listen_for_message(void * fd)
{
    int sockfd = (int)fd;
    int n;
    char buffer[256];
    bzero(buffer,256);
    printf("YOUR MESSAGE: ");
    fflush(stdout);
    while (1)
    {
        n = read(sockfd,buffer,256);
        if (n < 0) 
            error("ERROR reading from socket");
        if (n == 0) pthread_exit(1);
        printf("\nMESSAGE BROADCAST: %sYOUR MESSAGE: ",buffer);
        fflush(stdout);
    }
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    pthread_t read_message;
    char buffer[256];
    if (argc < 3) {
        fprintf(stderr,"usage %s hostname port\n", argv[0]);
        exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
            (char *)&serv_addr.sin_addr.s_addr,
            server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");
    bzero(buffer,256);
    if (pthread_create(&read_message,NULL,(void *)listen_for_message,(void *)sockfd) !=0 )
    {
        perror("error creating thread");
    }
    while (1)
    {
        fgets(buffer,255,stdin);
        n = write(sockfd,buffer,256);
        if (n < 0) 
            error("ERROR writing to socket");
        bzero(buffer,256);
    }
    return 0;
}

解决方案

After accepting the connection, your recv() on the socket will return 0 or -1 in special cases.

Excerpt from recv(3) man page:

Upon successful completion, recv() shall return the length of the message in bytes. If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0. Otherwise, -1 shall be returned and errno set to indicate the error.

So, if your client exited gracefully, you will get 0 from recv() at some point. If the connection was somehow lost, you may also get -1 and checking for appropriate errno would tell you if the connection was lost of some other error occured. See more details at recv(3) man page.

Edit:

I see that you are using read(). Still, the same rules as with recv() apply.

Your server can also fail when trying to write() to your clients. If your client disconnects write() will return -1 and the errno would probably be set to EPIPE. Also, SIGPIPE signal will be send to you process and kill him if you do not block/ignore this signal. And you don't as I see and this is why your server terminates when client presses Ctrl-C. Ctrl-C terminates client, therefore closes client socket and makes your server's write() fail.

See mark4o's answer for nice detailed explanation of what else might go wrong.

这篇关于如何了解客户的插座已终止的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆