当前位置: 首页 > news >正文

Linux-c TCP服务模型

1、TCP模型,服务端与客户端的搭建时序图

2、TCP模型,在创建阶段和通信阶段,对套接字的理解

2.1、tcp连接阶段

2.2、tcp通信状态

一个服务端与多个客户端的通信状态

TCP与UDP的对比

(下图是笔者理解所画,可能也许有错,欢迎指出)

3、基于EPOLL多路复用模型,代码实现的TCP服务端和客户端

3.1、EPOLL的几个主要的函数

3.1.1、epoll_create1(int flags)

创建epfd,笔者理解,这个是管理众多事件的一个集合。

3.1.2、epoll_ctl

用于添加和删除fd及其关联的事件

3.1.3、epoll_wait

阻塞等待监听的fd的时间发生变化

3.2、服务端代码(TCP+EPOLL)

#define BACKLOG 50  //支持多少个客户端连接int main(int argc, const char *argv[])
{//socketint server=socket(AF_INET, SOCK_STREAM, 0);if(server == -1){perror("socket error");return -1;}//bindstruct sockaddr_in serverAddr;serverAddr.sin_family=AF_INET;serverAddr.sin_port=htons(atoi(argv[1]));serverAddr.sin_addr.s_addr=inet_addr("0.0.0.0");if(bind(server,(struct sockaddr*)&serverAddr, sizeof(serverAddr))==-1){perror("bind server error");return -1;}//listenif(listen(server, BACKLOG)==-1){perror("listen error");return -1;}//epoll_createint epfd=epoll_create1(EPOLL_CLOEXEC);if(epfd==-1){perror("epoll_create1 error");return -1;}//epoll_ctl add/delstruct epoll_event e1;e1.data.fd=server;e1.events=EPOLLIN;if(epoll_ctl(epfd, EPOLL_CTL_ADD, server, &e1)==-1){//server文件FDperror("epoll_ctl add 1: error");return -1;}struct epoll_event e2;e2.data.fd=0;e2.events=EPOLLIN;if(epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &e2)==-1){//标准输入perror("epoll_ctl add 2:error");return -1;}while(1){//epoll_waitstruct epoll_event eArr[50];int num=epoll_wait(epfd, eArr,50,-1);//printf("num=[%d]\n", num);//if(num >= 2){//	printf("num=[%d]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", num);//}if(num == -1){perror("epoll_wait error");return -1;}for(int i=0;i<num;i++){int fd=eArr[i].data.fd;int events=eArr[i].events;//以下由于只监听了EPOLLIN,因此不对事件进行判断if(fd==server){//服务端监听套接字struct sockaddr_in clientAddr;socklen_t clientAddrLen;int fd=accept(server, (struct sockaddr*)&clientAddr, &clientAddrLen);printf("有新的客户端连接[%s]:[%d],FD=[%d]\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port),fd);//将此客户端fd添加到epfd中struct epoll_event e3;e3.data.fd=fd;e3.events=EPOLLIN;if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd, &e3)==-1){perror("epoll_ctl add 3 error");return -1;}continue;}if(fd==0){//标准输入流printf("请输入:");char buf[64];fgets(buf,sizeof(buf), stdin);buf[strlen(buf)-1]=0;printf("键盘键入了:%s\n", buf);continue;}//客户端char buf[64]={0};struct sockaddr_in clientAddr;socklen_t clientAddrLen;ssize_t cnt=recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&clientAddr, &clientAddrLen);printf("<<收到来自[%s]:[%d]的消息:\n[%ld]:%s\n",inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port),cnt,buf);if(cnt == 0){printf("<<客户端[%s]:[%d]的断开了连接:\n",inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));//从epfd中删除这个fdepoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);continue;}//TODO 转换成大写,返还给客户端for(int j=0;j<cnt;j++){buf[j]=toupper(buf[j]);}cnt=send(fd, buf, sizeof(buf), 0);printf(">>向[%s]:[%d]发送了消息:\n[%ld]:%s\n",inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port),cnt,buf);}}return 0;
}

