[英]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 whichmain()
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_running
和recv_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.