LINUX下Socket編程 函數格式詳解

你需要了解的一些系統調用:

socket()
bind()
connect()
listen()
accept()
send()
recv()
sendto()
recvfrom()
close()
shutdown()
setsockopt()
getsockopt()
getpeername()
getsockname()
gethostbyname()
gethostbyaddr()
getprotobyname()
fcntl()
我們將在以下詳細介紹這些系統調用。
1. socket()函數
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain , int type , int protocol);
首先,domain 需要被設置爲 “AF_INET”,就像上面的struct sockaddr_in。然後,type參數告訴內核這個socket 是什麼類型,“SOCK_STREAM”或是“SOCK_DGRAM”。最後,只需要把protocol 設置爲0 。
socket()函數只是簡單的返回一個你以後可以使用的套接字描述符。如果發生錯誤,socket()函數返回 –1 。全局變量errno 將被設置爲錯誤代碼。(可以參考perror() 的manpages)
 
2. bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen)
bind()的系統調用聲明如下:
#include <sys/types.h>
#include <sys/socket.h>
int bind (int sockfd , struct sockaddr *my_addr , int addrlen) ;
參數說明:
l sockfd 是由socket()函數返回的套接字描述符。
l my_addr 是一個指向struct sockaddr 的指針,包含有關你的地址的信息:名稱、端口和IP 地址。
l addrlen 可以設置爲sizeof(struct sockaddr)。
當bind()函數調用錯誤的時候,它也是返回–1 作爲錯誤發生的標誌。errn 的值爲錯誤代碼。
當你調用bind()的時候,不要把端口數設置的過小!小於1024 的所有端口都是保留下來作爲系統使用端口的,沒有root 權利無法使用。你可以使用1024 以上的任何端口,一直到65535
 
                       對socket進行定位
 
相關函數
socket,accept,connect,listen
表頭文件
#include<sys/types.h>
#include<sys/socket.h>
定義函數
int bind(int sockfd,struct sockaddr * my_addr,int addrlen);
函數說明
bind()用來設置給參數sockfd的socket一個名稱。此名稱由參數my_addr指向一sockaddr結構,對於不同的socket domain定義了一個通用的數據結構
struct sockaddr
{
unsigned short int sa_family;
char sa_data[14];
};
sa_family 爲調用socket()時的domain參數,即AF_xxxx值。
sa_data 最多使用14個字符長度。
此sockaddr結構會因使用不同的socket domain而有不同結構定義,例如使用AF_INET domain,其socketaddr結構定義便爲
struct socketaddr_in
{
unsigned short int sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr
{
uint32_t s_addr;
};
sin_family 即爲sa_family
sin_port 爲使用的port編號
sin_addr.s_addr 爲IP 地址
sin_zero 未使用。
參數
addrlen爲sockaddr的結構長度。
返回值
成功則返回0,失敗返回-1,錯誤原因存於errno中。
錯誤代碼
EBADF 參數sockfd 非合法socket處理代碼。
EACCESS 權限不足
ENOTSOCK 參數sockfd爲一文件描述詞,非socket。
 
該函數用來指定一個端口號,一個IP地址,兩者都指定,或者兩者都不指定.可以不使用該函數調用。使用socket()得到套接口後可以直接調用函數conect()或者listen(),這時內核會自動給套接口分配一個地址和端口號(衆所周知的端口號),這是常用的方法。只有在進程需要使用特定的網絡地址和端口時纔會進行綁定,即使用bind()函數。調用bind()的常見錯誤是EADDRINUSE,即指定的地址正在使用,主要是指定的端口號被使用了,IP地址可以被多個進程使用,但端口在同一時刻只能被一個進程使用。
套接口中port=0表示由內核指定端口號,設定sin_addr爲INADDR_ANY(表示任意的意思),就有內核指定端口號。
設置端口爲0的語句:
       struct socketaddr_in seeveraddr;
       serveraddr.port = 0;
設置IP的語句:
      serveraddr.sin_addr = htonl(INADDR_ANY);
 
htonl()說明:
 
   
表頭文件
#include<netinet/in.h>
定義函數
unsigned long int htonl(unsigned long int hostlong);
函數說明
htonl()用來將參數指定的32位hostlong 轉換成網絡字符順序。
返回值

返回對應的網絡字符順序。

 
 
3. connect() 函數
#include <sys/types.h>
#include <sys/socket.h>
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);
connect()的三個參數意義如下:
l sockfd :套接字文件描述符,由socket()函數返回的。
l serv_addr 是一個存儲遠程計算機的IP 地址和端口信息的結構。
l addrlen 應該是sizeof(struct sockaddr)。
 
