【问题标题】:C pthreads send()ing and recv()ing on a socket. Worked separately but not together. Won't quitC pthreads send()ing 和 recv()ing 在套接字上。单独工作,但没有一起工作。不会退出
【发布时间】:2013-03-08 23:01:47
【问题描述】:

为了满足我对 C 知识的渴望,在连接到我的家庭网络的两个 linux 机器上,我正在编写一种骨架 telnet,其中包含 send()s 和 recv()s 字符串(只是为了获得一些套接字经验和线程)。服务器侦听,客户端连接并从stdin 发送字符串。我让它们工作,然后我将它们更改为实现 pthreads 并且线程版本工作。最后,我将两者放在一个程序中,这样连接的任一端都可以(理论上)发送和接收字符串。客户端和服务器都使用strstr() 监视"quit" 然后退出。正如这篇文章的标题所暗示的,当我把它们放在一起时,组合版本会发送字符串,但它不会在它应该退出的时候退出。我不确定出了什么问题。我尝试使用 gdb 逐步完成它,但我对 gdb 太缺乏经验,无法判断发生了什么。

那么,它为什么不退出呢?

退一步说,有没有更好的方法来实现我正在尝试做的事情?

感谢您的帮助。

clientserver.c

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

int sockfd = 0, send_running = 1, recv_running = 1, status = 0, acptsockfd = 0;
char str_to_send[200], str_rcvd[200];
char *remote_host_addr_str = NULL;
struct sockaddr_in remote_addr, listening_addr;

void *sender(void *threadid);

void *receiver(void *threadid);

int main(int argc, char *argv[])
{
pthread_t threads[2];
long t = 0;

memset(&remote_addr, 0, sizeof remote_addr);
memset(&listening_addr, 0, sizeof listening_addr);
str_to_send[0] = '\0';
str_rcvd[0] = '\0';
if(argc != 2)
{
    fprintf(stderr, "\n Usage: %s <IP of host to connect to> \n", argv[0]);
    return 1;
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
    fprintf(stderr, "\n Socket Error %s\n", strerror(errno));
    return 1;
}
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(1234);
remote_host_addr_str = argv[1];
if(inet_pton(AF_INET, argv[1], &remote_addr.sin_addr)<=0)
{
    fprintf(stderr, "\n inet_pton error \n");
    return 1;
}
listening_addr.sin_addr.s_addr = htonl(INADDR_ANY);
listening_addr.sin_port = htons(1234);
status = pthread_create(&threads[t], NULL, receiver, (void *)t);
if(status)
{
    fprintf(stderr, "Error: pthread_create(receiver) returned %d\n", status);
    exit(-1);
}
status = pthread_create(&threads[t+1], NULL, sender, (void *)t);
if(status)
{
    fprintf(stderr, "Error: pthread_create(sender) returned %d\n", status);
    exit(-1);
}
while(send_running && recv_running)
    continue;
pthread_exit(NULL);
return 0;
}

void *sender(void *threadid)
{
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof remote_addr) == -1)
{
    fprintf(stderr, "socket error %s", strerror(errno));
    send_running = 0;
}

while(1)
{
    fgets(str_to_send, sizeof str_to_send, stdin);
    send(sockfd, str_to_send, sizeof str_to_send, 0);
    if((strstr(str_to_send, "quit")) || strstr(str_rcvd, "quit"))
    {
        send_running = 0;
        recv_running = 0;
        pthread_exit(NULL);
        break;
    }
}

send_running = 0;
}

void *receiver(void *threadid)
{
bind(sockfd, (struct sockaddr*)&listening_addr, sizeof listening_addr);
listen(sockfd, 5);
acptsockfd = accept(sockfd, (struct sockaddr *)NULL, NULL);
while(1)
{
    recv(acptsockfd, str_rcvd, sizeof str_rcvd, 0);
    if(str_rcvd[0] != '\0') 
        printf("%s", str_rcvd);
    if(strstr(str_rcvd, "quit"))
    {
        close(acptsockfd);
        recv_running = 0;
        send_running = 0;
        pthread_exit(NULL);
        break;
    }
}   
recv_running = 0;
}

