1. 程式人生 > >《網路socket程式設計指南》3

《網路socket程式設計指南》3


listen()函式 
  是換換內容得時候了。假如你不希望與遠端的一個地址相連,或者說, 僅僅是將它踢開,那你就需要等待接入請求並且用各種方法處理它們。處 理過程分兩步:首先,你聽--listen(),然後,你接受--accept() (請看下面的 內容)。 
除了要一點解釋外,系統呼叫 listen 也相當簡單。 
int listen(int sockfd, int backlog);  
sockfd 是呼叫 socket() 返回的套接字檔案描述符。backlog 是在進入 佇列中允許的連線數目。什麼意思呢? 進入的連線是在佇列中一直等待直 到你接受 (accept() 請看下面的文章)連線。它們的數目限制於佇列的允許。 大多數系統的允許數目是20,你也可以設定為5到10。 
和別的函式一樣,在發生錯誤的時候返回-1,並設定全域性錯誤變數 errno。 
你可能想象到了,在你呼叫 listen() 前你或者要呼叫 bind() 或者讓內 核隨便選擇一個埠。如果你想偵聽進入的連線,那麼系統呼叫的順序可 能是這樣的:  
socket();  
  bind();  
listen();  
  /* accept() 應該在這 */  
因為它相當的明瞭,我將在這裡不給出例子了。(在 accept() 那一章的 程式碼將更加完全。)真正麻煩的部分在 accept()。  
-------------------------------------------------------------------------------- 
accept()函式 
  準備好了,系統呼叫 accept() 會有點古怪的地方的!你可以想象發生 這樣的事情:有人從很遠的地方通過一個你在偵聽 (listen()) 的埠連線 (connect()) 到你的機器。它的連線將加入到等待接受 (accept()) 的佇列 中。你呼叫 accept() 告訴它你有空閒的連線。它將返回一個新的套接字文 件描述符!這樣你就有兩個套接字了,原來的一個還在偵聽你的那個埠, 新的在準備傳送 (send()) 和接收 ( recv()) 資料。這就是這個過程! 
函式是這樣定義的:  
#include <sys/socket.h> 
int accept(int sockfd, void *addr, int *addrlen);  
sockfd 相當簡單,是和 listen() 中一樣的套接字描述符。addr 是個指 向區域性的資料結構 sockaddr_in 的指標。這是要求接入的資訊所要去的地 方(你可以測定那個地址在那個埠呼叫你)。在它的地址傳遞給 accept 之 前,addrlen 是個區域性的整形變數,設定為 sizeof(struct sockaddr_in)。 accept 將不會將多餘的位元組給 addr。如果你放入的少些,那麼它會通過改 
變 addrlen 的值反映出來。  
同樣,在錯誤時返回-1,並設定全域性錯誤變數 errno。  
現在是你應該熟悉的程式碼片段。  
#include <string.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#define MYPORT 3490 /*使用者接入埠*/  
#define BACKLOG 10 /* 多少等待連線控制*/  
main()  
   {  
  int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */  
  struct sockaddr_in my_addr; /* 地址資訊 */  
  struct sockaddr_in their_addr; /* connector's address information */  
  int sin_size;  
sockfd = socket(AF_INET, SOCK_STREAM, 0); /* 錯誤檢查*/  
my_addr.sin_family = AF_INET; /* host byte order */  
  my_addr.sin_port = htons(MYPORT); /* short, network byte order */  
  my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */  
  bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */  
/* don't forget your error checking for these calls: */  
  bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));  
listen(sockfd, BACKLOG);  
sin_size = sizeof(struct sockaddr_in);  
  new_fd = accept(sockfd, &their_addr, &sin_size);  
   .  
   .  
   .  
注意,在系統呼叫 send() 和 recv() 中你應該使用新的套接字描述符 new_fd。如果你只想讓一個連線進來,那麼你可以使用 close() 去關閉原 來的檔案描述符 sockfd 來避免同一個埠更多的連線。  
-------------------------------------------------------------------------------- 
send() and recv()函式 
  這兩個函式用於流式套接字或者資料報套接字的通訊。如果你喜歡使 用無連線的資料報套接字,你應該看一看下面關於sendto() 和 recvfrom() 的章節。 
send() 是這樣的: 
int send(int sockfd, const void *msg, int len, int flags);  
sockfd 是你想傳送資料的套接字描述符(或者是呼叫 socket() 或者是 accept() 返回的。)msg 是指向你想傳送的資料的指標。len 是資料的長度。 把 flags 設定為 0 就可以了。(詳細的資料請看 send() 的 man page)。  
這裡是一些可能的例子: 
char *msg = "Beej was here!";  
  int len, bytes_sent; 
  .  
  .  
  len = strlen(msg); 
  bytes_sent = send(sockfd, msg, len, 0); 
  .  
  .  
  .  
send() 返回實際傳送的資料的位元組數--它可能小於你要求傳送的數 目! 注意,有時候你告訴它要傳送一堆資料可是它不能處理成功。它只是 傳送它可能傳送的資料,然後希望你能夠傳送其它的資料。記住,如果 send() 返回的資料和 len 不匹配,你就應該傳送其它的資料。但是這裡也 有個好訊息:如果你要傳送的包很小(小於大約 1K),它可能處理讓資料一 次傳送完。最後要說得就是,它在錯誤的時候返回-1,並設定 errno。 
recv() 函式很相似: 
int recv(int sockfd, void *buf, int len, unsigned int flags); 
sockfd 是要讀的套接字描述符。buf 是要讀的資訊的緩衝。len 是緩 衝的最大長度。flags 可以設定為0。(請參考recv() 的 man page。) recv() 返回實際讀入緩衝的資料的位元組數。或者在錯誤的時候返回-1, 同時設定 errno。 
很簡單,不是嗎? 你現在可以在流式套接字上傳送資料和接收資料了。 你現在是 Unix 網路程式設計師了!