socket, 又稱爲"套接字"或者"插座". 是操做系統提供的一種進程間通訊機制.目前大多用於不一樣網絡設備之間的通訊. socket 位於應用層與傳輸層之間, 經過傳遞給 socket 不一樣的參數, socket 最終選擇不同的協議(TCP/UDP等), 也就是說 socket 其實傳輸層協議簇的軟件抽象.html
圖片來自於網絡linux
在網絡應用中, 通訊的兩個進程之間主要用的是客戶端/服務器 (C/S)模式, 即客戶向服務器發出服務請求,服務器接收到請求後,提供相應的服務. socket 最開始設計於 Unix, Unix/Linux 設計哲學是 "一切皆文件", 所以,咱們能夠像文件操做(open/read/write/close)同樣來操做 socket.git
下圖展現展現 C/S 之間如何使用 socketgithub
對於 TCP 服務端來講:編程
對於客戶端來講就比較的簡單: 建立一個socket, 而後鏈接服務器(connect), 完成對應的數據操做(讀寫), 完成後關閉(close);swift
int socket(int domain, int type, int protocol);
socket 建立返回一個 socket 描述符, 跟文件描述符相似,後面的讀寫都須要依賴於這個描述符.服務器
協議族決定了使用的 socket 地址類型;網絡
AF_UNIX
或 AF_LOCAL
, 使用地址類型爲: struct sockaddr_un
struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* Pathname */ };
AF_INET
, ipv4, 使用地址類型爲: struct sockaddr_in
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
AF_INET6
, ipv6, 使用地址類型爲: sockaddr_in6
struct sockaddr_in6 { sa_family_t sin6_family; /* AF_INET6 */ in_port_t sin6_port; /* port number */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */ }; struct in6_addr { unsigned char s6_addr[16]; /* IPv6 address */ };
AF_X25
/ AF_AX25
/ AF_NETLINK
等),其餘類型目前尚未涉及到,不作過多討論,感興趣能夠訪問手冊進行研究;表示 socket 通訊的類型dom
指定實際使用的傳輸協議, 傳 0 表示根據前面的 domain 和 type 選擇協議;socket
其餘的函數沒有什麼特別須要注意的地方,直接參考下面例子以及手冊就能看懂
當給 socket()
的協議族傳入(AF_UNIX
或 AF_LOCAL
)時,可表示Unix域socket, 用於本地進程間通訊.
其中地址類型中的 sun_path
是一個值得注意的地方,根據文檔所描述,有三種類型
+ pathname: 文件類型,這種文件須要服務端與客戶端對文件都有操做權限, 程序執行過程當中,能夠看到對應的文件.
+ unnamed
+ abstract: 抽象類型, 這種類型與 pathname 區別在於,給 sun_path 的第一個值爲 \0
;
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <string.h> #include <stddef.h> #include <unistd.h> static const char *SERVERNAME = "@servername"; int main(int argc, char const *argv[]) { int sockfd; struct sockaddr_un serverAddr; socklen_t len; char buffer[1024]; sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) { perror("init fail"); exit(1); } memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sun_family = AF_UNIX; strncpy(serverAddr.sun_path, SERVERNAME, sizeof(serverAddr.sun_path)); serverAddr.sun_path[0] = '\0'; len = offsetof(struct sockaddr_un, sun_path) + sizeof(SERVERNAME); if (connect(sockfd, (struct sockaddr *)&serverAddr, len) < 0) { perror("connect fail"); exit(1); } while (fgets(buffer, sizeof(buffer), stdin) != NULL) { write(sockfd, buffer, strlen(buffer)); } close(sockfd); return 0; }
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <string.h> #include <stddef.h> #include <unistd.h> static const char *SERVERNAME = "@servername"; int main(int argc, char const *argv[]) { int sockfd; struct sockaddr_un serverAddr; struct sockaddr_un clientAddr; socklen_t clientLen; socklen_t serverLen; char buffer[1024]; int connfd; if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("init fail"); exit(1); } // 2. 設置對應地址,而後進行 connect memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sun_family = AF_UNIX; strncpy(serverAddr.sun_path, SERVERNAME, sizeof(serverAddr.sun_path)); // abstract & calculate length serverAddr.sun_path[0] = '\0'; serverLen = offsetof(struct sockaddr_un, sun_path) + sizeof(SERVERNAME); if (bind(sockfd, (struct sockaddr *)&serverAddr, serverLen) < 0) { perror("bind fail"); exit(1); } if (listen(sockfd, 20) < 0) { perror("listen fail"); exit(1); } while (1) { // 阻塞到有客戶端連接 if ((connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientLen)) < 0) { printf("accept fail \r\n"); continue; } printf("accept success \r\n"); while(1) { memset(buffer, 0, sizeof(buffer)); int n = read(connfd, buffer, sizeof(buffer)); if (n < 0) { perror("read error"); break; } else if(n == 0) { printf("EOF\n"); break; } printf("received: %s", buffer); } close(connfd); } close(sockfd); return 0; }
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #include <netinet/ip.h> #include <string.h> #include <arpa/inet.h> int main(int argc, char const *argv[]) { // ipv4 int sockfd; struct sockaddr_in addr_in; char buffer[1000]; if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("init socket fail"); exit(1); } bzero(&addr_in, sizeof(addr_in)); addr_in.sin_family = AF_INET; addr_in.sin_port = htons(9898); addr_in.sin_addr.s_addr = inet_addr("103.101.153.8"); if ((connect(sockfd, (struct sockaddr *)&addr_in, sizeof(addr_in)))<0) { perror("connect fail"); exit(1); } while (fgets(buffer, sizeof(buffer), stdin)) { write(sockfd, buffer, strlen(buffer)); } close(sockfd); return 0; }
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <string.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> int main(int argc, char const *argv[]) { int serfd, clifd; struct sockaddr_in seraddr_in, cliaddr_in; socklen_t clilen; char buffer[1000]; if ((serfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("init socket fail"); exit(1); } bzero(&seraddr_in, sizeof(seraddr_in)); seraddr_in.sin_family = AF_INET; seraddr_in.sin_port = htons(9898); seraddr_in.sin_addr.s_addr = inet_addr("103.101.153.8"); if (bind(serfd, (struct sockaddr *)&seraddr_in, sizeof(seraddr_in)) < 0) { perror("bind fail"); exit(1); } if (listen(serfd, 20) < 0) { perror("listen fail"); exit(1); } while (1) { if ((clifd = accept(serfd, (struct sockaddr *)&cliaddr_in, &clilen)) < 0) { perror("accept fail"); continue; } while (1) { memset(buffer, 0, sizeof(buffer)); int len = read(clifd, buffer, sizeof(buffer)); if (len < 0 ) { perror("read fail"); break; } if (len == 0) { perror("EOF"); break; } printf("receive: %s", buffer); } close(clifd); } close(serfd); return 0; }
對比本地與網絡通訊,就是選擇的協議族以及對應的地址不同而已.
參考連接: