2013-03-20 21 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; 
} 
相關問題