2011-05-30 102 views
3

我正在編寫一個C網絡服務器,它將兩個客戶端配對在一起,並允許它們互相發送消息。線程服務器,在客戶端之間發送消息

目前,每個客戶端都有自己的線程在服務器和線程中我有一個循環,基本上是while((numBytesRead = read(fd, buffer, 1024)) > 0)。這工作正常,我能夠接收消息,然後將它們回顯給客戶端。

我的問題是,雖然我不知道通過服務器將消息從一個客戶端傳輸到另一個客戶端的最佳方式。

我認爲我最大的問題是read()塊,所以我不能發送消息到客戶端,直到客戶端發送一些文本到服務器,以便讀停止阻止。

有沒有辦法解決這個問題?我最初的想法是讓一個線程從客戶端讀取,一個寫入客戶端,但如果讀取在一個線程中被阻塞,然後我嘗試寫入相同的文件描述符,那麼這不會導致問題?

感謝任何幫助! :)

+1

相關:http://stackoverflow.com/questions/4675824/how-to-implement-a-full-duplex-channel-over-tcp-with-a-single-thread – cmcginty 2011-05-30 07:28:28

回答

2

read只阻止未發送數據的客戶端線程;所以你可以在理論上使用剛剛發送數據到另一個的客戶端的線程write

另一種方法是使客戶端套接字爲非阻塞狀態,如果流中沒有數據,則會導致讀取立即返回(錯誤)。然而,這是非常耗費處理器的,因爲兩個套接字不斷地被數據探測,並且(幾乎總是)不返回。

我最後的建議是尋找select函數,它可以用來檢查一組文件描述符並阻塞,直到它們中的任何一個都有數據流;另外它可以通過一個超時(阻塞的時間上限)。算法的草圖可能是:

  1. 選擇(客戶端插口,100毫秒)
  2. 與現有數據每個插座:
    1. 讀取數據
    2. 其存儲在其他套接字輸出緩衝區
  3. 每個插座:
    1. 寫入其電流輸出緩衝器
  4. 重複
+0

謝謝馬蒂!我對第一個解決方案很感興趣,但是我有點擔心在另一個線程嘗試從它讀取''讀''的時候,試圖在一個線程中寫入文件描述符?這是安全的嗎? – Sam 2011-05-30 07:42:35

+0

@Sam它應該是非常好的。請讓我知道,如果不是。 – 2011-05-30 07:47:35

+0

謝謝,我最終制作了一個寫入線程和一個讀取線程......似乎工作得很好。 :) – Sam 2011-05-30 09:47:30

1

這當然不是一個簡單的問題

  1. 設計的協議和協議消息格式
  2. 讀協議消息從插座
  3. 處理類型爲「登錄」的消息
  4. 綁定uniq id t O此插座
  5. 讀取從插座類型「消息」的其它消息(發送器),其包含一個「接收者ID」
  6. 找到具有該ID(接收機)的插座
  7. 發送數據的消息中向接收器

建議使用IO多路複用代替多線程。

0

使用非阻塞操作是服務器設計的不錯選擇。在這種情況下,最簡單的方法是使用select或poll。更高級的是kqueue(FreeBSD)和epoll(Linux),同樣可以使用異步I/O(AIO)

所以如果select()是選擇,那麼可以使用下一個方法代碼):

fd_set read_set, write_set; 
struct timeval timeout; 

while(!quit) 
{ 
    // adds your sockets to fd_set structure, return max socket + 1, this is important! 
    max = fillFDSet(&read_set); 
    setReadTimeout(&timeout); // sets timeout for select 

    if (0 < select(max, &read_set, NULL, NULL, &timeout)) // wait for read 
    { 
      // there is at least one descriptor ready 
      if (FD_ISSET(your_socket)) 
      { 
       socket_size = read(socket, socket_buffer, 1024); 
      } 
    } 
    max = fillFDSet(&write_set); 
    setWriteTimeout(&timeout); // sets timeout for select 

    if (0 < select(max, NULL, &write_set, NULL, &timeout)) // wait for write 
    { 
      // there is at least one descriptor ready 
      if (FD_ISSET(your_socket)) 
      { 
       write(socket, socket_buffer, socket_size); 
      } 
    } 
} 
相關問題