位置:首頁 > 其他技術 > Socket編程 > Socket 核心函數

Socket 核心函數

本教學將介紹寫一個完整的TCP客戶端和服務器需要的套接字核心函數

以下是完整的客戶端和服務器的交互圖:

Socket Client Server

socket 函數:

要執行網絡I/O,進程必須做的第一件事是調用socket函數,指定所需的通信協議類型和協議族等。

#include <sys/types.h>
#include <sys/socket.h>

int socket (int family, int type, int protocol);

這個調用給一個套接字描述符,可以用在以後的係統調用,-1為出錯。

參數:

協議族: 指定協議族,是一個常量如下所示:

Family 描述
AF_INET IPv4 protocols
AF_INET6 IPv6 protocols
AF_LOCAL Unix domain protocols
AF_ROUTE Routing Sockets
AF_KEY Ket socket

本教學不談論除IPv4協議之外的其他協議

類型: 指定類想要的套接字。它可以取下列值之一:

類型 描述
SOCK_STREAM Stream socket
SOCK_DGRAM Datagram socket
SOCK_SEQPACKET Sequenced packet socket
SOCK_RAW Raw socket

協議: 參數應設置具體的協議類型如下,或低於0的係統的默認值

協議 描述
IPPROTO_TCP TCP transport protocol
IPPROTO_UDP UDP transport protocol
IPPROTO_SCTP SCTP transport protocol

connect 函數:

connect函數使用一個TCP客戶端,TCP服務器建立連接。

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

這個調用返回0,則它成功地連接到服務器,否則它給-1的錯誤。

參數:

  • sockfd: socket函數返回一個套接字描述符.

  • serv_addr 是一個指向struct sockaddr的包含目的IP地址和端口.

  • addrlen 設置sizeof為(struct sockaddr).

bind 函數:

分配一個本地協議地址綁定功能的套接字。與互聯網協議的協議地址是一個32位的IPv4地址或128比特的IPv6地址的組合,以及與一個16-bit的TCP或UDP端口號。僅由TCP服務器調用此函數。

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr,int addrlen);

這個調用返回0,則表示它成功綁定的地址,否則它給-1的錯誤。

參數:

  • sockfd: 是socket函數返回一個套接字描述符。

  • my_addr 是一個指向struct sockaddr的包含本地IP地址和端口。

  • addrlen 設置sizeof為(struct sockaddr).

可以把IP地址和端口自動設置

端口號0值意味著係統將隨機選擇一個端口和IP地址INADDR_ANY值是指服務器的IP地址將被自動分配。

server.sin_port = 0;  		     
server.sin_addr.s_addr = INADDR_ANY;

注: 不倫不類的端口和服務的教學,所有端口小於1024被保留。所以,可以設置1024以上的端口(但小於65535),同時設置端口不能正在被其他程序使用

listen 函數:

監聽listen函數被調用時,隻能由一個TCP服務器,它執行兩個動作:

  • 監聽函數將陷入被動套接字未連接的套接字,表明內核應該接受傳入的連接請求定向到該套接字。

  • 這個函數的第二個參數指定連接的內核應此套接字隊列的最大數目。

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);

這個調用成功返回0,否則它返回-1的錯誤。

參數:

  • sockfd: socket函數返回一個套接字描述符。

  • backlog 允許的連接數。

accept 函數:

由TCP服務器調用accept函數返回下一個已完成連接,從完整的連接隊列的前麵。以下是調用的簽名:

#include <sys/types.h>
#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

這個調用返回非負描述符成功,否則 -1 為出錯。返回的描述符被假定為一個客戶端的套接字描述符,描述的所有讀寫操作的工作在客戶端通信。

參數:

  • sockfd: socket函數返回一個套接字描述符。

  • cliaddr 是一個指向struct sockaddr,包含客戶端的IP地址和端口。

  • addrlen 它設置於sizeof(struct sockaddr).

send 函數:

發送功能是用來發送數據流套接字或連接的數據報套接字。如果想在未連接的數據報套接字發送數據,必須使用sendto()函數。

可以使用write()係統調用發送數據。此調用解釋在輔助功能的教學。

int send(int sockfd, const void *msg, int len, int flags);

這個調用返回發送出去的字節數,否則將返回-1錯誤.

參數:

  • sockfd: 是socket函數返回一個套接字描述符。

  • msg 要發送的數據是一個指針。

  • len 是要發送的數據(以字節為單位)長度。

  • flags 設置為 0.

recv 函數:

recv函數是用來接收數據流套接字或連接數據報套接字。如果想在未連接的數據報套接字接收數據,必須使用recvfrom()函數.

可以使用read()係統調用來讀取數據。此調用解釋在輔助功能的教學。

int recv(int sockfd, void *buf, int len, unsigned int flags);

這個調用返回讀入緩衝區的字節數,否則將返回-1錯誤。

參數:

  • sockfd: socket函數返回一個套接字描述符。

  • buf 緩衝區讀取信息。

  • len 最大的緩衝區的長度。

  • flags 設置為 0.

sendto 函數:

sendto函數用於未連接的數據報套接字發送數據。簡單地說,當使用SCOKET類型為SOCK_DGRAM

int sendto(int sockfd, const void *msg, int len, unsigned int flags,
		   const struct sockaddr *to, int tolen);

這個調用返回發送的字節數否則將返回-1錯誤。

參數:

  • sockfd: socket函數返回一個套接字描述符。

  • msg 要發送的數據是一個指針。

  • len 是要發送的數據(以字節為單位)的長度。

  • flags 設置為 0.

  • to 是一個指向結構sockaddr的主機要發送數據。

  • tolen is set it to sizeof(struct sockaddr).

recvfrom 函數:

recvfrom函數用於未連接的數據報套接字接收數據。簡單地說,當使用SCOKET類型為SOCK_DGRAM時適用。

int recvfrom(int sockfd, void *buf, int len, unsigned int flags
		     struct sockaddr *from, int *fromlen);

這個調用返回讀入緩衝區的字節數,否則將返回-1錯誤。

參數:

  • sockfd: socket函數返回一個套接字描述符。

  • buf 緩衝區讀取信息。

  • len 最大的緩衝區的長度。

  • flags 被設置為0。

  • from 是一個指向結構sockaddr的數據的主機被讀取。

  • fromlen 設置為sizeof(struct sockaddr)

close 函數:

close函數是用來關閉客戶端和服務器之間的通信。

int close( int sockfd );

這個調用成功返回0,否則返回-1錯誤。

參數:

  • sockfd: socket函數返回一個套接字描述符。

shutdown 函數:

shutdown函數用於正常關閉客戶端和服務器之間的通信。此函數提供了更多的控製在比較close函數。

int shutdown(int sockfd, int how);

這個調用成功返回0,否則返回-1錯誤。

參數:

  • sockfd: socket函數返回一個套接字描述符。

  • how: 放入一個數字:

    • 0 表示接收不允許的,

    • 1 表明發送不允許

    • 2 表明禁止發送和接收。如果設置為2,它與close()同樣。

select 函數:

select函數顯示指定文件的描述符是以待準備就緒讀取,準備寫入或有一個錯誤條件。

當應用程序調用recv或recvfrom被阻塞,直到數據到達該套接字。一個應用程序可以做其他有用的處理,而輸入的數據流是空的。另一種情況是,當應用程序從多個套接字接收數據。

調用recv或recvfrom防止立即接收數據與其他Socket上,它的輸入隊列中冇有數據。 select函數調用來解決這個問題,允許程序輪詢所有的套接字手柄,看看他們是否有無阻塞讀取和寫入操作。

 int select(int  nfds,  fd_set  *readfds,  fd_set  *writefds,
     fd_set *errorfds, struct timeval *timeout);

這個調用成功返回0,否則返回-1錯誤。

參數:

  • nfds: specifies the range of file descriptors to be tested. The select() function tests file descriptors in the range of 0 to nfds-1

  • readfds:points to an object of type fd_set that on input specifies the file descriptors to be checked for being ready to read, and on output indicates which file descriptors are ready to read. Can be NULL to indicate an empty set.

  • writefds:points to an object of type fd_set that on input specifies the file descriptors to be checked for being ready to write, and on output indicates which file descriptors are ready to write Can be NULL to indicate an empty set.

  • exceptfds :points to an object of type fd_set that on input specifies the file descriptors to be checked for error conditions pending, and on output indicates which file descriptors have error conditions pending. Can be NULL to indicate an empty set.

  • timeout :poins to a timeval struct that specifies how long the select call should poll the descriptors for an available I/O operation. If the timeout value is 0, then select will return immediately. If the timeout argument is NULL, then select will block until at least one file/socket handle is ready for an available I/O operation. Otherwise select will return after the amount of time in the timeout has elapsed OR when at least one file/socket descriptor is ready for an I/O operation.

返回值選擇多少文件描述符集指定的句柄,選擇返回0,準備就緒I/O如果超時字段指定的時限到達時。下麵的宏存在操縱一個文件描述符集:

  • FD_CLR(fd, &fdset): 清除位文件描述符fd文件描述符集fdset。

  • FD_ISSET(fd, &fdset): 返回一個非零值,如果該位被設置為文件描述符fd文件描述符集fdset指向,否則返回0。

  • FD_SET(fd, &fdset): 位設置文件描述符fd文件描述符集fdset。

  • FD_ZERO(&fdset): 初始化文件描述符集fdset所有文件描述符的零位。

這些宏的行為是不確定的,如果參數fd小於0或大於或等於FD_SETSIZE。

例如:

fd_set fds;

struct timeval tv;

/* do socket initialization etc.

tv.tv_sec = 1;
tv.tv_usec = 500000;

/* tv now represents 1.5 seconds */
 
FD_ZERO(&fds);

/* adds sock to the file descriptor set */
FD_SET(sock, &fds); 

/* wait 1.5 seconds for any data to be read 
   from any single socket */

select(sock+1, &fds, NULL, NULL, &tv);
if (FD_ISSET(sock, &fds))
{
   recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
   /* do something */
}
else
{
   /* do something else */
}