4. listen()函數
#include <sys/socket.h>
int listen(int sockfd, int backlog);
listen()函數的參數意義如下:
l sockfd 是一個套接字描述符,由socket()系統調用獲得。
l backlog 是未經過處理的連接請求隊列可以容納的最大數目。
backlog 具體一些是什麼意思呢?每一個連入請求都要進入一個連入請求隊列,等待listen 的程序調用accept()(accept()函數下面有介紹)函數來接受這個連接。當系統還沒有調用accept()函數的時候,如果有很多連接,那麼本地能夠等待的最大數目就是backlog 的數值。你可以將其設成5 到10 之間的數值(推薦)。像上面的所有函數一樣, listen()如果返回 –1 ,那麼說明在listen()的執行過程中發生了錯誤。全局變量errno 中存儲了錯誤代碼。
 
5. accept()函數
當調用它的時候,大致過程是下面這樣的
l 有人從很遠很遠的地方嘗試調用connect()來連接你的機器上的某個端口(當然是你已經在listen()的)。
l 他的連接將被listen 加入等待隊列等待accept()函數的調用(加入等待隊列的最多數目由調用listen()函數的第二個參數backlog 來決定)。
l 你調用accept()函數,告訴他你準備連接。
l accept()函數將回返回一個新的套接字描述符,這個描述符就代表了這個連接!
 
#include <sys/socket.h>
int accept(int sockfd, void *addr, int *addrlen);
accept()函數的參數意義如下:
l sockfd 是正在listen() 的一個套接字描述符。
addr 一般是一個指向struct sockaddr_in 結構的指針;裏面存儲着遠程連接過來的計算機的信息(比如遠程計算機的IP 地址和端口)。
l addrlen 是一個本地的整型數值,在它的地址傳給accept() 前它的值應該是sizeof(struct sockaddr_in);accept()不會在addr 中存儲多餘addrlen bytes 大小的數據。如果
accept()函數在addr 中存儲的數據量不足addrlen,則accept()函數會改變addrlen 的值來反應這個情況。
 
6. send()、recv()函數
#include <sys/types.h>
#include <sys/socket.h>
int send(int sockfd, const void *msg, int len, int flags);
send 的參數含義如下:
l sockfd 是代表你與遠程程序連接的套接字描述符。
l msg 是一個指針,指向你想發送的信息的地址。
l len 是你想發送信息的長度。
l flags 發送標記。一般都設爲0(你可以查看send 的man pages 來獲得其他的參數
值並且明白各個參數所代表的含義)。
 
send()函數在調用後會返回它真正發送數據的長度
注意:send() 所發送的數據可能少於你給它的參數所指定的長度!因爲如果你給send()的參數中包含的數據的長度遠遠大於send()所能一次發送的數據,則send()函數
只發送它所能發送的最大數據長度,然後它相信你會把剩下的數據再次調用它來進行第二次發送。所以,記住如果send()函數的返回值小於len 的話,則你需要再次發送剩下的數據。幸運的是,如果包足夠小(小於1K),那麼send()一般都會一次發送光的。像上面的函數一樣,send()函數如果發生錯誤,則返回 –1 ,錯誤代碼存儲在全局變量errno 中。
 
函數recv()調用在許多方面都和send()很相似,下面是recv()函數的聲明:
#include <sys/types.h>
#include <sys/socket.h>
int recv(int sockfd, void *buf, int len, unsigned int flags);
recv()的參數含義如下:
l sockfd 是你要讀取數據的套接字描述符。
l buf 是一個指針,指向你能存儲數據的內存緩存區域。
l len 是緩存區的最大尺寸。
l flags 是recv() 函數的一個標誌,一般都爲0 (具體的其他數值和含義請參考recv()
的man pages)。
recv() 返回它所真正收到的數據的長度。(也就是存到buf 中數據的長度)。如果返回–1 則代表發生了錯誤(比如網絡以外中斷、對方關閉了套接字連接等),全局變量errno 裏面存儲了錯誤代碼。
 
7. close()和shutdown()函數
程序進行網絡傳輸完畢後,你需要關閉這個套接字描述符所表示的連接。實現這個非常簡單,只需要使用標準的關閉文件的函數:close()。
使用方法:
close(sockfd);
執行close()之後,套接字將不會在允許進行讀操作和寫操作。任何有關對套接字描述符進行讀和寫的操作都會接收到一個錯誤。
如果你想對網絡套接字的關閉進行進一步的操作的話,你可以使用函數shutdown()。
它允許你進行單向的關閉操作,或是全部禁止掉。
shutdown()的聲明爲:
#include <sys/socket.h>
int shutdown(int sockfd, int how);
它的參數含義如下:
l sockfd 是一個你所想關閉的套接字描述符.
l how 可以取下面的值。0 表示不允許以後數據的接收操;1 表示不允許以後數據的發送操作;2 表示和close()一樣,不允許以後的任何操作(包括接收,發送數據)
shutdown() 如果執行成功將返回0,如果在調用過程中發生了錯誤,它將返回–1,全局變量errno 中存儲了錯誤代碼.
如果你在一個未連接的數據報套接字上使用shutdown() 函數(還記得可以對數據報套接字UDP 進行connect()操作嗎?),它將什麼也不做.
 
8. setsockopt() 和getsockopt() 函數
Linux 所提供的socket 庫含有一個錯誤(bug)。此錯誤表現爲你不能爲一個套接字重
新啓用同一個端口號,即使在你正常關閉該套接字以後。問題就是Linux 內核在一個綁定套接字的進程結束後從不把端口標記爲未用。
在Linux 中繞開這個問題的辦法是,當套接字已經打開但尚未有連接的時候用
setsockopt()系統調用在其上設定選項(options)。setsockopt() 調用設置選項而getsockopt()
從給定的套接字取得選項。
這裏是這些調用的語法:
#include<sys/types.h>
#include<sys/socket.h>
int getsockopt(int sockfd, int level, int name, char *value, int *optlen);
int setsockopt(int sockfd, int level, int name, char *value, int *optlen);

下面是兩個調用的參數說明:
l sockfd 必須是一個已打開的套接字。
l level 是函數所使用的協議標準(protocol level)(TCP/IP 協議使用IPPROTO_TCP,
套接字標準的選項實用SOL_SOCKET)。
l name 選項在套接字說明書中(man page)有詳細說明。
l value 指向爲getsockopt()函數所獲取的值,setsockopt()函數所設置的值的地址。
l optlen 指針指向一個整數,該整數包含參數以字節計算的長度。
 
9. getpeername()函數
這個函數可以取得一個已經連接上的套接字的遠程信息(比如IP 地址和端口),告訴你在遠程和你連接的究竟是誰.
它的聲明爲:
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
下面是參數說明:
l sockfd 是你想取得遠程信息的那個套接字描述符。
l addr 是一個指向struct sockaddr (或是struct sockaddr_in)的指針。
l addrlen 是一個指向int 的指針,應該賦於sizeof(struct sockaddr)的大小。
如果在函數執行過程中出現了錯誤,函數將返回 –1 ,並且錯誤代碼儲存在全局變量
errno 中。
當你擁有了遠程連接用戶的IP 地址,你就可以使用inet_ntoa() 或gethostbyaddr()來輸
出信息或是做進一步的處理。
 
10. gethostname()函數
gethostname()函數可以取得本地主機的信息.它比getpeername()要容易使用一些。
它返回正在執行它的計算機的名字。返回的這個名字可以被gethostbyname()函數使用,
由此可以得到本地主機的IP 地址。
下面是它的聲明:
#include <unistd.h>
int gethostname(char *hostname, size_t size);
參數說明如下:
l hostname 是一個指向字符數組的指針,當函數返回的時候,它裏面的數據就是本
地的主機的名字.
l size 是hostname 指向的數組的長度.
函數如果成功執行,它返回0,如果出現錯誤,則返回–1,全局變量errno 中存儲着錯
誤代碼。
 