3.3、客户端代码(TCP+EPOLL)

int main(int argc, const char *argv[])
{//tcp epoll客户端//socketint client=socket(AF_INET, SOCK_STREAM, 0);if(client == -1){perror("socket error");return -1;}//connectstruct sockaddr_in serverAddr;serverAddr.sin_family=AF_INET;serverAddr.sin_port=htons(atoi(argv[1]));serverAddr.sin_addr.s_addr=inet_addr("127.0.0.1");if(connect(client, (struct sockaddr*)&serverAddr,sizeof(serverAddr))==-1){perror("connect error");return -1;}printf("与服务端成功创建连接\n");//epoll createint epfd=epoll_create1(EPOLL_CLOEXEC);if(epfd == -1){perror("epoll_create1 error");return -1;}//epoll ctl addstruct epoll_event e1;e1.data.fd=client;//将客户端fd,收到消息事件,添加e1.events=EPOLLIN;if(epoll_ctl(epfd, EPOLL_CTL_ADD, client, &e1)==-1){perror("epoll_ctl error [1]");return -1;}//添加事件:键盘输入//struct epoll_event e1;e1.data.fd=0;//将客户端fd,收到消息事件,添加e1.events=EPOLLIN;if(epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &e1)==-1){perror("epoll_ctl error [1]");return -1;}while(1){struct epoll_event eArr[2];int num=epoll_wait(epfd, eArr, 2, -1);if(num == -1){perror("epoll_wait error");return -1;}for(int i=0;i<num;i++){int fd=eArr[i].data.fd;int es=eArr[i].events;printf("fd=[%d],es=[%d]\n", fd, es);if(fd == 0){//键盘输入char buf[64]={0};fgets(buf, sizeof(buf), stdin);buf[strlen(buf)-1]=0;printf("键盘输入了:%s\n", buf);ssize_t cnt=send(client, buf,strlen(buf),0);printf("发送了[%ld]:%s\n", cnt, buf);if(cnt == -1){perror("send error");}continue;}if(fd == client){//收到消息char buf[64]={0};ssize_t cnt=recv(fd, buf, sizeof(buf), 0);printf("收到[%ld]:%s\n", cnt, buf);continue;}//不知道fprintf(stderr, "错误的监听FD[%d]\n", fd);}}//epoll ctl del//epoll waitreturn 0;
}


http://www.mrgr.cn/news/67983.html

相关文章:

  • SQL Server 数据太多如何优化
  • webrtc前端播放器完整案例
  • 低代码环境中的领域与根实体解析
  • 【1个月速成Java】基于Android平台开发个人记账app学习日记——第8天,完成注册登录并保存到数据库
  • Linux系列-进程的属性
  • 使用GitHub Actions实现CI/CD流程
  • 【测试】【Debug】vscode中同一个测试用例出现重复
  • 27.旅游推荐管理系统(基于springboot和vue)
  • 【系统架构设计师】高分论文:论软件的可用性设计
  • 【JavaScript】模块化开发
  • (4)Java 编程基础概览:Java中的输入输出操作与代码注释详解
  • canfestival主站多电机对象字典配置
  • 力扣中等难度热题——长度为K的子数组的能量值
  • python基础(2)
  • SpringBoot监控
  • 模糊理论与模糊集概述
  • 一文了解Android本地广播
  • 探索开放资源上指令微调语言模型的现状
  • 鸿蒙多线程开发——TaskPool任务池
  • Scala学习记录,List
  • 嵌入式linux中设备树控制硬件的方法
  • 【初阶数据结构与算法】沉浸式刷题之顺序表练习(顺序表以及双指针两种方法)
  • Serverless云计算服务
  • Java SPI机制简单讲解
  • Markdown 全面教程:从基础到高级
  • salesforce批量修改对象字段的四种方法