1. 程式人生 > >TCP/IP的TCP socket通訊過程

TCP/IP的TCP socket通訊過程

傳統的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);

}