TCP/IP的TCP socket通訊過程
阿新 • • 發佈:2019-02-08
傳統的TCP/IP通訊過程依賴於socket,位於應用層和傳輸層之間,使得應用程式可以進行通訊。相當於港口城市的碼頭,使得城市之間可以進行貨物流通。伺服器和客戶端各有不同的通訊流程。
一、伺服器
1、建立連線階段
- 呼叫socket(),分配檔案描述符,即監聽套接字
- 呼叫bind(),將套接字與本地IP地址和埠繫結
- 呼叫listen(),監聽特定埠,socket()建立的套接字是主動的,呼叫listen使得該檔案描述符為監聽套接字,變主動為被動
- 呼叫accept(),阻塞等待客戶端連線
2、資料互動階段
- 呼叫read(),阻塞等待客戶端傳送的請求,收到請求後從read()返回,處理客戶端請求
- 呼叫write(),將處理結果傳送給客戶端,然後繼續呼叫read()等待客戶端請求
3、關閉連線
- 當read()返回0的時候,說明客戶端發來了FIN資料包,即關閉連線,也會呼叫close()關閉連線套接字和監聽套接字
二、客戶端
1、建立連線階段
- 呼叫socket(),分配檔案描述符
- 呼叫connect(),向伺服器傳送建立連線請求
2、資料互動階段
- 呼叫write(),將請求傳送給伺服器
- 呼叫read(),阻塞等待伺服器應答
3、關閉連線
- 當沒有資料傳送的時候,呼叫close()關閉連線套接字,即關閉連線,向伺服器傳送FIN資料報
三、TCP通訊過程
四、C語言程式碼
伺服器端
客戶端/* File name: server.c*/ //伺服器端 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #define MAXLINE 4096 #define PORT 8000 int main(void){ //定義伺服器監聽套接字和連線套接字 int listen_fd = -1, connect_fd = -1;//初始化為-1 struct sockaddr_in servaddr;//定義伺服器對應的套接字地址 //伺服器接收和傳送緩衝區 char sendbuf[MAXLINE], recbuf[MAXLINE]; //初始化套接字地址結構體 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET;//IPv4 servaddr.sin_port = htons(PORT);//設定監聽埠 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//表示接收任意IP的連線請求 //建立套接字 if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ //如果建立套接字失敗,返回錯誤資訊 //strerror(int errnum)獲取錯誤的描述字串 printf("create socket error: %s(error: %d)\n", strerror(errno), errno); exit(0); } //繫結套接字和本地IP地址和埠 if(bind(listen_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){ //綁定出現錯誤 printf("bind socket error: %s(error: %d)\n", strerror(errno), errno); exit(0); } //使得listen_fd變成監聽描述符 if(listen(listen_fd, 10) == -1){ printf("listen socket error: %s(error: %d)\n", strerror(errno), errno); exit(0); } //accept阻塞等待客戶端請求 printf("等待客戶端發起連線\n"); while(1){ if((connect_fd = accept(listen_fd, (struct sockaddr*)NULL, NULL)) == -1){ printf("accept socket error: %s(error: %d)\n", strerror(errno), errno); continue; } //可以一直保持連線 while(1){ //讀取客戶端發來的資訊 ssize_t len = read(connect_fd, recbuf, sizeof(recbuf)); if(len < 0){ if(errno == EINTR){ continue; } exit(0); } printf("接收客戶端的請求:%s\n", recbuf); //向客戶端傳送資訊 printf("回覆客戶端資訊:"); fgets(sendbuf, sizeof(sendbuf), stdin); write(connect_fd, sendbuf, sizeof(sendbuf)); } //關閉連線套接字 close(connect_fd); } //關閉監聽套接字 close(listen_fd); }
/* File name: client.c */ //客戶端 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #define MAXLINE 4096 #define PORT 8000 int main(void){ //定義客戶端套接字 int sockfd = -1; //定義想連線的伺服器的套接字地址 struct sockaddr_in servaddr; //傳送和接收資料的緩衝區 char sendbuf[MAXLINE], recbuf[MAXLINE]; //初始化伺服器套接字地址 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET;//IPv4 servaddr.sin_port = htons(PORT);//想連線的伺服器的埠 servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//伺服器的IP地址 //建立套接字 if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ printf("create socket error: %s(error: %d)\n", strerror(errno), errno); exit(0); } //向伺服器傳送連線請求 if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){ //連線失敗 printf("connect socket error: %s(error: %d)\n", strerror(errno), errno); exit(0); } while(1){ //向伺服器傳送資訊 printf("向伺服器傳送資訊:"); fgets(sendbuf, sizeof(sendbuf), stdin); write(sockfd, sendbuf, sizeof(sendbuf)); //從伺服器接收資訊 ssize_t len = read(sockfd, recbuf, sizeof(recbuf)); if(len < 0){ if(errno == EINTR){ continue; } exit(0); } printf("伺服器迴應:%s\n", recbuf); } //關閉套接字 close(sockfd); }