CPP模拟实现TCP协议下socket通信

  • 1. TCP 编程流程图
  • 2. 数据收发阶段使用的API
    • 2.1 send接口
    • 2.2 recv接口
  • 3. 两个队列
  • 4. 总结TCP 编程双端流程
  • 5. 单线程:TCP协议下 单客户端与单服务端 socket通信
    • 5.1 客户端代码
    • 5.2 服务端代码
    • 5.3 两个终端交互演示
    • 5.4 尝试多客户端连接发现问题
  • 6. 多进程:TCP协议下 多客户端与单服务端 socket通信
    • 6.1 客户端代码
    • 6.2 服务端代码
    • 6.3 三个终端交互演示
  • 7. 多线程:TCP协议下 多客户端与单服务端 socket通信
    • 7.1 客户端代码
    • 7.2 服务端代码
    • 7.3 三个终端交互演示

1. TCP 编程流程图

2. 数据收发阶段使用的API

#include <sys/types.h>
#include <sys/socket.h>int send(int s, const void *msg, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

2.1 send接口

int send(int sockfd, const void *msg, size_t len, int flags);
  • sockfd:客户端调用则填入客户端创建出来的套接字描述符;服务端调用则填入服务端创建出来的用于数据收发的套接字描述符
  • msg:待发送的数据
  • len:待发送的数据的长度
  • flags:填入0表示阻塞发送

返回值:小于0表示失败

2.2 recv接口

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • sockfd:客户端调用则填入客户端创建出来的套接字描述符;服务端调用则填入服务端创建出来的用于数据收发的套接字描述符
  • buf:接收数据的缓冲区
  • len:缓冲区的最大接受数据长度
  • flags:填入0表示阻塞接收

返回值:

返回值ssize_t 含义
<0 接收失败
=0 对端关闭连接
>0 接收了多少个字节数据

3. 两个队列

  • TCP编程中有两个队列:未完成连接队列已完成连接队列
  • 未完成连接队列:保存目前正在处于三次握手之中的连接
  • 已完成连接队列:保存的是已经完成三次握手的连接

4. 总结TCP 编程双端流程

5. 单线程:TCP协议下 单客户端与单服务端 socket通信

5.1 客户端代码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;int main()
{// 1. 创建套接字int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(sockfd < 0){perror("socket");return -1;}// 2. 发起连接struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("192.168.153.128");addr.sin_port = htons(19999);int ret = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));if(ret < 0){perror("connect");return -1;}while(1){// 3. 发送数据string s;cout<<"You want to say:";fflush(stdout);getline(cin,s);ret = send(sockfd, s.c_str(),s.size(),0);if(ret < 0){perror("send");continue;}// 4. 接受数据char buf[1024] = {0};ret = recv(sockfd, buf,strlen(buf)-1,0);if(ret < 0){perror("recv");continue;}else if(ret == 0){printf(" server shutdown!\n");break;}cout << "server say:" << buf << endl;}// 5. 关闭套接字close(sockfd);return 0;
}

5.2 服务端代码

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <string>
#include <iostream>
using namespace std;int main()
{// 1. 创建套接字//创建一个使用流式套接字+IPv4的套接字int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(listen_sockfd < 0){perror("socket");return -1;}// 2. 绑定地址信息  char ip_str[] = "192.168.153.128";uint32_t ip = inet_addr(ip_str);uint16_t port = htons(19999);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = ip;addr.sin_port = port;int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));if(ret < 0){perror("bind");return -1;}// 3. 监听:告诉内核已经准备好可以接受连接啦ret = listen(listen_sockfd, 1);if(ret < 0){perror("listen");return -1;}// 4.接受连接 int new_sockfd = accept(listen_sockfd, NULL, NULL);if(new_sockfd < 0){perror("accept");return -1;}while(1){// 5. 接收数据char buf[1024] = { 0 };ret = recv(new_sockfd, buf, strlen(buf) - 1, 0);if(ret < 0){perror("recv");continue;}else if(ret == 0){// 若客户端已经关闭连接,则需要关闭用于数据收发的套接字printf(" client shutdown !\n");close(new_sockfd);break;}cout << "client say:" << buf << endl;// 6. 发送数据string s;cout << "you want to say:";getline(cin,s);ret = send(new_sockfd,s.c_str(),s.size(),0);if(ret < 0){perror("send");continue;}}// 7. 关闭监听套接字close(listen_sockfd);return 0;
}

