2013-08-27 1 views
3

У меня возникла проблема с сервером tcp. Я хотел бы слушать несколько портов, чтобы отвечать на запросы клиентов. Это должно быть своего рода событие. Каждый порт указывает другой тип ответа. Я много читал о epoll, опросе, выборе или многопоточности. Я пытался работать с большим количеством примеров из таких книг, как Unix Network Programming. Но, вероятно, мне нужны несколько ключевых слов триггера. Как я мог начать правильно?Linux TCP Server - прослушивание нескольких портов в C++

Надеюсь, мои вопросы легко понять. Цените каждый ответ!

сужать его вниз ЕСТЬ МОЯ ИДЕЯ ... Я начал думать об этом:

Если я есть «Диспетчер сервера» с большим количеством сервера, я могу сделать это следующим образом?

CreateSockets (ServerList); CheckSockets (SocketList, master_set);

В диспетчере сервера: 1) для цикла, чтобы создать все сокеты сервера (функции: гнездо/setsockopt/IOCTL/привязывать/слушать)

void CreateSockets(map<int,ServerType> ServerList) 
{ 
    fd_set  master_set; 
    map<int,ServerType>::iterator it; 
    map<int,int> SocketList; 
    for (it= ServerList.begin();it!= ServerList.end();it++) 
    { 
     listen_sd = socket(AF_INET, SOCK_STREAM, 0); 
     if (listen_sd < 0) 
     { 
      perror("socket() failed"); 
      exit(-1); 
     } 
     rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, 
       (char *)&on, sizeof(on)); 
     if (rc < 0) 
     { 
      perror("setsockopt() failed"); 
      close(listen_sd); 
      exit(-1); 
     } 
     rc = ioctl(listen_sd, FIONBIO, (char *)&on); 
     if (rc < 0) 
     { 
      perror("ioctl() failed"); 
      close(listen_sd); 
      exit(-1); 
     } 
     memset(&addr, 0, sizeof(addr)); 
     addr.sin_family  = AF_INET; 
     addr.sin_addr.s_addr = htonl(INADDR_ANY); 
     addr.sin_port  = ((*it).second->Port); 
     rc = bind(listen_sd,(struct sockaddr *)&addr, sizeof(addr)); 
     if (rc < 0) 
     { 
      perror("bind() failed"); 
      close(listen_sd); 
      exit(-1); 
     } 
     rc = listen(listen_sd, 32); 
     if (rc < 0) 
     { 
      perror("listen() failed"); 
      close(listen_sd); 
      exit(-1); 
     } 
     SocketList.insert(make_pair(((*it).second->Port),listen_sd)); 
     FD_ZERO(&master_set); 
     max_sd = listen_sd; 
     FD_SET(listen_sd, &master_set); 
    } 
} 

Следующая: 2) Некоторые, как ждать, пока некоторые события в диспетчере сервера (Select со списком гнездовых дескрипторов)

void CheckSockets(map<int,int> SocketList, fd_set master_set) 
{ 
    fd_set working_set; 
    do 
    { 
     memcpy(&working_set, &master_set, sizeof(master_set)); 
     ready_descriptors = select(max_sd + 1, &working_set, NULL, NULL, NULL); 

     if (ready_descriptors >0) 
     { 
      desc_ready = rc; 
      for (i=0; i <= max_sd && desc_ready > 0; ++i) 
      { 
       if (FD_ISSET(i, &working_set)) 
       { 
        desc_ready -= 1; 
        if (i == listen_sd) 
        { 
         do 
         { 
          new_sd = accept(listen_sd, NULL, NULL); 
          if (new_sd < 0) 
          { 
           //error 
          } 
          FD_SET(new_sd, &master_set); 
          if (new_sd > max_sd) 
           max_sd = new_sd; 
         } while (new_sd != -1); 
        } 
        else 
        { 
         do 
         { 
          //Go into server and recv and send (Input Parameter = i) 
          CheckServer(i); 
         } while (TRUE); 
        } 
       } 
      } 
     } 
     else {endserver=true;} 
    }while(endserver=true;) 

} 

3) Заходим на сервер и обработать вопрос (RECV/посыла) ????

void CheckServer(int sd) 
{ 
    rc = recv(sd, buffer, sizeof(buffer), 0); 

    //some stuff in between 

    rc = send(i, buffer, len, 0); 
} 

Могло ли это работать?

Некоторые детали используются и заменяются кодом источника неблокирующего ввода-вывода IBM.


благодарим за вашу помощь. Я смог что-то сделать, НО еще одно не работает.

То, что я сделал до сих пор:

1) consrtuctor отдельного сервера не включает в себя операции сокета. 2) Я могу вернуть идентификатор сокета и сохранить его в диспетчере серверов. 3) Менеджер имеет цикл for, который содержит команду select для проверки любого события в сокетах. 4) если что-то произойдет, все затронутые сокеты будут последовательно воспроизводиться.

Моя проблема:

Он отлично работает, если я всегда подключать и отключать во время я запрашивая данные с сервера. Когда мой клиент настроен таким образом, что соединение будет удерживаться, все блокируется, так как мой код ждет разъединения.

Вот фрагменты кода для каждой части:

1)

 Server::Server() 
    { 

    listen_sd = socket(AF_INET, SOCK_STREAM, 0); 
    ret = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,(char *)&on, sizeof(on)); 
    ret = ioctl(listen_sd, FIONBIO, (char *)&on); 

    memset(&addr, 0, sizeof(addr)); 
    addr.sin_family  = AF_INET; 
    addr.sin_addr.s_addr = htonl(INADDR_ANY); 
    addr.sin_port  = htons(Server_Port); 
    ret = bind(listen_sd,(struct sockaddr *)&addr, sizeof(addr)); 
    ret = listen(listen_sd, 32); 
    Socket = listen_sd; 
} 

2)

Socket= new_Server->GetSocket(); 
SocketList.insert(make_pair(Socket,new_Server->ServerID)); 

3)

while (TRUE) 
    { 
     FD_ZERO(&working_set); 
     for (i=0;i < max_conn;i++) 
{ 
      if (SocketArray[i] >= 0) {FD_SET(SocketArray[i], &working_set);} 
     } 

     ret = select(max_sd+1, &working_set, NULL, NULL, NULL); 

    desc_ready= ret; 
     for (i=0; i <= max_sd && desc_ready > 0; ++i) 
     { 

      if (FD_ISSET(i, &working_set)) //jeder Peer der was hat 
      { 
       desc_ready -= 1; 
//delete all loops to get the correct object 
         (Server).second->DoEvent(i); 

      } 
     } 
    } 

4)

Я просто удаляю обработку ошибок dor listen/bind и т. Д., Чтобы просто сократить код здесь ... Первоначально он там.

+1

Этот вопрос слишком широк, пожалуйста, сузите его к чему-то, на что можно ответить. – Mgetz

+0

вы проверили [Beej's Guide to Network Programming] (http://beej.us/guide/bgnet/) уже? который имеет все основы (в стиле c, хотя). – codeling

+0

Взгляните на шаблон реактора ... – Sambuca

ответ

2

Пример: несколько серверов TCP (как серверные сокеты) прослушивают каждый порт. Затем вы можете использовать дескрипторы select() и pass file для каждого из этих серверных сокетов. Если вы получите какое-либо соединение по любому из них, затем выберите, чтобы вернуть событие чтения и пометить fd сокета сервера, у которого есть соединение. Вам нужно будет вызвать accept() на этом сервере fd.

Вы не можете подключить один сокет TCP к нескольким портам.

+0

Благодарим вас за ответ. Я думал о выборе, но я не хочу, чтобы у вас были таймауты, поэтому мне нужны не блокирующие серверные сокеты. Возможно ли это с выбором? Так как прием действительно подходит? – user2545592

+0

Выберите блокирующий вызов. Вы можете указать тайм-аут в качестве параметра для выбора, но не указывая его (что означает передачу его как NULL) означало бы, что выбор будет тайм-аутом только тогда, когда есть событие (в данном случае это событие чтения). Btw, вы всегда можете иметь несколько потоков, и один поток может сделать выбор для всех фреймов сервера. –

+0

Спасибо. Итак, еще один вопрос. Если у меня есть «Менеджер сервера» с большим количеством Сервера, могу ли я сделать это следующим образом? Внутри диспетчера сервера: 1) для цикла для создания всех сокетов сервера (функции: socket/setsockopt/ioctl/bind/listen) 2) Некоторые из них, как ждать некоторых событий в диспетчере серверов (выберите со списком дескрипторов сокета) 3) Зайдите на сервер и обработайте вопрос (recv/send) ???? – user2545592