服務端套接字創建過程
第一步:調用socket函數創建套接字
//成功時返回文件表述符,失敗時返回-1
int socket(int __domain, int __type, int __protocol)
- domain:套接字使用的協議族(Protocol Family)信息;
- type:套接字數據傳輸類型信息;
- protocol:計算機間通信中使用的協議信息;
協議族(Protocol Family)
Name Purpose Man page
AF_UNIX Local communication unix(7)
AF_LOCAL Synonym for AF_UNIX
AF_INET IPv4 Internet protocols ip(7)
AF_IPX IPX - Novell protocols
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_PACKET Low-level packet interface packet(7)
套接字類型(Type):是指套接字的傳輸方式
- 面向連接的套接字(SOCK_STREAM)
- 傳輸過程中數據不會消失
- 按序傳輸數據
- 傳輸的數據不存在數據邊界(Boundary):調用了三次write傳遞了100字節,接受者僅一次read接收了全部
- 面向消息的套接字(SOCK_DGRAM)
- 強調快速傳輸而非傳輸順序
- 傳輸的數據可能丟失也可能損毀
- 傳輸的數據有數據邊界
- 限制每次傳輸的數據大小
協議的最終選擇
/* Standard well-defined IP protocols. */
enum
{
IPPROTO_IP = 0, /* Dummy protocol for TCP. */
#define IPPROTO_IP IPPROTO_IP
IPPROTO_ICMP = 1, /* Internet Control Message Protocol. */
#define IPPROTO_ICMP IPPROTO_ICMP
IPPROTO_IGMP = 2, /* Internet Group Management Protocol. */
#define IPPROTO_IGMP IPPROTO_IGMP
IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94). */
#define IPPROTO_IPIP IPPROTO_IPIP
IPPROTO_TCP = 6, /* Transmission Control Protocol. */
#define IPPROTO_TCP IPPROTO_TCP
IPPROTO_EGP = 8, /* Exterior Gateway Protocol. */
#define IPPROTO_EGP IPPROTO_EGP
IPPROTO_PUP = 12, /* PUP protocol. */
#define IPPROTO_PUP IPPROTO_PUP
IPPROTO_UDP = 17, /* User Datagram Protocol. */
#define IPPROTO_UDP IPPROTO_UDP
IPPROTO_IDP = 22, /* XNS IDP protocol. */
#define IPPROTO_IDP IPPROTO_IDP
IPPROTO_TP = 29, /* SO Transport Protocol Class 4. */
#define IPPROTO_TP IPPROTO_TP
IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol. */
#define IPPROTO_DCCP IPPROTO_DCCP
IPPROTO_IPV6 = 41, /* IPv6 header. */
#define IPPROTO_IPV6 IPPROTO_IPV6
IPPROTO_RSVP = 46, /* Reservation Protocol. */
#define IPPROTO_RSVP IPPROTO_RSVP
IPPROTO_GRE = 47, /* General Routing Encapsulation. */
#define IPPROTO_GRE IPPROTO_GRE
IPPROTO_ESP = 50, /* encapsulating security payload. */
#define IPPROTO_ESP IPPROTO_ESP
IPPROTO_AH = 51, /* authentication header. */
#define IPPROTO_AH IPPROTO_AH
IPPROTO_MTP = 92, /* Multicast Transport Protocol. */
#define IPPROTO_MTP IPPROTO_MTP
IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET. */
#define IPPROTO_BEETPH IPPROTO_BEETPH
IPPROTO_ENCAP = 98, /* Encapsulation Header. */
#define IPPROTO_ENCAP IPPROTO_ENCAP
IPPROTO_PIM = 103, /* Protocol Independent Multicast. */
#define IPPROTO_PIM IPPROTO_PIM
IPPROTO_COMP = 108, /* Compression Header Protocol. */
#define IPPROTO_COMP IPPROTO_COMP
IPPROTO_SCTP = 132, /* Stream Control Transmission Protocol. */
#define IPPROTO_SCTP IPPROTO_SCTP
IPPROTO_UDPLITE = 136, /* UDP-Lite protocol. */
#define IPPROTO_UDPLITE IPPROTO_UDPLITE
IPPROTO_MPLS = 137, /* MPLS in IP. */
#define IPPROTO_MPLS IPPROTO_MPLS
IPPROTO_RAW = 255, /* Raw IP packets. */
#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_MAX
};
第二步:調用bind函數分配IP地址和端口號
//成功時返回0,失敗時返回-1
int bind(int __fd, const struct sockaddr *__addr, socklen_t __len)
- __fd:要分配地址信息(IP地址和端口號)的套接字文件表述符。
- __addr:存有地址信息的結構體變量地址值
- __len:第二個結構體變量的長度
第三步:調用listen函數轉為可接收請求狀態
//成功時返回0,失敗時返回-1
int listen(int __fd, int __n)
- __fd:希望進入等待連接請求狀態的套接字文件描述符,傳遞的描述符套接字參數成為服務器端套接字(監聽套接字)。
- __n:連接請求等待隊列的長度。
第四步:調用accept函數受理連接請求
//成功時返回0,失敗時返回-1
int accept(int __fd, struct sockaddr *__restrict__ __addr, socklen_t *__restrict__ __addr_len)
- __fd:服務器套接字的文件描述符。
- __addr:保存發起連接請求的客戶端地址信息地變量地址值,調用函數后向傳遞來的地址變量參數填充客戶端的地址信息。
- __addr_len:第二個參數__addr結構體的長度,但是存有長度的變量地址。函數調用完后,該變量即被填入客戶端地址長度。
客戶端創建套接字過程
第一步:調用socket函數創建套接字
//成功時返回文件表述符,失敗時返回-1
int socket(int __domain, int __type, int __protocol)
第二步:調用socket函數向服務器端發送連接請求
//成功時返回0,失敗時返回-1
int connect(int __fd, const struct sockaddr *__addr, socklen_t __len)
編寫
echosrv.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char** argv) {
// 1. 創建套接字
int listenfd;
//協議族 套接字類型 協議類型
if ((listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
}
// 2. 分配套接字地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof servaddr);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
// servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton("127.0.0.1", &servaddr.sin_addr);
int on = 1;
// 確保time_wait狀態下同一端口仍可使用
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
{
perror("setsockopt");
}
// 3. 綁定套接字地址
if (bind(listenfd, (struct sockaddr*) &servaddr, sizeof servaddr) < 0) {
perror("bind");
}
// 4. 等待連接請求狀態
if (listen(listenfd, SOMAXCONN) < 0) {
perror("listen");
}
// 5. 允許連接
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof peeraddr;
int connfd;
if ((connfd = accept(listenfd, (struct sockaddr *) &peeraddr, &peerlen)) < 0) {
perror("accept");
}
printf("id = %s, ", inet_ntoa(peeraddr.sin_addr));
printf("port = %d\n", ntohs(peeraddr.sin_port));
// 6. 數據交換
char recvbuf[1024];
while (1)
{
memset(recvbuf, 0, sizeof recvbuf);
int ret = read(connfd, recvbuf, sizeof recvbuf);
if (ret == 0)
{
printf("client close\n");
break;
} else if (ret == -1)
{
perror("read");
}
fputs(recvbuf, stdout);
write(connfd, recvbuf, ret);
}
// 7. 斷開連接
close(connfd);
close(listenfd);
return 0;
}
echocli.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
// 1. 創建套接字
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
}
// 2. 分配套接字地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof servaddr);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
// servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton("127.0.0.1", &servaddr.sin_addr);
// 3. 請求鏈接
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof servaddr) < 0) {
perror("connect");
}
// 4. 數據交換
char recvbuf[1024] = {0};
char sendbuf[1024] = {0};
while (fgets(sendbuf, sizeof sendbuf, stdin) != NULL) // 鍵盤輸入獲取
{
// memset(recvbuf, 0, sizeof recvbuf);
// memset(sendbuf, 0, sizeof sendbuf);
write(sockfd, sendbuf, sizeof sendbuf); // 寫入服務器
int ret = read(sockfd, recvbuf, sizeof recvbuf); // 服務器讀取
if (ret == 0)
{
printf("server close\n");
break;
} else if (ret == -1)
{
perror("read");
}
fputs(recvbuf, stdout); // 服務器返回數據輸出
// 清空
memset(recvbuf, 0, sizeof recvbuf);
memset(sendbuf, 0, sizeof sendbuf);
}
// 5. 斷開連接
close(sockfd);
return 0;
}
