Berkeley套接字

伯克利套接字 ，又稱為BSD 套接字(--)是一種應用程式介面與Unix域套接字，包括了一個用C語言寫成的應用程式開發庫，主要用於實現程序間通訊，在計算機網路通訊方面被廣泛使用。Berkeley套接字的一套應用程式介面。然而，由於AT&amp;T的專利保護著UNIX，所以只有在1989年伯克利大學才能自由地釋出自己的作業系統和網路庫。Berkeley套接字應用程式介面形成了事實上的網路套接字的標準精髓。 大多數其他的程式語言使用與這套用C語言寫成的應用程式介面 類似的介面。 這套應用程式介面也被用於Unix域套接字的介面。這種基於流的傳輸層介面為套接字應用程式介面提供了一種選擇。 #重定向Berkeley套接字介面.Berkeley套接字介面，一個應用程式介面，使用一個Internet套接字的概念，使主機間或者一臺計算機上的程序間可以通訊。 它可以在很多不同的輸入/輸出裝置和驅動之上執行，儘管這有賴於作業系統的具體實現。 介面實現用於TCP/IP協議，因此它是維持Internet的基本技術之一。 它是由加利福尼亞的伯克利大學開發，最初用於Unix系統。 如今，所有的現代作業系統都有一些源於Berkeley套接字介面的實現，它已成為連線Internet的標準介面。套接字介面的接入有三個不同的級別，最基礎的也是最有效的就是raw socket級別接入。 很少的應用程式需要在外向通訊控制的這個級別接入，所以raw socket級別是隻為了用於開發計算機Internet相關技術的。 最近幾年，大多數的作業系統已經實現了對它的全方位支援，包括Windows XP。使用Berkeley套接字的系統.由於Berkeley套接字是第一個socket，大多數程式設計師很熟悉它們，所以大量系統把伯克利套接字作為其主要的網路API。一個不完整的列表如下：標頭檔案.Berkeley套接字介面的定義在幾個標頭檔案中。這些檔案的名字和內容與具體的實現之間有些許的不同。 大體上包括：; codice_1核心BSD套接字核心函式和資料結構。AF_INET、AF_INET6 地址集和它們相應的協議集PF_INET、PF_INET6. 廣泛用於Internet，這些包括了IP地址和TCP、UDP埠號。; codice_2AF_INET 和AF_INET6 地址家族和他們對應的協議家族 PF_INET 和 PF_INET6。在網際網路程式設計中廣泛使用，包括IP地址以及TCP和UDP埠號。; codice_3PF_UNIX/PF_LOCAL 地址集。用於執行在一臺計算機上的程式間的本地通訊，不用於網路通訊。; codice_4處理數值型IP地址的函式。; codice_5將協議名和主機名翻譯為數值地址的函式。搜尋本地資料以及DNS。套接字API函式.這個列表是一個Berkeley套接字API庫提供的函式或者方法的概要：更多的細節如下給出。socket().codice_6 為通訊建立一個端點，為套接字返回一個檔案描述符。 socket() 有三個引數：如果發生錯誤，函式返回值為-1。 否則，函式會返回一個代表新分配的描述符的整數。int socket(int domain, int type, int protocol);bind().codice_7 為一個套接字分配地址。當使用codice_6建立套接字後，只賦予其所使用的協議，並未分配地址。在接受其它主機的連線前，必須先呼叫codice_7為套接字分配一個地址。codice_7有三個引數：如果發生錯誤，函式返回值為-1，否則為0。int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);listen().當socket和一個地址繫結之後，codice_8函式會開始監聽可能的連線請求。然而，這隻能在有可靠資料流保證的時候使用，例如：資料型別(codice_28, codice_30)。listen()函式需要兩個引數：一旦連線被接受，返回0表示成功，錯誤返回-1。原型:int listen(int sockfd, int backlog);accept().當應用程式監聽來自其他主機的面對資料流的連線時，透過事件通知它。必須用 codice_10函式初始化連線。 codice_10為每個連線創立新的套接字並從監聽佇列中移除這個連線。它使用如下引數：返回新的套接字描述符，出錯返回-1。進一步的通訊必須透過這個套接字。Datagram 套接字不要求用accept()處理，因為接收方可能用監聽套接字立即處理這個請求。int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);connect().codice_9系統呼叫為一個套接字設定連線，引數有檔案描述符和主機地址。某些型別的套接字是無連線的，大多數是UDP協議。對於這些套接字，連線時這樣的：預設傳送和接收資料的主機由給定的地址確定，可以使用 send()和 recv()。返回-1表示出錯，0表示成功。int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);select().int select (int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout);getsockname() 和 getpeername ().int getsockname (SOCKET s, struct sockaddr *name, int* namelen);getsockname函式獲取已繫結的套介面本地協議地址。int getpeername (SOCKET s, struct sockaddr *name, int* namelen);getpeername函式獲得與指定套介面連線的遠端資訊。gethostbyname() 和 gethostbyaddr().codice_18 和 codice_19函式是用來解析主機名和地址的。可能會使用DNS服務或者本地主機上的其他解析機制。返回一個指向 struct hostent的指標，這個結構體描述一個IP主機。函式使用如下引數：出錯返回NULL指標，可以透過檢查 h_errno 來確定是臨時錯誤還是未知主機。正確則返回一個有效的 struct hostent *。這些函式並不是伯克利套接字嚴格的組成部分。這些函式可能是過時了，只能處理IPv4地址。在IPv6中，替代的新函式是 getaddrinfo() and getnameinfo(), 這些新函式是基於"addrinfo"資料結構。參考。struct hostent *gethostbyname(const char *name);struct hostent *gethostbyaddr(const void *addr, int len, int type);setsockopt().int setsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);setsockopt函式用來設定套接字選項。引數：在socket層, 有以下一些選項:ioctlsocket.int ioctlsocket(_In_ SOCKET s, _In_ long cmd, _Inout_ u_long *argp);根據第二個引數的取值，設定socket I/O模式：inet_pton與inet_ntop.inet_pton與inet_ntop兩個函式，在ASCII字元描述的IP地址與網路位元組序的4位元組IP地址之間轉換。 字母"n"與"p"，分別是numerical與presentation的縮寫。協議和地址.套接字API是Unix網路的通用介面，允許使用各種網路協議和地址。下面列出了一些例子，在現在的 Linux 和 BSD 中一般都已經實現了。PF_LOCAL, PF_UNIX, PF_FILELocal to host (pipes and file-domain)PF_INET IP protocol familyPF_AX25 Amateur Radio AX.25PF_IPX Novell Internet ProtocolPF_APPLETALK Appletalk DDPPF_NETROM Amateur radio NetROMPF_BRIDGE Multiprotocol bridgePF_ATMPVC ATM PVCsPF_X25 Reserved for X.25 projectPF_INET6 IP version 6PF_ROSE Amateur Radio X.25 PLPPF_DECnet Reserved for DECnet projectPF_NETBEUI Reserved for 802.2LLC projectPF_SECURITY Security callback pseudo AFPF_KEY PF_KEY key management APIPF_NETLINK, PF_ROUTErouting APIPF_PACKET Packet familyPF_ASH AshPF_ECONET Acorn EconetPF_ATMSVC ATM SVCsPF_SNA Linux SNA ProjectPF_IRDA IRDA socketsPF_PPPOX PPPoX socketsPF_WANPIPE Wanpipe API socketsPF_BLUETOOTH Bluetooth socketssocket的通用address描述結構sockaddr是一個16位元組大小的結構，sa_family可以認為是socket address family的縮寫。另外的14位元組是用來描述地址。當指定sa_family=AF_INET之後，sa_data的形式也就被固定了下來：最前端的2位元組用於記錄16位的埠，緊接著的4位元組用於記錄32位的IP地址，最後的8位元組清空為零。struct sockaddrunsigned short sa_family;char sa_data[14];struct sockaddr_in //means socket address internetunsigned short sin_family; //sin means socket (address) internetunsigned short sin_port;struct in_addr sin_addr;char sin_zero[8];struct in_addrunsigned long s_addr; // means source address使用TCP的伺服器客戶機舉例.伺服器.設定一個簡單的TCP伺服器涉及下列步驟：/* Server code in C */#include#include#include#include#include#include#include#includeint main(void)struct sockaddr_in stSockAddr;int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);if(-1 == SocketFD)perror("can not create socket");exit(EXIT_FAILURE);memset(&amp;stSockAddr, 0, sizeof(struct sockaddr_in));stSockAddr.sin_family = AF_INET;stSockAddr.sin_port = htons(1100);stSockAddr.sin_addr.s_addr = INADDR_ANY;if(-1 == bind(SocketFD,(const struct sockaddr *)&amp;stSockAddr, sizeof(struct sockaddr_in)))perror("error bind failed");close(SocketFD);exit(EXIT_FAILURE);if(-1 == listen(SocketFD, 10))perror("error listen failed");close(SocketFD);exit(EXIT_FAILURE);for(;;)int ConnectFD = accept(SocketFD, NULL, NULL);if(0 &gt; ConnectFD)perror("error accept failed");close(SocketFD);exit(EXIT_FAILURE);/* perform read write operations ... */shutdown(ConnectFD, SHUT_RDWR);close(ConnectFD);close(SocketFD);return 0;Python實現：from socket import *from time import ctimeHOST="PORT=1100BUFSIZ=1024ADDR=(HOST, PORT)sock=socket(AF_INET, SOCK_STREAM)sock.bind(ADDR)sock.listen(5)while True:print('waiting for connection')tcpClientSock, addr=sock.accept()print('connect from ', addr)while True:try:data=tcpClientSock.recv(BUFSIZ)except:print(e)tcpClientSock.close()breakif not data:breaks='Hi,you send me :[%s] %s' %(ctime(), data.decode('utf8'))tcpClientSock.send(s.encode('utf8'))print([ctime()], ':', data.decode('utf8'))tcpClientSock.close()sock.close()客戶機.建立一個客戶機連線涉及以下步驟：/* Client code in C */#include#include#include#include#include#include#include#includeint main(void)struct sockaddr_in stSockAddr;int Res;int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);if (-1 == SocketFD)perror("cannot create socket");exit(EXIT_FAILURE);memset(&amp;stSockAddr, 0, sizeof(struct sockaddr_in));stSockAddr.sin_family = AF_INET;stSockAddr.sin_port = htons(1100);Res = inet_pton(AF_INET, "192.168.1.3", &amp;stSockAddr.sin_addr);if (0 &gt; Res)perror("error: first parameter is not a valid address family");close(SocketFD);exit(EXIT_FAILURE);else if (0 == Res)perror("char string (second parameter does not contain valid ipaddress");close(SocketFD);exit(EXIT_FAILURE);if (-1 == connect(SocketFD, (const struct sockaddr *)&amp;stSockAddr, sizeof(struct sockaddr_in)))perror("connect failed");close(SocketFD);exit(EXIT_FAILURE);/* perform read write operations ... */shutdown(SocketFD, SHUT_RDWR);close(SocketFD);return 0;Python實現：from socket import *HOST='192.168.1.3'PORT=1100BUFSIZ=1024ADDR=(HOST, PORT)client=socket(AF_INET, SOCK_STREAM)client.connect(ADDR)while True:data=input('&gt;')if not data:breakclient.send(data.encode('utf8'))data=client.recv(self.BUFSIZ)if not data:breakprint(data.decode('utf8'))使用UDP的伺服器客戶機舉例.使用者資料報協議是一個不保證正確傳輸的無連線協議。 UDP資料包可能會亂序到達，多次到達或者直接丟失。但是設計的負載比TCP小。UDP地址空間，也即是UDP埠，和TCP埠是沒有關係的。伺服器.Code may set up a UDP server on port 7654 as follows:int main(void)int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);struct sockaddr_in sa;char buffer[1024];ssize_t recsize;socklen_t fromlen;memset(&amp;sa, 0, sizeof(sa));sa.sin_family = AF_INET;sa.sin_addr.s_addr = INADDR_ANY;sa.sin_port = htons(7654);if (-1 == bind(sock,(struct sockaddr *)&amp;sa, sizeof(struct sockaddr)))perror("error bind failed");close(sock);exit(EXIT_FAILURE);for (;;)printf ("recv test...\n");recsize = recvfrom(sock, (void *)buffer, 1024, 0, (struct sockaddr *)&amp;sa, &amp;fromlen);if (recsize &lt; 0)fprintf(stderr, "%s\n", strerror(errno));printf("recsize: %d\n ",recsize);sleep(1);printf("datagram: %s\n",buffer);上面的無限迴圈用recvfrom()接收給UDP埠7654的資料包。使用如下引數：同樣功能的Python實現：import socketport=7654s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)s.bind((",port))print('正在等待接入...')while True:#接收一個數據data,addr=s.recvfrom(1024)print('Received:',data,'from',addr)客戶機.用UDP資料包傳送一個"Hello World!" 給地址127.0.0.1，埠 7654 。int main(int argc, char *argv[])int sock;struct sockaddr_in sa;int bytes_sent, buffer_length;char buffer[200];buffer_length = snprintf(buffer, sizeof(buffer), "Hello World!");sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);if (-1 == sock) /* if socket failed to initialize, exit */printf("Error Creating Socket");exit(EXIT_FAILURE);memset(&amp;sa, 0, sizeof(sa));sa.sin_family = AF_INET;sa.sin_addr.s_addr = htonl(0x7F000001);sa.sin_port = htons(7654);bytes_sent = sendto(sock, buffer, buffer_length, 0,(struct sockaddr*)&amp;sa, sizeof (struct sockaddr_in));if (bytes_sent &lt; 0)printf("Error sending packet: %s\n", strerror(errno));close(sock); /* close the socket */return 0;codice_68指定要傳送資料的指標, codice_69指定快取內容的大小。同樣功能的Python實現：import socketport=7654host='localhost's=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)s.sendto(b'Hello World!',(host,port))The "de jure" standard definition of the Sockets interface is contained in the POSIX standard, known as:Information about this standard and ongoing work on it is available from the Austin website.The IPv6 extensions to the base socket API are documented in RFC 3493 and RFC 3542.本條目部分或全部內容出自以GFDL授權釋出的《自由線上電腦詞典》。