1. 基本套接字函數
(1)socket函數原型
socket(建立一個socket文件描述符) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 建立一個socket文件描述符 | |
函數原型 | int socket(int domain, int type, int protocol) | |
函數傳入值 | domain | AF_INET:IPv4協議 |
AF_INET6:IPv6協議 | ||
AF_LOCAL:Unix域協議 | ||
AF_ROUTE:路由套接口 | ||
AF_KEY:密鑰套接口 | ||
type | SOCKET_STREAM:雙向可靠數據流,對應TCP | |
SOCKET_DGRAM:雙向不可靠數據報,對應UDP | ||
SOCKET_RAW:提供傳輸層以下的協議,可以訪問內部網絡接口,例如接收和發送ICMP報文 | ||
protocol | type爲SOCKET_RAW時需要設置此值說明協議類型,其他類型設置爲0即可 | |
函數返回值 | 成功:socket文件描述符 | |
失敗:-1,失敗原因存於error中 |
表18-1列出了當進行socket調用時,中協議簇(domain)與類型(type)可能產生的組合。
表18-1 socket中協議簇(domain)與類型(type)組合表
| AF_INET | AF_INET6 | AF_LOCAL | AF_ROUTE | AF_KEY |
SOCK_STREAM | TCP | TCP | Yes |
|
|
SOCK_DGRAM | UDP | UDP | Yes |
|
|
SOCK_RAW | IPv4 | IPv6 |
| Yes | Yes |
(2)bind函數原型
bind(將一個本地協議地址與socket文件描述符聯繫起來) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 將一個協議地址與socket文件描述符聯繫起來 | |
函數原型 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) | |
函數傳入值 | sockfd | socket文件描述符 |
addr | my_addr指向sockaddr結構,該結構中包含IP地址和端口等信息 | |
addrlen | sockaddr結構的大小,可設置爲sizeof(struct sockaddr) | |
函數返回值 | 成功:0 | |
失敗:-1,失敗原因存於error中 |
利用bind函數綁定地址時,可以指定IP地址和端口號,也可以指定其中之一,甚至一個也不指定。可以使用通配地址INADDR_ANY(爲宏定義,其值等於0),它通知內核選擇IP地址。表18-2列出了設置socket地址結構的幾種方式,但在實際中,綁定的端口號都需要指定。
表18-2 設置socket地址結構的幾種方式
進程指定 | 說 明 | |
IP地址 | 端口 | |
通配地址INADDR_ANY | 0 | 內核自動選擇IP地址和端口號 |
通配地址INADDR_ANY | 非0 | 內核自動選擇IP地址,進程指定端口號 |
本地IP地址 | 0 | 進程指定IP地址,內核自動選擇端口號 |
本地IP地址 | 非0 | 進程指定IP地址和端口號 |
(3)listen函數原型
listen (等待連接) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 等待連接 | |
函數原型 | int listen(int sockfd, int backlog) | |
函數傳入值 | sockfd | 監聽socket文件描述符 |
backlog | 套接字排隊的最大連接個數 | |
函數返回值 | 成功:0 | |
失敗:-1,失敗原因存於error中 | ||
特別說明 | 對於監聽socket文件描述符sockfd,內核要維護兩個隊列,分別爲未完成連接隊列和已完成連接隊列,這兩個隊列之和不超過backlog |
(4)connect函數原型
connect(建立socket連接) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 建立socket連接 | |
函數原型 | int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) | |
函數傳入值 | sockfd | socket文件描述符 |
serv_addr | 連接的網絡地址和端口 | |
addrlen | sockaddr結構的大小,可設置爲sizeof(struct sockaddr) | |
函數返回值 | 成功:0 | |
失敗:-1,失敗原因存於error中 | ||
附加說明 | 函數connect激發TCP的三路握手過程,出錯返回有以下幾種情況: ① 如果客戶沒有收到SYN分節的響應(總共75秒,這之間可能重發了若干次SYN),則返回ETIMEDOUT ② 如果對客戶的SYN的響應是RST,則表明該服務器主機在指定的端口上沒有進程在等待與之相連,函數返回錯誤ECONNREFUSED ③ 如果客戶發出的SYN在中間路由器上引發一個目的地不可達的ICMP錯誤,內核返回EHOSTUNREACH或ENETUNREACH錯誤(即ICMP錯誤)給進程 |
(5)accept函數原型
accept(接受socket連接) |
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> |
函數說明 | 接受socket連接,返回一個新的socket文件描述符,原socket文件描述符仍爲listen函數所用,而新的socket文件描述符用來處理連接的讀寫操作 |
函數原型 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) |
函數傳入參數 | sockfd: socket文件描述符 |
addrlen:addr的大小,可設置爲sizeof(struct sockaddr) | |
函數傳出參數 | addr:填入遠程主機的地址數據 |
函數返回值 | 成功:實際讀取字節數 |
失敗:-1,錯誤代碼存放在error中 | |
附加說明 | ① accept函數由TCP服務器調用,爲阻塞函數,從已完成連接的隊列中返回一個連接;如果該對列爲空,則進程進入阻塞等待 ② 函數返回的套接字爲已連接套接字,而監聽套接字仍爲listen函數所用 |
(6)close函數原型
close (關閉連接的socket文件描述符) | |
所需頭文件 | #include <unistd.h> |
函數說明 | 關閉連接的socket文件描述符 |
函數原型 | int close(int sockfd) |
函數傳入值 | sockfd:socket文件描述符 |
函數返回值 | 成功:0 |
失敗:-1,失敗原因存於error中 | |
附加說明 | ① close函數默認功能是將套接字置爲“已關閉”標記,並立即返回給進程,這個套接字不能再爲該進程所用 ② 正常情況下,close將引發四個分節終止序列,但在終止前將發送已排隊的數據 ③ 如果套接字描述符訪問計數在調用close後大於0(多個進程共享同一個套接字的情況下),則不會引發TCP終止序列(即不會發送FIN分節) |
(7)shutdown函數原型
shutdown(終止socket通信) | ||
所需頭文件 | #include <sys/socket.h> | |
函數說明 | 終止socket通信 | |
函數原型 | int shutdown(int s, int how) | |
函數傳入值 | s | socket文件描述符 |
how | 0(SHUT_RD):關閉socket連接的讀這一半,不再接收套接字中的數據且現留在收緩衝區的數據作廢 | |
1(SHUT_WR):關閉socket連接的寫這一半(半關閉),但留在套接字發送緩衝區中的數據都會被髮送,後跟TCP連接終止序列,不管訪問計數是否大於0,此後將不能再執行對套接字的任何寫操作 | ||
2(SHUT_RDWR):socket連接的讀、寫都關閉 | ||
函數返回值 | 成功:0 | |
失敗:-1,失敗原因存於error中 |
(8)read函數原型
read(從打開的socket文件流中讀取數據) |
所需頭文件 | #include <unistd.h> |
函數說明 | 從打開的socket文件流中讀取數據,這裏僅說明此函數應用於socket的情況 |
函數原型 | ssize_t read(int fd, void *buf ,size_t count) |
函數傳入參數 | fd: socket文件描述符 |
count:最大讀取字節數 | |
函數傳出參數 | buf:讀取數據的首地址 |
函數返回值 | 成功:實際讀取字節數 |
失敗:-1,錯誤代碼存放在error中 | |
附加說明 | 調用函數read從socket文件流中讀取數據時,有如下幾種情況: ① 套接字接收緩衝區接收數據,返回接收到的字節數 ② TCP協議收到FIN數據,返回0 ③ TCP協議收到RST數據,返回-1,同時errno設置爲ECONNRESET ④ 進程阻塞過程中接收到信號,返回-1,同時errno設置爲EINTR |
(9)write函數原型
write(向socket文件流中寫入數據) |
所需頭文件 | #include <unistd.h> |
函數說明 | 向socket文件流中寫入數據,這裏僅說明此函數應用於socket的情況 |
函數原型 | ssize_t write (int fd,const void *buf,size_t count) |
函數傳入參數
| fd: socket文件描述符 |
buf:寫入數據的首地址 | |
count:最大寫入字節數 | |
函數返回值 | 成功:實際寫入的字節數 |
失敗:-1,錯誤代碼存放在error中 | |
附加說明 | 調用函數write向socket文件流寫數據時,有如下幾種情況: ① 套接字發送緩衝區有足夠空間,返回發送的字節數 ② TCP協議接收到RST數據,返回-1,同時errno設置爲ECONNRESET ③ 進程阻塞過程中接收到信號,返回-1,同時errno設置爲EINTR |
2. 高級套接字函數
recv和send函數提供了和read和write差不多的功能。不過它們提供了第四個參數來控制讀寫操作。
(1) send函數原型
send(通過socket文件描述符發送數據到對方) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 通過socket文件描述符發送數據到對方 | |
函數原型 | ssize_t send(int s, const void *buf, size_t len, int flags) | |
函數傳入值 | s | socket文件描述符 |
buf | 發送數據的首地址 | |
len | 發送數據的長度 | |
flags | 0:此時功能同write,flags還可以設爲以下標誌的組合 | |
MSG_OOB:發送帶外數據 | ||
MSG_DONTROUTE:告訴IP協議,目的主機在本地網絡,沒有必要查找路由表 | ||
MSG_DONTWAIT:設置爲非阻塞操作 | ||
MSG_NOSIGNAL:表示發送動作不願被SIGPIPE信號中斷 | ||
函數返回值 | 成功:實際發送的字節數 | |
失敗:-1,失敗原因存於error中 |
(2) recv函數原型
recv(通過socket文件描述符從對方接收數據) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 通過socket文件描述符從對方接收數據 | |
函數原型 | ssize_t recv(int s, void *buf, size_t len, int flags) | |
函數傳入值 | s | socket文件描述符 |
len | 可接收數據的最大長度 | |
flags | 0:此時功能同read,flags還可以設爲以下標誌的組合 | |
MSG_OOB:接收帶外數據 | ||
MSG_PEEK:查看數據標誌,返回的數據並不在系統中刪除,如果再次調用recv函數會返回相同的數據內容 | ||
MSG_DONTWAIT:設置爲非阻塞操作 | ||
MSG_WAITALL:強迫接收到len大小的數據後才返回,除非有錯誤或有信號產生 | ||
函數傳出值 | buf | 接收數據的首地址 |
函數返回值 | 成功:實際發送的字節數 | |
失敗:-1,失敗原因存於error中 |
3. 套接字屬性控制函數
系統提供getsockopt、setsockopt兩函數獲取和修改套接字結構中一些屬性,通過修改這些屬性,可以調整套接字的性能,進而調整應用程序的性能。
(1) getsockopt函數原型
getsockopt(獲取套接字的屬性) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 獲取套接字的屬性 | |
函數原型 | int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) | |
函數傳入值 | s | socket文件描述符 |
level | SOL_SOCKET:通用套接字選項 | |
IPPROTO_IP:IP選項 | ||
IPPROTO_TCP:TCP選項 | ||
optname | 訪問的選項名,具體見表18-3 | |
optlen | optval的長度 | |
函數傳出值 | optval | 取得的屬性值 |
函數返回值 | 成功:0 | |
失敗:-1,失敗原因存於error中 |
表18-3 套接字屬性表
level(級別) | optname(選項名 | 說明 | 數據類型 |
SOL_SOCKET
| SO_BROADCAST | 允許發送廣播數據 | int |
SO_DEBUG | 允許調試 | int | |
SO_DONTROUTE | 不查找路由 | int | |
SO_ERROR | 獲得套接字錯誤 | int | |
SO_KEEPALIVE | 保持連接 | int | |
SO_LINGER | 延遲關閉連接 | struct linger | |
SO_OOBINLINE | 帶外數據放入正常數據流 | int | |
SO_RCVBUF | 接收緩衝區大小 | int | |
SO_SNDBUF | 發送緩衝區大小 | int | |
SO_RCVLOWAT | 接收緩衝區下限 | int | |
SO_SNDLOWAT | 發送緩衝區下限 | int | |
SO_RCVTIMEO | 接收超時 | struct timeval | |
SO_SNDTIMEO | 發送超時 | struct timeval | |
SO_REUSERADDR | 允許重用本地地址和端口 | int | |
SO_TYPE | 獲得套接字類型 | int | |
SO_BSDCOMPAT | 與BSD系統兼容 | int | |
IPPROTO_IP
| IP_HDRINCL | 在數據包中包含IP首部 | int |
IP_OPTINOS | IP首部選項 | int | |
IP_TOS | 服務類型 | int | |
IP_TTL | 生存時間 | int | |
IPPRO_TCP | TCP_MAXSEG | TCP最大數據段的大小 | int |
CP_NODELAY | 不使用Nagle算法 | int |
(2)setsockopt函數原型
setsockopt(設置套接字的屬性) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 設置套接字的屬性 | |
函數原型 | int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) | |
函數傳入值 | s | socket文件描述符 |
level | SOL_SOCKET:通用套接字選項 | |
IPPROTO_IP:IP選項 | ||
IPPROTO_TCP:TCP選項 | ||
optname | 設置的選項名,具體見表18-3 | |
optval | 設置的屬性值 | |
optlen | optval的長度 | |
函數返回值 | 成功:0 | |
失敗:-1,失敗原因存於error中 |
(3)getsockopt、setsockopt函數舉例
sockopt.c源代碼如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
int main()
{
int sockfd,optval,optlen = sizeof(int);
int sndbuf = 0 ;
int rcvbuf = 0 ;
int flag;
if((sockfd = socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket") ;
return -1 ;
}
getsockopt(sockfd,SOL_SOCKET,SO_TYPE,&optval,&optlen);
printf("optval = %d\n",optval);
optlen = sizeof(sndbuf);
flag = getsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&sndbuf,&optlen);
printf("sndbuf=%d\n",sndbuf) ;
printf("flag=%d\n",flag) ;
sndbuf = 51200;
flag = setsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&sndbuf, optlen);
sndbuf=0 ;
flag = getsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&sndbuf,&optlen);
printf("sndbuf=%d\n",sndbuf) ;
printf("flag=%d\n",flag) ;
close(sockfd);
return 0 ;
}
編譯 gcc sockopt.c -o sockopt。
執行 ./sockopt, 執行結果如下:
optval = 1
sndbuf=16384
flag=0
sndbuf=102400
flag=0
4. UDP讀寫函數
UDP套接字是無連接協議,必須使用sendto函數發送數據,必須使用recvfrom函數接收數據,發送時需指明目的地址。sendto函數與send功能基本相同,recvfrom與recv功能基本相同,只不過sendto函數和recvfrom函數參數中都帶有對方地址信息,這兩個函數是專門爲UDP協議提供的。
(1)sendto函數原型
sendto(通過socket文件描述符發送數據到對方) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 通過socket文件描述符發送數據到對方,用於UDP協議 | |
函數原型 | ssize_t sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) | |
函數傳入值 | s | socket文件描述符 |
buf | 發送數據的首地址 | |
len | 發送數據的長度 | |
flags | 0:默認方式發送數據,flags還可以設爲以下標誌的組合 | |
MSG_OOB:發送帶外數據 | ||
MSG_DONTROUTE:告訴IP協議,目的主機在本地網絡,沒有必要查找路由表 | ||
MSG_DONTWAIT:設置爲非阻塞操作 | ||
MSG_NOSIGNAL:表示發送動作不願被SIGPIPE信號中斷 | ||
to | 存放目的主機IP地址和端口信息 | |
tolen | to的長度,可設置爲sizeof(struct sockaddr) | |
函數返回值 | 成功:實際發送的字節數 | |
失敗:-1,失敗原因存於error中 |
(2) recvfrom函數
recv(通過socket文件描述符從對方接收數據) | ||
所需頭文件 | #include <sys/types.h> #include <sys/socket.h> | |
函數說明 | 通過socket文件描述符從對方接收數據,用於UDP協議 | |
函數原型 | ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) | |
函數傳入值 | s | socket文件描述符 |
len | 可接收數據的最大長度 | |
flags | 0:默認方式接收數據,flags還可以設爲以下標誌的組合 | |
MSG_OOB:接收帶外數據 | ||
MSG_PEEK:查看數據標誌,返回的數據並不在系統中刪除,如果再次調用recv函數會返回相同的數據內容 | ||
MSG_DONTWAIT:設置爲非阻塞操作 | ||
MSG_WAITALL:強迫接收到len大小的數據後才返回,除非有錯誤或有信號產生 | ||
fromlen | from的長度,可設置爲sizeof(struct sockaddr) | |
函數傳出值 | buf | 接收數據的首地址 |
from | 存放發送方的IP地址和端口 | |
函數返回值 | 成功:實際發送的字節數 | |
失敗:-1,失敗原因存於error中 |
摘錄自《深入淺出Linux工具與編程》