简体   繁体   English

C pthread在套接字上进行send()ing和recv()ing。 单独工作但不一起工作。 不会退出

[英]C pthreads send()ing and recv()ing on a socket. Worked separately but not together. Won't quit

In order to slake my thirst for C knowledge, on two linux boxes connected to my home network, I'm writing kind of a skeleton telnet that send() s and recv() s strings (just for some experience with sockets and threads). 为了减轻对C知识的渴望,在连接到家庭网络的两个linux机器上,我正在编写一种类似于telnet的骨架,该telnet send()recv()字符串(仅对于套接字和线程有一定的经验) 。 The server listens and the client connects and sends strings from stdin . 服务器侦听,客户端连接并从stdin发送字符串。 I got those to work then I changed them to implement pthreads and the threaded versions worked. 我让那些代码可以工作,然后我将它们更改为实现pthreads ,而线程版本也可以正常工作。 Last, I put the two together into one program so that either end of the connection could (in theory) send and receive strings. 最后,我将两者放到一个程序中,以便连接的任何一端(理论上)都可以发送和接收字符串。 Both the client and server use strstr() to watch for "quit" and then quit. 客户端和服务器都使用strstr()监视"quit" ,然后退出。 As the title of this post implies, when I put it all together, the combined version will send strings but it doesn't quit when it is supposed to. 就像这篇文章的标题所暗示的那样,当我将它们放在一起时,合并的版本将发送字符串,但是在应该的情况下不会退出。 I'm not sure what went wrong. 我不确定出了什么问题。 I tried to step through it with gdb but I'm just too inexperienced with gdb and couldn't tell what is happening. 我尝试使用gdb逐步解决问题,但是对gdb太缺乏经验了,无法知道发生了什么。

So, why won't it quit? 那么,为什么不退出呢?

To kind of take a step back, is there a better way to implement what I'm trying to do? 回想一下,是否有更好的方法来实现我要执行的操作?

Thanks for any help. 谢谢你的帮助。

clientserver.c 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;
}

From the pthread_exit synopsis 来自pthread_exit 简介

An implicit call to pthread_exit() is made when a thread other than the thread in which main() was first invoked returns from the start routine that was used to create it. 当除最初调用main()的线程以外的其他线程从用于创建它的启动例程返回时,将隐式调用pthread_exit() The function's return value serves as the thread's exit status. 该函数的返回值用作线程的退出状态。

You are calling pthread_exit() unnecessarily. 您不必要地调用pthread_exit() If you're able to return from your function normally, then the thread will finish correctly. 如果您能够正常从函数返回,则线程将正确完成。 I would prefer to just return from the function if you can. 如果可以的话,我宁愿从函数中返回。

I think you'll find that the send_running and recv_running flags are superfluous. 我认为您会发现send_runningrecv_running标志是多余的。 Basically, if both the send and receive functions loop until they reach their exit condition ("quit" was sent or received), then they return, then the main function should be able to wait on the other two threads. 基本上,如果发送和接收函数都循环直到它们达到退出状态(发送或接收“退出”),然后它们返回,则主函数应该能够在其他两个线程上等待。 Look at pthread_join . 看一下pthread_join This will eliminate the busy-waiting (looping on send_running && recv_running ) in your main function. 这将消除主函数中的繁忙等待(在send_running && recv_running上循环)。

As to why the process doesn't end? 至于为什么这个过程没有结束? I don't think the receiver function is ever exiting, so the process won't end until all threads are finished. 我认为接收器功能永远不会退出,因此该过程直到所有线程完成后才会结束。 The receiver function is only checking to see if "quit" was received. 接收器功能仅检查是否已收到“退出”。 If you send "quit", the sender function will quit normally, as will main, but receiver will continually wait to receive the value "quit". 如果您发送“ quit”,则发送方函数将正常退出,如main一样,但是接收方将继续等待接收值“ quit”。

You shouldn't use the same socket to do listen and connect. 您不应使用相同的套接字进行监听和连接。 Use two sockets. 使用两个插座。

This is the fixed code for what I was trying to do. 这是我尝试执行的固定代码。

/*
* 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM