2013-08-27 104 views
3

我遇到了tcp服務器的問題。我想傾聽多個端口來回應客戶。它應該是一種基於事件的。每個端口都指示另一種類型的響應。我閱讀了很多關於epoll,poll,select或multithreading的內容。我試圖使用Unix網絡編程等書籍中的很多例子。但可能我需要一些觸發關鍵字。我該如何正確啓動?Linux TCP服務器 - 偵聽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)一些如何等待一些事件在服務器管理器中(選擇與插座描述符列表)

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非阻塞IO源代碼中使用和更改。


謝謝你的一切幫助。我能夠完成某件事,但有一件事仍然沒有奏效。

我做了什麼至今:

1)中的各個服務器的consrtuctor包括插座操作。 2)我能夠返回套接字ID並將其保存在服務器管理器中。 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)

new_sd = accept(new_sd, NULL, NULL); 
    if (new_sd < 0) 
    { 
     if (errno != EWOULDBLOCK) 
     { 
      perror(" accept() failed"); 
     } 
    } 
do 
{ 


    rc = recv(new_sd, buffer, sizeof(buffer), 0); 
//edit datastream and create response 
      rc = send(new_sd, buffer, len, 0); 
     if (rc < 0) 
     { 
      perror(" send() failed"); 
      close_conn = TRUE; 
      break; 
     } 

    }while (TRUE); 

我只是刪除了錯誤處理dor listen/bind等,只是爲了縮短代碼在這裏......原來它在那裏。

+1

這個問題太廣泛了,請把它縮小到可以回答的地方。 – Mgetz

+0

你有沒有看過[Beej的網絡編程指南](http://beej.us/guide/bgnet/)?它具有所有的基礎知識(儘管是c風格)。 – codeling

+0

看看反應堆模式... – Sambuca

回答

2

這裏粗略的步驟是:你可以有多個TCP服務器(又名服務器套接字)監聽每個端口。接下來,您可以爲每個這些服務器套接字使用select()和pass文件描述符。如果您在其中任何一個上獲得連接,則選擇將返回一個讀取事件並標記具有連接的服務器套接字的fd。你需要在服務器fd上調用accept()。

YOu無法使多個端口上的單個TCP套接字偵聽。

+0

謝謝你的回答。我正在考慮選擇,但我不想有任何超時 - 所以我需要非阻塞服務器套接字。我可以選擇?既然接受了等待的權利? – user2545592

+0

選擇是阻止呼叫。您可以指定一個超時作爲參數來選擇,但不指定它(這意味着將它作爲NULL傳遞)意味着只有在發生事件(在這種情況下爲讀事件)時,select纔會超時。順便說一句,你總是可以有多個線程和一個線程可以做所有服務器的FDS選擇。 –

+0

謝謝。所以還有一個問題。如果我有一個「服務器管理器」與很多服務器,我可以這樣做,如下所示?在服務器管理器中:1)for循環創建所有服務器套接字(函數:socket/setsockopt/ioctl/bind/listen)2)一些如何等待服務器管理器中的某些事件(使用套接字描述符列表選擇)3)進入服務器並處理問題(recv/send)? – user2545592