5.3 两个终端交互演示

[gongruiyang@localhost client]$ g++ cli.cpp -o client
[gongruiyang@localhost client]$ ./client
You want to say:hello Server!
server say:hello Client!
You want to say:Bye~
server say:Bye~
You want to say:^C
[gongruiyang@localhost client]$ g++ cli.cpp -o client
[gongruiyang@localhost client]$ ./client
You want to say:hello Server!
server say:hello Client!
You want to say:Bye~
server say:Bye~
You want to say:^C

5.4 尝试多客户端连接发现问题

  • 再启动一个客户端,尝试连接正在运行的服务端,发现不能与服务端正常通信

  • 原因分析:是因为我们accept到一个连接请求后,一直在while中循环接收与发送数据,没有继续调用accept,导致不能接收新的连接请求

  • 所以当前的这个TCP,是无法处理多个连接的

  • 解决方法:在服务端使用多线程多线程来处理

6. 多进程:TCP协议下 多客户端与单服务端 socket通信

6.1 客户端代码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;int main()
{// 1. 创建套接字int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(sockfd < 0){perror("socket");return -1;}// 2. 发起连接struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("192.168.153.128");addr.sin_port = htons(19999);int ret = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));if(ret < 0){perror("connect");return -1;}while(1){// 3. 发送数据string s;cout<<"You want to say:";fflush(stdout);getline(cin,s);ret = send(sockfd, s.c_str(),s.size(),0);if(ret < 0){perror("send");continue;}// 4. 接受数据char buf[1024] = {0};ret = recv(sockfd, buf,strlen(buf)-1,0);if(ret < 0){perror("recv");continue;}else if(ret == 0){printf(" server shutdown!\n");break;}cout << "server say:" << buf << endl;}// 5. 关闭套接字close(sockfd);return 0;
}

6.2 服务端代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>#include <iostream>
#include <string>
using namespace std;#include <signal.h>
#include <wait.h>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>void signal_handler(int signum)
{wait(NULL);
}int main()
{// 1. 创建套接字//创建一个使用流式套接字+IPv4的套接字int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(listen_sockfd < 0){perror("socket");return -1;}// 2. 绑定地址信息  char ip_str[] = "192.168.153.128";uint32_t ip = inet_addr(ip_str);uint16_t port = htons(19999);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = ip;addr.sin_port = port;int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));if(ret < 0){perror("bind");return -1;}// 3. 监听:告诉内核已经准备好可以接受连接啦ret = listen(listen_sockfd, 1);if(ret < 0){perror("listen");return -1;}// 为了防止子进程退出后产生僵尸进程,需要处理子进程发出的SIGCHLD信号signal(SIGCHLD,signal_handler);while(1){int new_sockfd = accept(listen_sockfd, NULL, NULL);if(new_sockfd < 0){perror("accept");continue;}// 4.接受连接 //接收到了新连接,需要创建子进程去处理pid_t pid = fork();if(pid < 0){perror("fork");continue;}else if (pid == 0){close(listen_sockfd);while(1){// 5. 接收数据char buf[1024] = { 0 };ret = recv(new_sockfd, buf, strlen(buf) - 1, 0);if(ret < 0){perror("recv");continue;}else if(ret == 0){// 若客户端已经关闭连接,则需要关闭用于数据收发的套接字printf(" client shutdown !\n");close(new_sockfd);break;}cout << "client say:" << buf << endl;// 6. 发送数据string s;cout << "you want to say:";getline(cin,s);ret = send(new_sockfd,s.c_str(),s.size(),0);if(ret < 0){perror("send");continue;}}}close(new_sockfd);}// 7. 关闭监听套接字close(listen_sockfd);return 0;
}

6.3 三个终端交互演示

服务端

[gongruiyang@localhost server]$ ./pro_svr
client say:I'm Client1
you want to say:Hello Client1
client say:I'm Client2
you want to say:Hello Client2
^C

客户端1

[gongruiyang@localhost client]$ ./client
You want to say:I'm Client1
server say:Hello Client1
You want to say:1server shutdown!

客户端2

[gongruiyang@localhost client]$ ./client
You want to say:I'm Client2
server say:Hello Client2
You want to say:1server shutdown!

