【问题标题】:restore connection to the server after it crashed崩溃后恢复与服务器的连接
【发布时间】:2013-11-23 02:19:04
【问题描述】:

我有一个客户端连接到服务器(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

 }

【问题讨论】:

  • 了解什么不适合您以及您看到的诊断信息会很有帮助。我可以在代码中看到许多问题,但是使其与我实现的测试服务一起工作可能无法解决您的问题。您的真实情况是否有客户端以及主服务器和备份服务器都驻留在同一主机上,还是您的测试版本的工件?您是否希望修复这个测试程序,或者回答如何解决客户端在主服务器和备用服务器之间自动失败的原始问题?

标签: c sockets networking tcp


【解决方案1】:

我认为您首先要考虑的是:在您的第一台服务器崩溃并且您的客户端已成功重新连接到备份服务器之后,您的客户端如何知道第一台服务器已重新启动行吗?

我可以想到两种可能性:一种可能是备份服务器可能会通知客户端主服务器重新出现(例如通过 TCP 连接发送某种 PRIMARY_SERVER_ONLINE 消息,或者可能只是通过关闭TCP 连接,期望这会导致客户端再次尝试连接到主服务器)。

另一种方法是让您的客户端足够智能,即使它正在使用与备份服务器的 TCP 连接,它也可以定期(例如每分钟一次)尝试重新连接到主服务器。这是可行的,但不能像您发布的代码那样使用单个线程和阻塞 I/O...(因为如果您的程序在 recv() 调用中被阻塞,则它无法执行任何其他操作,例如尝试连接 TCP 连接)。您需要使用非阻塞 I/O 和 select()(或类似方法),或异步 I/O,或多线程,才能正确执行。

【讨论】:

  • 从带宽的角度来看,第一个选项几乎肯定更好。这样,只有备份服务器轮询主服务器。主服务器恢复后会不同步吗?如果是这样,它可能需要在恢复服务之前与备份对话。如果不是,那么您的服务器基本上是无状态的吗?如果是这样,为什么你有一个主要的和一个备份......他们只是对等的。在这种情况下,出于负载原因,您应该尝试让客户端在它们之间分布,并在一个失败时减轻压力。
  • @JeremyFriesner 我会尝试第二种方法,我认为我会在向备用服务器发送消息之前尝试连接到主服务器。您还可以详细说明我所拥有的阻塞 I/O,我对此并不十分熟悉。谢谢
  • @swiftk 套接字默认以阻塞模式创建,这意味着如果在调用 send()/recv() 时它们无法发送/接收数据,则 send()/ recv() 调用将不会返回,直到有一些数据发送/接收(即可能不会很长时间)。如果您希望这些调用永远不会阻塞(即,当您等待套接字准备好发送/接收时,您的线程可以做其他事情),那么您可以在创建套接字后将其设置为非阻塞模式, 通过 fcntl(fd, F_SETFL, O_NONBLOCK);或者在 Windows 下它是 unsigned long m=1; ioctlsocket(fd, FIONBIO, &m);
【解决方案2】:

您的程序在重新连接后递归调用tcp()。这几乎可以肯定是不正确的,并且会导致每次断开连接时都会使用资源(主要是堆栈)。

您需要避免让代码将套接字文件描述符 (sockfd) 按值传递给函数,因为它会在每次新连接后发生变化。

作为一般原则,您可以按优先顺序列出(两个或更多)主机。然后,始终尝试与那些优先级高于您当前连接的人建立连接。然后,在建立连接后,关闭所有其他打开的会话,并切换到新的首选连接。

保持这个封装并让它返回当前活动的 sockfd 以供所有其他函数使用。

【讨论】:

    猜你喜欢
    • 2017-10-28
    • 1970-01-01
    • 2011-03-29
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多