2013-01-25 89 views
0

我想實現多客戶端服務器使用C.我已經編寫代碼服務器和客戶端是一個軟件,將不斷髮送一些數據包到服務器。服務器將讀取數據包並對其進行處理。我一次嘗試5個連接到服務器。但是我寫的服務器代碼存在一些問題,因爲我無法將5客戶端連接到服務器。確切的問題是,當客戶端嘗試連接服務器時,它會獲得連接,但是當我關閉客戶端軟件並嘗試重新啓動時,它沒有獲得連接。以下是我的服務器端代碼。有誰能幫我解決這個問題。Multiclient服務器實現在C

#include <ctype.h> 
#include <sys/time.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/utsname.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/select.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 
#include <unistd.h> 
#include <sys/file.h> 


int sock;      /* The socket file descriptor for our "listening"socket */ 
int connectlist[15];   /* Array of connected sockets so we know who we are talking to */ 
fd_set socks;     /* Socket file descriptors we want to wake up for, using select() */ 
int highsock = 1;    /* Highest #'d file descriptor, needed for select() */ 
struct sockaddr_in client_address[5]; 
unsigned int clientLength = sizeof(client_address) ; 
#define PORTNO  (int)49153 
int port;      /* The port number after conversion from ascport */ 
struct sockaddr_in server_address; /* bind info structure */ 
int reuse_addr = 1; 
struct timeval timeout;   /* Timeout for select */ 
int readsocks;     /* Number of sockets ready for reading */ 
int err = 0 ; 

#define BACKLOG   (int)10 

void deal_with_data(int listnum /* Current item in connectlist for for loops */) 
{ 
    //Here I am trying to read packet from client s/w and process it 
} 

void setnonblocking(int sock) 
{ 
    int opts; 
    opts = fcntl(sock,F_GETFL); 
    if (opts < 0) 
    { 
     printf("fcntl(F_GETFL)_error"); 
     exit(0); 
    } 
    opts = (opts | O_NONBLOCK); 
    if (fcntl(sock,F_SETFL,opts) < 0) 
    { 
     printf("fcntl(F_SETFL)_error"); 
     exit(0); 
    } 
    return; 
} 

void build_select_list() 
{ 
    int listnum;   /* Current item in connectlist for for loops */ 
    FD_ZERO(&socks); 
    FD_SET(sock,&socks); 
    for (listnum = 0; listnum < 5; listnum++) 
    { 
     if (connectlist[listnum] != 0) 
     { 
      FD_SET(connectlist[listnum],&socks); 
      if (connectlist[listnum] > highsock) 
       highsock = connectlist[listnum]; 
     } 
    } 
} 

void handle_new_connection() { 
    int listnum;   /* Current item in connectlist for for loops */ 
    int connection; /* Socket file descriptor for incoming connections */ 
    connection = accept(sock, (struct sockaddr *)&client_address[highsock], &clientLength); 
    if (connection < 0) 
    { 
     printf("accept_error"); 
     exit(0); 
    } 
    setnonblocking(connection); 
    for (listnum = 0; (listnum < 5) && (connection != -1); listnum ++) 
     if (connectlist[listnum] == 0) 
     { 
      printf("\nConnection accepted: FD=%d; Slot=%d\n", 
       connection,listnum); 
      printf("Connection accepted from %s\n",inet_ntoa(client_address[highsock].sin_addr)); 
      connectlist[listnum] = connection; 
      connection = -1; 

     } 
    if (connection != -1) 
    { 
     printf("\nNo room left for new client.\n"); 
     write(connection,"Sorry, this server is too busy.Try again later!\r\n",80); 
     close(connection); 
    } 
    printf("return from handle_new_connection\n"); 
} 

void read_socks(void) 
{ 
    int listnum;   /* Current item in connectlist for for loops */ 
    if (FD_ISSET(sock,&socks)) 
     handle_new_connection(); 
    for (listnum = 0; listnum < 5; listnum++) 
    { 
     if (FD_ISSET(connectlist[listnum],&socks)) 
     { 
      //printf("read_socks2\n"); 
      deal_with_data(listnum); 
     } 
    } 
} 

int main (/*int argc, char *argv[]*/) 
{ 
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    if (sock < 0) 
    { 
     printf("socket_error"); 
     exit(EXIT_FAILURE); 
    } 
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr)); 
    setnonblocking(sock); 

    memset((char *) &server_address, 0, sizeof(server_address)); 
    server_address.sin_family = AF_INET; 
    server_address.sin_addr.s_addr = htonl(INADDR_ANY); 
    server_address.sin_port = htons(port); 

    if (bind(sock, (struct sockaddr *) &server_address,sizeof(server_address)) < 0) 
    { 
     printf("bind_error"); 
     close(sock); 
     exit(EXIT_FAILURE); 
    } 

    if((err = listen(sock,10)) == -1) 
    { 
     printf("listen_error"); 
    } 

    highsock = sock; 
    memset((char *) &connectlist, 0, sizeof(connectlist)); 

    while (1) 
    { /* Main server loop - forever */ 
     build_select_list(); 
     timeout.tv_sec = 2; 
     timeout.tv_usec = 0; 

     readsocks = select(highsock+2, &socks, (fd_set *) 0,(fd_set *) 0, &timeout); 
     if (readsocks < 0) 
     { 
      printf("select_error"); 
      exit(EXIT_FAILURE); 
     } 
     if (readsocks == 0) 
     { 
      printf("."); 
      fflush(stdout); 
     } 
     else 
     { 
      read_socks(); 
     } 
    } /* while(1) */ 
} /* main */ 
+0

您需要檢測客戶端何時斷開連接並釋放客戶端放置在「client_address」中。 –

回答

2

您的問題是,當客戶端關閉套接字時,您的主循環不會退出。這意味着它不能接受新的連接。我會用fork()來處理來自socket的數據,並且用main()函數來接受連接和fork()的進程。此外,你需要有一些代碼會殺死fork() ed(即檢查客戶端是否已經在進程中斷開連接)(因爲它本身不會關閉並佔用內存)。

編輯:

好吧,我無法找到你的程序recv()的調用。根據recv(3) man page它將return 0如果客戶端斷開「優雅」和return -1和設置errnoECONNRESET如果客戶端強行斷開連接。爲了使用叉子,我會(在main()功能)包裝while循環在此:

int childpid = fork(); 
if(childpid == -1) { 
     printf("Could not fork process"); 
     exit(EXIT_FAILURE); 
} 
else if(childpid == 0) { /* in child process*/ 
     while(/* check if the socket has been closed */) { 
       /* While loop stuff */ 
     } 
     /* free up memory */ 
     exit(EXIT_SUCCESS); 
} 

而且你的主要功能應該是在一個循環中,等待新的連接。

N.B.我沒有測試任何這些代碼,所以它可能無法正常工作。但是,如果你閱讀手冊頁fork(3)recv(3)它應該工作。

+0

嘿謝謝你的建議,但你能否詳細說明你的答案,因爲我是這個分岔概念的新手。如果你告訴我我應該在哪裏使用這個叉子以及如何使用,會更好。預先感謝。 – user1400571

+0

更新回答舉個例子。 – cyphar

+0

另外,如果您決定使用'read()'而不是'recv()'從套接字讀取,則應用相同的規則。 (請參閱[閱讀(3)手冊頁](http://linux.die.net/man/3/read)瞭解更多信息。 – cyphar

0

您是不是更喜歡使用像Apache這樣的現成產品,並根據需要進行定製?