2013-03-20 25 views
1

为了消除我对C知识的渴望,在连接到我的家庭网络的两个Linux机器上,我正在写一种骨架telnet,send() s和recv() s字符串(仅用于某些socket和线程)。服务器侦听并且客户端连接并发送来自stdin的字符串。我让这些工作,然后我改变他们实施pthreads和螺纹版本工作。最后,我将这两部分放在一个程序中,以便连接的任何一端都可以(理论上)发送和接收字符串。客户端和服务器都使用strstr()来查看"quit",然后退出。正如这篇文章的标题所暗示的,当我将它们放在一起时,组合版本将发送字符串,但在它应该时不会退出。我不确定哪里出了问题。我尝试用gdb来完成它,但我对gdb太缺乏经验,无法分辨发生了什么。C pthreads发送()ing和recv()在套接字上。单独工作但不在一起。不会退出

那么,它为什么不放弃?

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

感谢您的任何帮助。

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

回答

1

pthread_exitsynopsis

pthread_exit()隐式调用时比 其中main()首次调用返回从开始 的线程以外的线程例程,用于创建它。该函数的返回值服务于 作为线程的退出状态。

您打电话pthread_exit()不必要的。如果你能够正常返回你的函数,那么线程将正确完成。如果可以的话,我宁愿只从函数返回。

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

至于为什么过程没有结束?我认为接收函数不会退出,所以直到所有线程都完成后,进程才会结束。接收器功能仅检查是否收到“退出”。如果您发送“退出”,发送者函数将正常退出,但主持人将继续等待接收值“quit”。

+0

我相信你是对的。我开始在send函数中检查'!recv_running',并在receive函数中检查'!send_running',并开始退出。我会尽力实施你的其他建议。谢谢! – JB0x2D1 2013-03-20 02:42:38

0

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

+1

这是不正确的。套接字可以发送和接收,但是如果接收方阻塞,最好使用线程(或* fork *)使发送方处于活动状态。 – 2013-03-20 02:50:48

+0

发送和接收完全正确,但听和连接不正确。仔细阅读他的代码。套接字无法连接到自己。 – 2013-03-20 06:11:41

+0

谢谢你指出。我会花一段时间才能弄清楚什么是错的。 – JB0x2D1 2013-03-21 01:32:27

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; 
} 
相关问题