7. 多线程:TCP协议下 多客户端与单服务端 socket通信

7.1 客户端代码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;int main()
{// 1. 创建套接字int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(sockfd < 0){perror("socket");return -1;}// 2. 发起连接struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("192.168.153.128");addr.sin_port = htons(19999);int ret = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));if(ret < 0){perror("connect");return -1;}while(1){// 3. 发送数据string s;cout<<"You want to say:";fflush(stdout);getline(cin,s);ret = send(sockfd, s.c_str(),s.size(),0);if(ret < 0){perror("send");continue;}// 4. 接受数据char buf[1024] = {0};ret = recv(sockfd, buf,strlen(buf)-1,0);if(ret < 0){perror("recv");continue;}else if(ret == 0){printf(" server shutdown!\n");break;}cout << "server say:" << buf << endl;}// 5. 关闭套接字close(sockfd);return 0;
}

7.2 服务端代码

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <string>
#include <iostream>
using namespace std;                                                                                             #include <pthread.h>struct SockVal
{int new_sockfd;
};void* work_task(void* arg)
{pthread_detach(pthread_self());SockVal* sv = (SockVal*)arg;int new_sockfd = sv->new_sockfd;while(1){// 5. 接收数据char buf[1024] = { 0 };int ret = recv(new_sockfd, buf, strlen(buf) - 1, 0);if(ret < 0){perror("recv");continue;}else if(ret == 0){// 若客户端已经关闭连接,则需要关闭用于数据收发的套接字printf(" client shutdown !\n");close(new_sockfd);break;}cout << "client say:" << buf << endl;// 6. 发送数据string s;cout << "you want to say:";getline(cin,s);ret = send(new_sockfd,s.c_str(),s.size(),0);if(ret < 0){perror("send");continue;}}delete sv;return NULL;
}int main()
{// 1. 创建套接字//创建一个使用流式套接字+IPv4的套接字int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(listen_sockfd < 0){perror("socket");return -1;}// 2. 绑定地址信息  char ip_str[] = "192.168.153.128";uint32_t ip = inet_addr(ip_str);uint16_t port = htons(19999);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = ip;addr.sin_port = port;int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));if(ret < 0){perror("bind");return -1;}// 3. 监听:告诉内核已经准备好可以接受连接啦ret = listen(listen_sockfd, 1);if(ret < 0){perror("listen");return -1;}while(1){// 4.接受连接 int new_sockfd = accept(listen_sockfd, NULL, NULL);if(new_sockfd < 0){perror("accept");continue;}// 使用多线程处理 接受的连接    SockVal* sv = new SockVal();sv->new_sockfd = new_sockfd;pthread_t ptid;int ret = pthread_create(&ptid,NULL,work_task,(void*)sv);if(ret < 0){perror("pthread_create");continue;}}// 7. 关闭监听套接字close(listen_sockfd);return 0;
}

7.3 三个终端交互演示

Server

[gongruiyang@localhost server]$ ./server
client say:Hi! I'm ClientA!
you want to say:Hi! ClientA! I'm Server!
client say:Hi! I'm ClientB!
you want to say:Hi! ClientB! I'm Server!
client say:Bye~~~
you want to say:Bye~~client shutdown !
client say:Bye~~
you want to say:Bye~~client shutdown !
^C

ClientA

[gongruiyang@localhost client]$ ./client
You want to say:Hi! I'm ClientA!
server say:Hi! ClientA! I'm Server!
You want to say:Bye~~~
server say:Bye~~
You want to say:^C

ClientB

[gongruiyang@localhost client]$ ./client
You want to say:Hi! I'm ClientB!
server say:Hi! ClientB! I'm Server!
You want to say:Bye~~
server say:Bye~~
You want to say:^C

【Linux】一篇文章搞定 CPP模拟实现TCP协议下socket通信相关推荐

  1. Android NDK开发之旅(2):一篇文章搞定Android Studio中使用CMake进行NDK/JNI开发

    Android NDK开发之旅(2):一篇文章搞定android Studio中使用CMake进行NDK/JNI开发 (码字不易,转载请声明出处:http://blog.csdn.NET/andrex ...

  2. 一篇文章搞定GVIM(根据工作经验持续更新)

    文章目录 0.引言 1.在Linux下面安装VIM 2.基本操作 2.1三种模式 2.1 保存退出:wq没反应?! 2.2 解决鼠标不能用的问题 2.3 VIM上下左右移动hjkl 2.4 跳转到第n ...

  3. 一篇文章搞定DX9.0c 环境里的3DXSpriet !!

    四年前写过一篇<一篇文章搞定3DXSpriet !! >得到了很多的反馈,现在那篇文章中的好多代码已经不可以再用了,所以对其中的一些代码做了改动,为了方便初学者掌握3DXSpriet,再写 ...

  4. 一篇文章搞定百度OCR图片文字识别API

    一篇文章搞定百度OCR图片文字识别API https://www.jianshu.com/p/7905d3b12104 转载于:https://www.cnblogs.com/chongdongxia ...

  5. python基础知识-一篇文章搞定Python全部基础知识

    原标题:一篇文章搞定Python全部基础知识 前言: 1.Python软件安装 第一章.字符串及数字变量 1.变量 要点提炼:Python变量为强类型动态类型.换言之,变量很任性,你给他int,他就是 ...

  6. 一篇文章搞定《RecyclerView缓存复用机制》

    一篇文章搞定<RecyclerView缓存复用机制> 前言 零.为什么要缓存 一.RecyclerView如何构建我们的列表视图 二.缓存过程 三.缓存结构 1.mChangedScrap ...

  7. 超硬核!!!一篇文章搞定TCP、UDP、Socket、HTTP(详细网络编程内容+现实解释三次握手四次挥手+代码示例)【网络编程 1】

    TCP.UDP.Socket 一天面试的经验: 什么是网络编程 网络编程中两个主要的问题 网络协议是什么 为什么要对网络协议分层 计算机网络体系结构 1 TCP / UDP 1.1 什么是TCP/IP ...

  8. 一篇文章搞定java中的垃圾回收机制面试题

    一篇文章搞定java中的垃圾回收机制面试题 任何语言在运行过程中都会创建对象,也就意味着需要在内存中为这些对象在内存中分配空间,在这些对象失去使用的意义的时候,需要释放掉这些内容,保证内存能够提供给新 ...

  9. 一篇文章搞定《Android布局优化》

    ------<一篇文章搞定Android布局优化> 前言 为什么要进行布局优化? Android绘制原理 双缓冲机制 布局加载原理 布局加载优化的一些方法介绍 AsyncLayoutInf ...

最新文章

  1. 什么是URL?协议头,路径和端口是什么意思?
  2. 数据分析从零开始实战,Pandas读写Excel/XML数据
  3. python数据特征提取_训练数据的特征提取
  4. C语言归并排序Merge Sort算法(附完整源码)
  5. mac tomcat java_Mac下配置Java开发环境(JDK1.8)和Tomcat服务器
  6. 精述IBM的MQTT协议和MQTT-S协议
  7. 如何优雅地使用 VSCode 来编辑 vue 文件?
  8. android https通过载入pfx证书获取数据
  9. 面试总结之人工智能AI(Artificial Intelligence)/ 机器学习(Machine Learning)
  10. OSChina 周日乱弹 ——程序员被辞退的理由
  11. PC 护眼模式(凑合用)
  12. 图像分割-专有名词讲解
  13. 云计算工程师必备技能
  14. mac os 开启redis_在Mac os x 安装 Redis
  15. 数据结构课程设计 神秘国度的爱情故事
  16. linux vi 编辑器下经常会用到保存退出与不保存退出
  17. 视频教程-《信息学竞赛-C语言》 DAY30-清华尹成老师-C/C++
  18. 《C primer plus》——文件输入/输出
  19. win10接USB设备(移动硬盘扫码枪之类)蓝屏问题处理
  20. 游戏开发者必须是骨灰级玩家吗?

热门文章

  1. 在layui数据表格中修改数据
  2. C#流程控制————异常捕获
  3. 陕西师范大学大学:融合传统与创新的学府之旅
  4. 【计算机毕业设计】java ssm 高校食堂订餐/点餐系统
  5. 神经网络建模的建模步骤,神经网络模型图怎么画
  6. 三菱PLC的通讯协议
  7. 芯片验证从零开始系列(一)——芯片验证概论
  8. c语言实现axi通信,【参赛手记】AXI-Stream接口开发详细流程
  9. 马化腾会是传统电信大佬的掘墓人吗
  10. 大语言模型举例和相关论文推荐