2013-11-23 76 views
6

我有一個客戶端連接到服務器(TCP連接)。在服務器崩潰的情況下(我斷開連接),我的客戶端需要連接到另一臺服務器才能繼續服務。但是當第一臺服務器回來時,我需要再次將客戶端重新連接到它。 第一臺服務器崩潰後,我能夠將客戶端連接到備份服務器,但是我的客戶端重新連接到第一臺服務器時出現問題。我做了一個函數create_newconnect()重新連接到服務器,但它不起作用(這就是爲什麼我沒有在代碼中調用它) 我試圖儘可能簡化我的程序,所以它不會以大 這是一個客戶端崩潰後恢復到服務器的連接

#include <stdlib.h> 
#include <stdio.h> 
#include <ctype.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <signal.h> 
#include <string.h> 
#include <arpa/inet.h> 
#include <time.h> 
#define SIZE sizeof(struct sockaddr_in) 


struct sockaddr_in server; 
void tcp_protocol();//execute client tcp protocol 
void server_check(); 
void tcp(); 
void create_newconnect(); 

int main (int argc, char *argv[]) 
{ 

    int portno; 

    //Test for correct number of arguments 
    if (argc != 3) 
    { 
     fprintf(stderr, "Usage: %s Port# IP Address \n", argv[0]); 
     exit(1); 
    } 
    portno = atoi(argv[1]);//convert port # to int 
    server.sin_family = AF_INET; 
    server.sin_port = htons(portno); 
    server.sin_addr.s_addr = inet_addr(argv[2]);//use client ip address 
    tcp();//call tcp function 
    return 0; 
} 
void tcp() 
{ 
    int sockfd; 
    char c ; 
    //create socket 
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0))==-1) 
    { 
     perror ("socket call faild"); 
     exit (1); 
    } 
    //connect to the server 
    if (connect (sockfd, (struct sockaddr *)&server, SIZE)==-1) 
    { 
     perror ("connect call faild"); 
     exit (1); 
    } 
    while(1) 
    { 
    printf("Enter char\n"); 
    scanf("%c",&c); 
     server_check(sockfd); 
     //send packet to server 
     if (send(sockfd, &c, sizeof(c),0)<0) 
     { 
     printf("error sending\n"); 
     } 
     //if packet is received from server 
     if(recv(sockfd, &c, sizeof(c),0)>0) 
     { 
      printf("server's respond %c\n", c);//print result 
     } 
    } 
close(sockfd); 
} 


void server_check(int sock) 
{ 
    char b ='b'; 
    //send packet to server 
    if (send(sock, &b, sizeof(b),0)<0) 
     printf("error sending\n"); 
    //if packet is received from server 
    if((recv(sock, &b, sizeof(b),0)>0)) 
    { 
     printf("server responded\n"); 

    } 
    else//if server is not responding 
    { 
    printf("server crashed\n"); 
     close(sock);//close socket 
     server.sin_port = htons(5002); 
     server.sin_addr.s_addr = inet_addr("127.0.0.1"); 
     tcp();//create new connection 
    } 


} 
void create_newconnect() 
{ 
    int newsockfd; 
    server.sin_port = htons(5001); 
    //create socket 
    if ((newsockfd = socket(AF_INET, SOCK_STREAM, 0))==-1) 
    { 
     perror ("socket call faild"); 
     exit (1); 
    } 
    //connect to the server 
    if (connect (newsockfd, (struct sockaddr *)&server, SIZE)==-1) 
    { 
     perror ("connect call faild"); 
     exit (1); 
    } 
     tcp();//call function to execute tcp protocol 

} 
+1

瞭解什麼不適合您以及您看到的診斷信息會很有幫助。我可以在代碼中看到許多問題,但是使其對抗我實施的測試服務可能無法解決您的問題。你的真實情況是否有客戶端,並且主服務器和備份服務器都位於同一主機上,還是你的測試版本的工件?您是否希望修復此測試程序,或者回答如何解決在主服務器和備份服務器之間自動失敗的客戶端原始問題? – gwaigh

回答

6

我認爲你將不得不考慮的第一件事是:後第一個服務器已經崩潰了,您的客戶端已成功地重新連接到備份服務器,怎麼會有你客戶端知道第一臺服務器已經重新上線了嗎?

我可以想到兩種可能性:一種可能是備份服務器可能會通知客戶端主服務器的重新出現(例如,通過TCP連接發送某種PRIMARY_SERVER_ONLINE消息,或者可能只是通過關閉TCP連接,期望這會導致客戶端再次嘗試連接到主服務器)。

另一種方法是使你的客戶足夠聰明,它可以定期(每分鐘一次例如)儘量儘管它使用TCP連接到備份服務器重新連接到主服務器。這是可行的,但不是用單個線程和阻止I/O像你的發佈代碼一樣...(因爲如果你的程序在recv()調用中被阻塞,它沒有辦法做任何事情,比如嘗試連接一個TCP連接)。您需要使用非阻塞I/O和select()(或類似),或異步I/O或多線程,才能正確執行。

+1

從帶寬的角度來看,第一個選項幾乎肯定會更好。這樣,只有備份服務器正在輪詢主服務器。主服務器恢復後會不會同步嗎?如果是這樣,它可能需要在恢復服務之前先與備份進行通話。如果不是那麼你的服務器基本上是無狀態的?如果是這樣,你爲什麼有一個主要和備份......他們只是同齡人。在這種情況下,您應該努力讓客戶因爲負載原因分佈在他們之間,並在出現問題時緩解壓力。 – Speed8ump

+0

@JeremyFriesner我會嘗試第二種方法,我認爲在將消息發送到備份服務器之前,我會嘗試連接到主服務器。你也可以詳細說明阻止我擁有的I/O,我對此不是很熟悉。謝謝 – swiftk

+0

@swiftk套接字默認情況下是在阻塞模式下創建的,這意味着如果send()/ recv()被調用時他們不能發送/ recv數據,那麼send()/ recv()調用將不會返回,直到有一些數據發送/接收(即可能不會很長時間)。如果您希望這些調用不會阻塞(即,在您等待套接字準備好發送/ recv時,您的線程可以做其他事情),那麼您可以在創建套接字後將套接字設置爲非阻塞模式,通過fcntl(fd,F_SETFL,O_NONBLOCK);或者在Windows下,它的無符號長m = 1; ioctlsocket(fd,FIONBIO,&m); –

0

重新連接後,您的程序遞歸調用tcp()。這幾乎肯定是不正確的,並且會導致在每次斷開連接時使用資源(主要是堆棧)。

你需要避免的代碼按值傳遞套接字文件描述符(的sockfd)的功能,因爲它會每個新的連接後更改。

作爲一般原則,你可以在優先順序(兩個或更多)的主機列表。然後,在任何時候嘗試創建與那些比您當前有連接的偏好更高的偏好的連接。然後,建立連接時,關閉所有其他打開的會話,然後切換到新的首選連接。

保留此封裝並讓它返回當前活動的sockfd供所有其他功能使用。

相關問題