【问题讨论】:

    标签: c sockets pthreads


    【解决方案1】:

    来自pthread_exitsynopsis

    当线程不是 首次调用main() 的线程从头开始返回 用于创建它的例程。函数的返回值服务 作为线程的退出状态。

    您不必要地拨打pthread_exit()。如果您能够正常从函数返回,那么线程将正确完成。如果可以的话,我宁愿从函数中返回。

    我想你会发现send_runningrecv_running 标志是多余的。基本上,如果发送和接收函数都循环直到它们达到退出条件(发送或接收“退出”),然后它们返回,那么主函数应该能够等待其他两个线程。看看pthread_join。这将消除 main 函数中的忙碌等待(循环 send_running &amp;&amp; recv_running)。

    至于为什么这个过程没有结束?我认为接收器函数永远不会退出,因此在所有线程完成之前该过程不会结束。接收器功能仅检查是否收到“退出”。如果你发送“quit”,sender函数会正常退出,main函数也会正常退出,但是receiver会一直等待接收“quit”的值。

    【讨论】:

    • 我相信你是对的。我开始在发送函数中检查!recv_running 和在接收函数中检查!send_running,它开始正常退出。我也会尝试实施您的其他建议。谢谢!
    【解决方案2】:

    您不应该使用同一个套接字进行监听和连接。使用两个套接字。

    【讨论】:

    • 这是不正确的。套接字可以发送和接收,但如果接收端阻塞,最好使用线程(或 fork)让发送端处于活动状态。
    • 发送和接收完全没问题,但监听和连接不正确。仔细阅读他的代码。套接字无法连接到自身。
    • 感谢您指出这一点。我需要一段时间才能弄清楚出了什么问题。
    【解决方案3】:

    这是我尝试做的固定代码。

    /*
    * clientserver.c -- send and receive strings over a socket using threads
    */
    
    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/select.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <errno.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <stdlib.h>
    
    const long MYSENDER = 0;    // send thread ID
    const long MYRECVR = 1;     // recv thread ID
    int sockfd = 0, out_sockfd = 0, status = 0, acptsockfd = 0, fdmax = 0;  // socket file descriptors, exit status, largest file descriptor
    char str_to_send[200], str_rcvd[200];   // send and receive buffers
    char *remote_host_addr_str = NULL;  // IP address of host to connect to from command line argument
    struct sockaddr_in remote_addr, listening_addr; // remote host and listening socket params
    fd_set master_fdset;    // file descriptor set for select()
    unsigned char flags = 0;    // operating conditions
    const unsigned char ACCEPTED_CONNECTION = 1;    // the receive function has accepted a connection
    const unsigned char SEND_RUNNING = 1<<1;    // the send function is running
    const unsigned char RECV_RUNNING = 1<<2;    // the receive function is running
    pthread_mutex_t flag_mutex; // so all threads can safely read & write the flags variable
    
    void *sender(void *threadid);
    
    void *receiver(void *threadid);
    
    int main(int argc, char *argv[])
    {
        FD_ZERO(&master_fdset); // initialize file descriptor set
        pthread_t threads[2];   // two threads: send and receive
    
        pthread_mutex_init(&flag_mutex, NULL);  // initialize flags mutex
        memset(&remote_addr, 0, sizeof remote_addr);    // initialize to zero
        memset(&listening_addr, 0, sizeof listening_addr);  // initialize to zero
        str_to_send[0] = '\0';  // initialize to NULL char
        str_rcvd[0] = '\0';     // initialize to NULL char
        if(argc != 2)   // expecting an IP address
        {
            fprintf(stderr, "\n Usage: %s <IP of host to connect to> \n", argv[0]);
            return 1;
        }
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  // create listening socket and check for error
        {
            fprintf(stderr, "\n socket() error %s\n", strerror(errno));
            return 1;
        }
        if((out_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  // create sending socket and check for error
        {
            fprintf(stderr, "\n socket() Error %s\n", strerror(errno));
            return 1;
        }
        /* fill in details about remote host socket */
        remote_addr.sin_family = AF_INET;
        remote_addr.sin_port = htons(1234);
        remote_host_addr_str = argv[1];
        if(inet_pton(AF_INET, argv[1], &remote_addr.sin_addr)<=0)
        {
            fprintf(stderr, "\n inet_pton error \n");
            return 1;
        }
        /* fill in details about listening socket */
        listening_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        listening_addr.sin_port = htons(1234);
        status = pthread_create(&threads[MYRECVR], NULL, receiver, (void *)MYRECVR);    // start the server thread and check for error
        if(status)
        {
            fprintf(stderr, "Error: pthread_create(receiver) returned %d\n", status);
            exit(-1);
        }
        pthread_mutex_lock(&flag_mutex);
        flags |= RECV_RUNNING;  // server thread is running
        pthread_mutex_unlock(&flag_mutex);
        sleep(1); // wait to see if an incoming connection was accepted
        pthread_mutex_lock(&flag_mutex);
        if(flags & ACCEPTED_CONNECTION) //received an incoming connection
            out_sockfd = acptsockfd;
        pthread_mutex_unlock(&flag_mutex);
        status = pthread_create(&threads[MYSENDER], NULL, sender, (void *)MYSENDER);    // start the client thread and check for error
        if(status)
        {
            fprintf(stderr, "Error: pthread_create(sender) returned %d\n", status);
            exit(-1);
        }
        pthread_mutex_lock(&flag_mutex);
        flags |= SEND_RUNNING;  // client thread is running
        pthread_mutex_unlock(&flag_mutex);
        pthread_join(threads[MYRECVR], NULL);   // main() will wait for the server thread to complete
        pthread_join(threads[MYSENDER], NULL);  // main() will wait for the client thread to complete
    
        return 0;
    }
    
    void *sender(void *threadid)
    {
        int c;  // loop counter
        fprintf(stderr, "Connecting to %s\n", remote_host_addr_str);
        for(c = 0; c < 12; ++c)
        {
            if (connect(out_sockfd, (struct sockaddr *)&remote_addr, sizeof remote_addr) == -1) // connect to the remote host.  Retry every 5 sec for 1 min
            {
                fprintf(stderr, "Send socket error: %s\nRetrying in 5 seconds.  %d tries remaining.\n", strerror(errno), (11 - c));
                int d;
                /* show the user a countdown to next retry on the screen */
                fprintf(stderr, " ");
                for(d=5; d>0; --d)
                {
                    fprintf(stderr, "\b%d", d);
                    sleep(1);
                }
                fprintf(stderr, "\b \b");
                if(c < 11)
                    continue;
                else    // failed to connect to remote host.  Shutdown client thread
                {
                    pthread_mutex_lock(&flag_mutex);
                    flags &= !SEND_RUNNING;
                    pthread_mutex_unlock(&flag_mutex);
                    return (int*)1;
                }
            }
            else
            {
                fprintf(stderr, "Connected!\n");
                c += 12;
            }
        }
    
        while(1)
        {
            if(fgets(str_to_send, sizeof str_to_send, stdin) == NULL)   // get input from stdin.  Shutdown client thread on error
                goto shutdown_send_function;
            if((status = send(out_sockfd, str_to_send, strlen(str_to_send)+2, 0)) == -1)    // send the input from stdin and check for error
                fprintf(stderr, "send() error : %s\n", strerror(errno));
            pthread_mutex_lock(&flag_mutex);
            status = (flags & RECV_RUNNING);    // make sure the server thread is still running
            pthread_mutex_unlock(&flag_mutex);
            if((strstr(str_to_send, "quit")) || !status)    // shutdown if the message contains "quit" or the server thread stopped
            {
    shutdown_send_function:
                pthread_mutex_lock(&flag_mutex);
                flags &= !SEND_RUNNING;
                pthread_mutex_unlock(&flag_mutex);
                if(out_sockfd != acptsockfd)    // if the sending socket is different than the accepted socket
                    if((status = close(sockfd)) == -1)  // close the sending socket
                        fprintf(stderr, "close() error : %s\n", strerror(errno));
                break;
            }
        }
        return 0;
    }
    
    void *receiver(void *threadid)
    {
        int opt = 1;
    
        setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        if(bind(sockfd, (struct sockaddr*)&listening_addr, sizeof listening_addr) == -1)    // bind the listening socket and check for error
            fprintf(stderr, "bind() error : %s\n", strerror(errno));
        fprintf(stderr, "Waiting for incoming connection\n");
        if(listen(sockfd, 5) == -1) // listen for incoming connections
            fprintf(stderr, "listen() error : %s\n", strerror(errno));
        FD_SET(sockfd, &master_fdset);  // add the listening socket to the file descriptor set
        fdmax = sockfd; // keep track of the largest file descriptor for select()
        if((acptsockfd = accept(sockfd, (struct sockaddr *)NULL, NULL)) == -1)  // accept incoming connection request and check for error
            fprintf(stderr, "accept() error : %s\n", strerror(errno));
        FD_SET(acptsockfd, &master_fdset);  // add accepted socket to file descriptor set
        if(acptsockfd > fdmax)  // keep track of the largest file descriptor for select()
            fdmax = acptsockfd;
        pthread_mutex_lock(&flag_mutex);
        flags |= ACCEPTED_CONNECTION;   // a connection has been accepted
        pthread_mutex_unlock(&flag_mutex);
        fprintf(stderr, "Incoming connection detected\n");
        while(1)
        {
            if((status = select(fdmax+1, &master_fdset, 0, 0, NULL)) > 0) // there is data available to be read
            {
                if(recv(acptsockfd, str_rcvd, sizeof str_rcvd, 0) == -1)    // receive the data and check for error
                    fprintf(stderr, "recv() error : %s\n", strerror(errno));
                if(str_rcvd[0] != '\0') 
                    printf("%s", str_rcvd); // print the message received
                pthread_mutex_lock(&flag_mutex);
                status = (flags & SEND_RUNNING);    // check if the client thread is still running
                pthread_mutex_unlock(&flag_mutex);
                if((strstr(str_rcvd, "quit")) || !status)   // shutdown the server thread if message contains "quit" or client thread stopped
                {
                    if((status = close(acptsockfd)) == -1)  // close the accepted socket
                        fprintf(stderr, "close() error : %s\n", strerror(errno));
                    pthread_mutex_lock(&flag_mutex);
                    flags &= !RECV_RUNNING;
                    pthread_mutex_unlock(&flag_mutex);
                    break;
                }
            }
            if(status == -1)
                fprintf(stderr, "select() error : %s\n", strerror(errno));
        }
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多