为了消除我对C知识的渴望,在连接到我的家庭网络的两个linux盒子上,我正在写一种send()
和recv()
s字符串的骨架telnet(仅供参考)一些套接字和线程的经验)。服务器侦听,客户端连接并从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;
}
答案 0 :(得分:1)
来自pthread_exit
synopsis
当一个线程不是时,对
pthread_exit()
进行隐式调用 首次调用main()
的线程从头开始返回 用于创建它的例程。函数的返回值起作用 作为线程的退出状态。
您正在不必要地致电pthread_exit()
。如果您能够正常返回函数,则线程将正确完成。如果可以,我宁愿从函数返回。
我认为你会发现send_running
和recv_running
标志是多余的。基本上,如果发送和接收函数都循环,直到它们达到退出条件(“退出”被发送或接收),然后它们返回,那么主函数应该能够在其他两个线程上等待。看看pthread_join
。这将消除主函数中的忙等待(send_running && recv_running
循环)。
至于为什么这个过程没有结束?我不认为接收器功能永远存在,因此在所有线程完成之前,该过程不会结束。接收器功能仅检查是否收到“退出”。如果你发送“退出”,发送者功能将正常退出,主要是,但接收器将继续等待接收值“退出”。
答案 1 :(得分:0)
您不应该使用相同的套接字来进行侦听和连接。使用两个插座。
答案 2 :(得分: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;
}