TCP的通信流程和socket套接字完成服务端和客户端通信
一、TCP是一个面向连接的、安全的、流式传输协议,这个协议是传输层协议。
面向连接:是一个双向连接,通过三次握手建立连接,通过四次挥手断开连接。
安全:tcp通信的过程中,会对发送的每一数据包都会进行校验,如果发现数据丢失,会自动重传。
流式传输:发送端和接受端处理数据的速度,数据的量都可以不一致。
二、服务器端通信流程
1.创建用于监听的套接字,这个套接字是一个文件描述符
1 int lfd = socket();
` 2.将得到的监听的文件描述符和本地 的ip端口进行绑定
1 bind();
3. 设置监听(成功之后开始监听,监听的是客户端的连接)
1 listen();
4.等待并接受客户端的连接请求,建立新的连接,会得到一个新的文件描述符(通信用的),没有新连接请求就阻塞
1 int cfd = accept();
5.通信,读写操作默认都是阻塞的
1 //接受数据 2 read(); //recv(); 3 4 //发送数据 5 write(); //send();
6.断开连接,关闭套接字
close();
基于tcp的服务端通信代码
1 //server.c 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <string.h> 6 #include <arpa/inet.h> 7 8 int main() 9 { 10 //1.建立监听的套接字 11 int lfd = socket(AF_INET, SOCK_STREAM, 0); 12 if (lfd == -1) 13 { 14 perror("socket"); 15 exit(0); 16 } 17 18 //设置端口复用,防止占用服务器后断开后还占用这端口,要等 19 int opt = 1; 20 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 21 struct sockaddr_in addr; 22 23 //2.将socket()返回值和本地的ip、端口绑定到一起 24 addr.sin_family = AF_INET; 25 addr.sin_port = htons(10000); 26 addr.sin_addr.s_addr = htonl(INADDR_ANY); 27 //INADDR_ANY代表本机的所有ip,假设有三个网卡就有三个IP地址 28 //这个宏可以代表任意一个IP地址 29 //这个宏一般用于本地的绑定操作。 30 //inet_pton(AF_INET, "192.168.0.23", &addr.sin_addr.s_addr);//绑定具体的ip 31 int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr)); 32 if (ret == -1) 33 { 34 perror("bind"); 35 exit(0); 36 } 37 38 //3.设置监听 39 ret = listen(lfd, 128); 40 if (ret == -1) 41 { 42 perror("listen"); 43 exit(0); 44 } 45 46 //阻塞等待并接受客户端连接 47 struct sockaddr_in cliaddr; 48 socklen_t clilen = sizeof(cliaddr); 49 int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &clilen); 50 //int cfd = accept() 51 if (cfd == -1) 52 { 53 perror("accept"); 54 exit(0); 55 } 56 57 //打印客户端的地址信息 58 char ip[24] = { 0 }; 59 printf("客户端的IP地址:%s,端口:%d\n", 60 inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip)), 61 ntohs(cliaddr.sin_port)); 62 63 //5.和客户端通信 64 while (1) 65 { 66 //接受数据 67 char buf[1024]; 68 memset(buf, sizeof(buf), 0); 69 int len = read(cfd, buf, sizeof(buf)); 70 if (len > 0) 71 { 72 printf("客户端说:%s\n", buf); 73 74 //回复客户端 75 memset(buf, sizeof(buf), 0); 76 sprintf(buf, "你好,客户端,我已经收到了你的问候", sizeof(buf)); 77 write(cfd, buf, len); 78 } 79 else if (len == 0) 80 { 81 printf("客户端断开了连接。。。\n"); 82 break; 83 } 84 else 85 { 86 perror("read"); 87 break; 88 } 89 } 90 close(cfd); 91 close(lfd); 92 93 return 0; 94 }
三、客户端的通信流程
在单线程的情况下客户端通信的文件描述符只有一个,没有监听的文件描述符
1,创建一个通信的套接字
1 int cfd = socket();
2.连接服务器,需要知道服务器绑定的ip和端口
1 connect();
3.与服务端通信
1 //接受数据 2 read(); //recv(); 3 4 //发送数据 5 write();//send()
4.断开连接,关闭文件描述符
close();
基于tcp通信的客户端
1 //客户端,client.c 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <string.h> 6 #include <arpa/inet.h> 7 8 int main() 9 { 10 //1.创建通信的套接字 11 int fd = socket(AF_INET, SOCK_STREAM, 0); 12 if (fd == -1) 13 { 14 perror("socket"); 15 exit(0); 16 } 17 18 //2.连接服务器 19 struct sockaddr_in addr; 20 addr.sin_family = AF_INET; 21 addr.sin_port = htons(10000); 22 //addr.sin_addr.s_addr = 23 inet_pton(AF_INET, "192.168.0.23", &addr.sin_addr.s_addr); 24 int ret =connect(fd, (struct sockaddr*)&addr, sizeof(addr)); 25 if (ret == -1) 26 { 27 perror("connect"); 28 exit(0); 29 } 30 31 //3.和服务端通信 32 int number = 0; 33 while (1) 34 { 35 //发送数据 36 char buf[1024]; 37 sprintf(buf, "您好,服务器。。。%d", number++); 38 write(fd, buf, sizeof(buf)); 39 40 //接受数据 41 memset(buf, 0, sizeof(buf)); 42 int len = read(fd, buf, sizeof(buf)); 43 if (len > 0) 44 { 45 printf("服务器说:%s\n", buf); 46 } 47 else if (len == 0) 48 { 49 printf("服务器断开了连接。。。\n"); 50 break; 51 } 52 else 53 { 54 perror("read"); 55 break; 56 } 57 sleep(1); //没隔一秒发送一条数据 58 } 59 }
5.最后,先启动服务端,再启动客户端,效果如下
服务端:
客户端: