1

C の知識への渇望を和らげるために、ホーム ネットワークに接続された 2 台の Linux ボックスで、send()s とrecv()s 文字列 (ソケットとスレッドの経験が必要なだけ) の一種のスケルトン telnet を作成しています。サーバーがリッスンし、クライアントが接続して から文字列を送信しますstdin。それらを機能させてから、それらを変更して実装するpthreadsと、スレッドバージョンが機能しました。最後に、この 2 つを 1 つのプログラムにまとめて、接続の両端で (理論的には) 文字列を送受信できるようにしました。クライアントとサーバーの両方が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;
}
4

3 に答える 3

1

あらすじよりpthread_exit

pthread_exit()最初に呼び出されたスレッド以外のスレッドが、それmain()を作成するために使用された開始ルーチンから戻ると、暗黙的にが呼び出されます。関数の戻り値は、スレッドの終了ステータスとして機能します。

あなたはpthread_exit()不必要に電話をかけています。関数から正常に戻ることができれば、スレッドは正しく終了します。可能であれば、関数から戻ることをお勧めします。

send_runningおよびrecv_runningフラグが不要であることがわかると思います。基本的に、送信関数と受信関数の両方が終了条件 (「quit」が送信または受信された) に達するまでループした場合、それらは返され、メイン関数は他の 2 つのスレッドで待機できるはずです。を見てくださいpthread_joinsend_running && recv_runningこれにより、メイン関数でのビジー待機 ( のループ) がなくなります。

なぜプロセスが終了しないのですか?レシーバー関数が終了することはないと思うので、すべてのスレッドが終了するまでプロセスは終了しません。レシーバー関数は、「終了」が受信されたかどうかを確認するだけです。「quit」を送信すると、送信側関数はメインと同様に通常どおり終了しますが、受信側は値「quit」の受信を継続的に待機します。

于 2013-03-20T01:53:45.193 に答える
0

これは、私がやろうとしていたことの修正コードです。

/*
* 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;
}
于 2013-03-22T17:50:50.973 に答える
0

リッスンと接続に同じソケットを使用しないでください。ソケットを 2 つ使用します。

于 2013-03-20T02:28:38.100 に答える