C pthreads在套接字上发送()ing和recv()。 分开工作但不在一起工作。 不会退出

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

那么,为什么不退出呢?

为了退一步,有没有更好的方法来实现我想要做的事情?

谢谢你的帮助。

clientserver.c

 #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  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  \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; } 

来自pthread_exit 简介

当首次调用main()的线程以外的线程从用于创建它的start例程返回时,对pthread_exit()进行隐式调用。 函数的返回值用作线程的退出状态。

你不必要地调用pthread_exit() 。 如果您能够正常返回函数,则线程将正确完成。 如果可以,我宁愿只从函数返回。

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

至于为什么这个过程没有结束? 我认为接收器function不会退出,因此在所有线程完成之前,该过程不会结束。 接收器function仅检查是否收到“退出”。 如果你发送“退出”,发送者function将正常退出,主要是,但接收器将持续等待接收值“退出”。

您不应该使用相同的套接字来进行监听和连接。 使用两个sockets。

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

 /* * clientserver.c -- send and receive strings over a socket using threads */ #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  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  \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; }