11. gethostbyname()函數
#include <netdb.h>
struct hostent *gethostbyname(const char *name);
正如你所看見的,它返回了一個指向struct hostent 的指針.Struct hostent 是這樣定義
的:
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
#define h_addr h_addr_list[0]
下面是上面各個域代表含義的解釋:
l h_name 是這個主機的正式名稱。
l h_aliases 是一個以NULL(空字符)結尾的數組,裏面存儲了主機的備用名稱。
l h_addrtype 是返回地址的類型,一般來說是“AF_INET”。
l h_length 是地址的字節長度。
l h_addr_list 是一個以0 結尾的數組,存儲了主機的網絡地址。
注意:網絡地址是以網絡字節順序存儲的。
l h_addr - h_addr_list 數組的第一個成員.
gethostbyname() 返回的指針指向結構struct hostent ,如果發生錯誤,它將會返回NULL
(但是errno 並不代表錯誤代碼,h_errno 中存儲的才識錯誤代碼。參考下面的herror()函數)。
使用gethostbyname()函數,你不能使用perror()來輸出錯誤信息(因爲錯誤代碼存儲在
h_errno 中而不是errno 中。所以,你需要調用herror()函數。
 
######################################################################################################################################################################
2. 服務器進程中系統調用的順序
          socket()————bind()————listen()————accept()
在面向連接的協議的程序中,服務器執行以下函數:
l 調用socket()函數創建一個套接字。
l 調用bind()函數把自己綁定在一個地址上。
l 調用listen()函數偵聽連接。
l 調用accept()函數接受所有引入的請求。
l 調用recv()函數獲取引入的信息然後調用send()回答
 
     TCP三次握手協議:
(1)客戶端先用connect()向服務器發出一個要求連接的信號SYN1。
(2)服務器進程接收到這個信號後,發回應答信號ack1,同時這也是一個要求回答的信號SYN2。
(3)客戶端收到應答信號ack1和SYN2後,再次應答ack2。
(4)服務器收到應答信號ack2,一次連接纔算建立完成。
 
3.使用完一個套接口後,一定要記得將它關掉,使用函數close(int sockfd)
4.Linux系統調用-- getsockname函數詳解
    當不用bind()或調用bind()沒有指定本地協議地址時,可以調用getsockname()來返回內核分配給此連接的本地IP地址和端口號,還可以獲得某套接口的協議族。當一個新的連接建立時,服務器也可以調用getsockname()來獲得分配給此連接的本地IP地址。
 當一個服務器的子進程調用exec函數啓動執行時,只能調用getpeername()函數來獲得客戶的Ip地址和端口號。

【 getsockname系統調用】   
    
功能描述: 
返回指定套接字的名稱。 

用法: 
#include <sys/socket.h>

int getsockname(int sockfd, struct sockaddr *name, socklen_t *namelen);


參數:   
sockfd:需要獲取名稱的套接字。
name:存放所獲取套接字名稱的緩衝區。
nemalen:作爲入口參數,name指向空間的最大長度。作爲出口參數,name的實際長度。

 返回說明:   
成功執行時,返回0。失敗返回-1,errno被設爲以下的某個值   
EBADF:sock不是有效的文件描述詞
EFAULT:name指向的內存並非有效的進程空間
EINVAL:namelen無效,可能爲負值
ENOBUFS:執行操作時,系統資源不足
ENOTCONN:套接字尚未連接上
ENOTSOCK:sock描述的不是套接字

功能:
getsockname: 返回本地協議地址
getpeername:返回遠程協議地址
定義:
#include <sys/unistd.h>

int getsockname (int sockfd, struct sockaddr *localaddr, int *addrlen);
int getpeername(int sockfd, struct sockaddr *peeraddr, int *addrlen);

   getsockname()函數用於獲取一個套接口的名字。它用於一個已捆綁或已連接套接口s,本地地址將被返回。本調用特別適用於如下情況:未調用bind()就調用了connect(),這時唯有getsockname()調用可以獲知系統內定的本地地址。在返回時,namelen參數包含了名字的實際字節數。
   若一個套接口與INADDR_ANY捆綁,也就是說該套接口可以用任意主機的地址,此時除非調用connect()或accept()來連接,否則getsockname()將不會返回主機IP地址的任何信息。除非套接口被連接,WINDOWS套接口應用程序不應假設IP地址會從INADDR_ANY變成其他地址。這是因爲對於多個主機環境下,除非套接口被連接,否則該套接口所用的IP地址是不可知的。

 

Open C 套接字: getsockname 方法

getsockname - 獲取套接字名稱

int getsockname (int s, struct sockaddr * restrict name, socklen_t * restrict namelen);

getsockname系統調用返回指定套接字的當前名稱namelen應被初始化指出name所指向的空間容量。返回時,該參數含有返回名稱

的實際大小(按字節)。

下面是getsockname函數的用法:

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
TInt GetSockName()
{   
int sock_fd;   
struct sockaddr_in addr,ss;   
unsigned int len;         
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);          
addr.sin_family = AF_INET;   
addr.sin_addr.s_addr = htonl(INADDR_ANY);   
addr.sin_port = htons(5000);   
bind(sock_fd,(struct sockaddr*)&addr,sizeof(addr));   
len=sizeof(ss);  
getsockname(sock_fd,(struct sockaddr*)&ss,&len);  
close(sock_fd);
}
***********************************************************************************
***********************************************************************************
***********************************************************************************
LINUX下Socket編程筆記:http://blog.chinaunix.net/u/19185/article_56798.html
***********************************************************************************
程序實例:
###################################################################################
//使用方法:定義的服務器的遠端端口號是4000,故編譯運行該程序後,需要使用以下命令在終端上顯示:Hello World!
//在cmd命令行中輸入:telnet 遠端服務器地址 端口號
//我的實際運行 telnet 192.168.12.94 4000?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
// 服務器要監聽的本地端口
#define MYPORT 4000
// 能夠同時接受多少沒有accept 的連接
#define BACKLOG 10
main()
{
     // 在sock_fd 上進行監聽,new_fd 接受新的連接
     int sockfd, new_fd ;
     // 自己的地址信息
     struct sockaddr_in my_addr;
     // 連接者的地址信息
     struct sockaddr_in their_addr;
     int sin_size;
     // 這裏就是我們一直強調的錯誤檢查.如果調用socket() 出錯,則返回
     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
     {
         // 輸出錯誤提示並退出
         perror("socket");
         exit(1);
     }
     // 主機字節順序
     my_addr.sin_family = AF_INET;
     // 網絡字節順序,短整型
     my_addr.sin_port = htons(MYPORT);
     // 將運行程序機器的IP 填充入s_addr
     my_addr.sin_addr.s_addr = INADDR_ANY;
     // 將此結構的其餘空間清零
     bzero(&(my_addr.sin_zero), 8);
     // 這裏是我們一直強調的錯誤檢查!!
     if (bind(sockfd, (struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1)
     {
         // 如果調用bind()失敗,則給出錯誤提示,退出
         perror("bind");
         exit(1);
     }
     // 這裏是我們一直強調的錯誤檢查!!
     if (listen(sockfd, BACKLOG) == -1)
     {
         // 如果調用listen 失敗,則給出錯誤提示,退出
         perror("listen");
         exit(1);
     }
     while(1)
     {
         // 這裏是主accept()循環
         sin_size = sizeof(struct sockaddr_in);
         // 這裏是我們一直強調的錯誤檢查!!
         if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
         {
             // 如果調用accept()出現錯誤,則給出錯誤提示,進入下一個循環
             perror("accept");
             continue;
         }
         // 服務器給出出現連接的信息
         printf("server: got connection from %s\n", inet_ntoa(their_addr.sin_addr));
         // 這裏將建立一個子進程來和剛剛建立的套接字進行通訊
         if (!fork())
         {
             // 這裏是子進程
             // 這裏就是我們說的錯誤檢查!
             if (send(new_fd, "Hello, world!\n", 14, 0) == -1)
             {
                 // 如果錯誤,則給出錯誤提示,然後關閉這個新連接,退出
                 perror("send");
                 close(new_fd);
                 exit(0);
             }
             // 關閉new_fd 代表的這個套接字連接
             close(new_fd);
         }
     }
     // 等待所有的子進程都退出
     while(waitpid(-1,NULL,WNOHANG) > 0);
}
###################################################################################
 
###################################################################################
/* include fig01 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h> 
#include <string.h> 
#include <sys/poll.h>
#include <errno.h>
#define MAXLINE 512
#define NOTDEF
int
main(int argc, char **argv)
{
 int      i, maxi, maxfd, listenfd, connfd, sockfd;
 int      nready, client[FD_SETSIZE];
 ssize_t     n;
 fd_set     rset, allset;
 char     buf[MAXLINE];
 socklen_t    clilen;
 struct sockaddr_in cliaddr, servaddr;
 listenfd = socket(AF_INET, SOCK_STREAM, 0);
 bzero(&servaddr, sizeof(servaddr));
 servaddr.sin_family       = AF_INET;
 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 servaddr.sin_port         = htons(4563);
 bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
 listen(listenfd, 12);
 maxfd = listenfd;    /* initialize */
 maxi = -1;      /* index into client[] array */
 //for (i = 0; i < FD_SETSIZE; i++)
 for (i = 0; i < 3; i++)
   client[i] = -1;    /* -1 indicates available entry */
 /*
 void FD_ZERO(fd_set *fdset)
 Initialises the file descriptor set fdset to have zero bits for all file descriptors. 
 初始化所有的文件描述符fd_set爲0
 */
 FD_ZERO(&allset);
 /*
 void FD_SET(int fd, fd_set *fdset)
 Sets the bit for the file descriptor fd in the file descriptor set fdset. 
 */
 FD_SET(listenfd, &allset);
/* end fig01 */
/* include fig02 */
 for ( ; ; ) {
   rset = allset;   /* structure assignment */
   nready =select(maxfd+1, &rset, NULL, NULL, NULL);
   /*
   定義函數  int select(int n,fd_set * readfds,fd_set * writefds,fd_set *       
                     exceptfds,struct timeval * timeout);
   select()用來等待文件描述詞狀態的改變。參數n代表最大的文件描述詞加1,參數readfds、writefds 和exceptfds 稱爲描述詞組,是用來回傳該描述詞的讀,寫或例外的狀況。底下的宏提供了處理這三種描述詞組的方式:
   FD_CLR(inr fd,fd_set* set);用來清除描述詞組set中相關fd 的位
   FD_ISSET(int fd,fd_set *set);用來測試描述詞組set中相關fd 的位是否爲真
   FD_SET(int fd,fd_set*set);用來設置描述詞組set中相關fd的位
   FD_ZERO(fd_set *set); 用來清除描述詞組set的全部位
    
   參數  timeout爲結構timeval,用來設置select()的等待時間,其結構定義如下
   struct timeval
   {
   time_t tv_sec;
   time_t tv_usec;
   };
    
   返回值  如果參數timeout設爲NULL則表示select()沒有timeout。
    
   錯誤代碼  執行成功則返回文件描述詞狀態已改變的個數,如果返回0代表在描述詞狀態改變前已超過timeout時間,當有錯誤發生時則返回-1,錯誤原因存於errno,此時參數readfds,writefds,exceptfds和timeout的值變成不可預測。
   EBADF 文件描述詞爲無效的或該文件已關閉
   EINTR 此調用被信號所中斷
   EINVAL 參數n 爲負值。
   ENOMEM 核心內存不足
  
   常見的程序片段:fs_set readset;
   FD_ZERO(&readset);
   FD_SET(fd,&readset);
   select(fd+1,&readset,NULL,NULL,NULL);
   if(FD_ISSET(fd,readset){……}
   */
  
   if (FD_ISSET(listenfd, &rset)) { /* new client connection */
    /*
    int FD_ISSET(int fd, fd_set *fdset)
    Returns a non-zero value if the bit for the file descriptor fd is set in the file descriptor set by fdset, and 0 otherwise. 
    */
    clilen = sizeof(cliaddr);
    connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);
    printf("Welcome!\n");
    sleep(1);
#ifndef NOTDEF
    printf("new client: %s, port %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, NULL,4 ),ntohs(cliaddr.sin_port));
#endif
    //for (i = 0; i < FD_SETSIZE; i++)
    for (i = 0; i < 3; i++)
     if (client[i] < 0) {
      client[i] = connfd; /* save descriptor */
      break;
     }
    if (i == FD_SETSIZE)
     printf("too many clients");
    FD_SET(connfd, &allset); /* add new descriptor to set */
    if (connfd > maxfd)
     maxfd = connfd;    /* for select */
    if (i > maxi)
     maxi = i;     /* max index in client[] array */
    if (--nready <= 0)
     continue;     /* no more readable descriptors */
   }
   for (i = 0; i <= maxi; i++) { /* check all clients for data */
    if ( (sockfd = client[i]) < 0)
     continue;
    if (FD_ISSET(sockfd, &rset)) {
     if ( (n = read(sockfd, buf, MAXLINE)) == 0) {
       /*4connection closed by client */
      close(sockfd);
      FD_CLR(sockfd, &allset);
      /*
      void FD_CLR(int fd, fd_set *fdset)
           Clears the bit for the file descriptor fd in the file descriptor set fdset. 
      */
      client[i] = -1;
     } else
      write(sockfd, buf, n);
     if (--nready <= 0)
      break;     /* no more readable descriptors */
    }
   }
 }
}
/* end fig02 */
###################################################################################
分類: 網絡編程
發佈了14 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章