不多说了,先上个图:
从上面的图中可以看出来,基于TCP协议进行通讯可以大致分成以下几个阶段:
1. 首先是在服务器端, TCP Sever调用socket(), bind(), listen()完成初始化。然后调用accept()阻塞等待,处于监听端口的状态。
2. 客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,
同时应答一个ACK段,服务器收到后从accept()返回。这也就是传说中的TCP三次握手,如下图所示:
TCP 三次握手有一种形象的理解,大家去买手机的时候总要试试手机的通话功能吧, 这时你会走远和你的朋友通话看看质量是否可靠,一般是不是这样:
- 喂, 听的到吗? (Client)
- 听的到, 你听的到吗 ? (Server)
- 嗯, 我也听的到 (Client)
那么就能保证通话质量是OK的, TCP协议也是一样, 通过规定了双方的应答保证了连接的可靠性。
3. 完成三次握手(TCP three way handshake ) TCP Server与TCP Client就建立起了可靠的连接。此后就是Server与Client 数据传输的过程了。因此,服务器从accept()返回
后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在
此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请
求,如此循环下去。
4. 关闭连接的过程。 如果客户端没有更多的请求了,就调用close()关闭连接,就像写端关闭的管道一样,服务器的read()返回0,这样服务器就知道客户端关闭了连接,也调用
close()关闭连接。注意,任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态,仍可接收对方发来的数
据。
贴一个简易的TCP Sever与TCP Client的实现:
1 /***********************************************************/ 2 /* Copyright (C) SA14226214, USTC, 2014-2015 */ 3 /* */ 4 /* FILE NAME : server.c */ 5 /* PRINCIPAL AUTHOR : GaoZhipeng */ 6 /* SUBSYSTEM NAME : lab1 */ 7 /* MODULE NAME : server */ 8 /* LANGUAGE : C */ 9 /* TARGET ENVIRONMENT : ANY */ 10 /* DATE OF FIRST RELEASE : 2014/12/01 */ 11 /* DESCRIPTION : This is a server program */ 12 /***********************************************************/ 13 14 /* 15 *Revision log: 16 * 17 *Ceated by GaoZhipeng, 2014/12/01 18 * 19 */ 20 21 #include<stdio.h> 22 #include<stdlib.h> 23 #include<string.h> 24 #include<unistd.h> 25 #include<sys/socket.h> 26 #include<netinet/in.h> 27 28 #define MAXLINE 80 29 #define SERV_PORT 8000 30 31 int main(void) 32 { 33 struct sockaddr_in servaddr, cliaddr; 34 socklen_t cliaddr_len; 35 int listenfd, connfd; 36 char recv_buf[MAXLINE]; 37 char *send_buf; 38 char str[INET_ADDRSTRLEN]; 39 int i, n; 40 41 listenfd = socket(AF_INET, SOCK_STREAM, 0); 42 43 bzero(&servaddr, sizeof(servaddr)); 44 servaddr.sin_family = AF_INET; 45 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 46 servaddr.sin_port = htons(SERV_PORT); 47 48 bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 49 listen(listenfd,20); 50 51 printf("Accepting connections ...\n"); 52 cliaddr_len = sizeof(cliaddr); 53 connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); 54 while(1) 55 { 56 n = read(connfd, recv_buf, MAXLINE); 57 if(n == 0) 58 { 59 printf("the client has been closed , please restart again\n"); 60 break; 61 } 62 printf("received from %s at PORT %d ",(char *)inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); 63 //print the recv_buf on the terminal 64 write(STDOUT_FILENO, recv_buf, n); 65 printf("\n"); 66 67 send_buf = "你好世界"; 68 write(connfd, send_buf, strlen(send_buf)); 69 } 70 close(connfd); 71 72 }
1 /***********************************************************/ 2 /* Copyright (C) SA14226214, USTC, 2014-2015 */ 3 /* */ 4 /* FILE NAME : server.c */ 5 /* PRINCIPAL AUTHOR : GaoZhipeng */ 6 /* SUBSYSTEM NAME : lab1 */ 7 /* MODULE NAME : server */ 8 /* LANGUAGE : C */ 9 /* TARGET ENVIRONMENT : ANY */ 10 /* DATE OF FIRST RELEASE : 2014/12/01 */ 11 /* DESCRIPTION : This is a server program */ 12 /***********************************************************/ 13 14 /* 15 *Revision log: 16 * 17 *Ceated by GaoZhipeng, 2014/12/01 18 * 19 */ 20 21 #include<stdio.h> 22 #include<stdlib.h> 23 #include<string.h> 24 #include<unistd.h> 25 #include<sys/socket.h> 26 #include<netinet/in.h> 27 28 #define MAXLINE 80 29 #define SERV_PORT 8000 30 31 int main(int argc, char *argv[]) 32 { 33 struct sockaddr_in servaddr; 34 char buf[MAXLINE]; 35 int sockfd, n; 36 37 sockfd = socket(AF_INET, SOCK_STREAM, 0); 38 39 bzero(&servaddr, sizeof(servaddr)); 40 servaddr.sin_family = AF_INET; 41 inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); 42 servaddr.sin_port = htons(SERV_PORT); 43 44 connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 45 46 printf("input you message (q to exit): "); 47 48 //ŽÓ±ê׌ÊäÈëÉ豞ÖжÁÈ¡×Ö·ûŽ®ÐŽÈëbuf 49 while(fgets(buf, MAXLINE, stdin) != NULL) { 50 if(buf[0] == 'q' && strlen(buf) == 2) 51 { 52 break; 53 } 54 write(sockfd, buf, strlen(buf)); 55 56 n = read(sockfd, buf, MAXLINE); 57 if(n == 0) 58 { 59 printf("the connect side has been closed. please run it again\n"); 60 exit(0); 61 } 62 write(STDOUT_FILENO, "Response from server : ", sizeof("Response from server : ")); 63 write(STDOUT_FILENO, buf, n); 64 printf("\ninput you message (q to exit): "); 65 } 66 close(sockfd); 67 return 0; 68 }
一个简单的MakeFile :
/* makefile */ all : gcc server.c -o server gcc client.c -o client clean : rm server client