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

2024/03/19(网络编程·day5)

一、思维导图

二、selec函数实现TCP并发服务器

#include<myhead.h>#define SER_PORT 8888 	//服务器端口号
#define SER_IP "192.168.117.116" 	//服务器IP
int main(int argc, const char *argv[])
{//1、创建一个套接字int sfd = -1;sfd = socket(AF_INET,SOCK_STREAM,0);//参数1:表示创建的是网络通信的套接字//参数2:表示使用的是TCP通信协议//参数3:参数2指定了协议,参数3填0即可if(sfd == -1){perror("socket error");return -1;}printf("%d success sfd = %d\n",__LINE__,sfd);  //3//设置端口号快速重用int reuse = 1;if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) == -1){perror("setsockopt error");return -1;}printf("端口号快速重用成功\n");//2、绑定IP和端口号//2.1填充地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET; 	//地址族sin.sin_port = htons(SER_PORT); 	//端口号sin.sin_addr.s_addr = inet_addr(SER_IP); //IP地址//2.2绑定if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))==-1){perror("bind error");return -1;}printf("%d bind success\n",__LINE__);//3、将套接字设置成被监听状态if(listen(sfd,128) == -1){perror("listen error");return -1;}//4、阻塞等待客户端的连接请求int newfd = -1;//定义结构体变量接受客户端地址信息结构体struct sockaddr_in cin;//接收客户端信息结构体socklen_t addrlen = sizeof(cin);//接收客户端结构体大小char sbuf[128] = ""; //服务器输入数据内容//11、定义一个文件描述符集合fd_set readfds,tempfds;//22、将集合清空FD_ZERO(&readfds);//33、将要被检测的文件描述符放入集合FD_SET(0,&readfds);FD_SET(sfd,&readfds);int maxfd = sfd; //记录当前容器中的最大文件描述符struct sockaddr_in cin_arr[1024]; //存储客户端信息结构体while(1){//将readfds备份tempfds = readfds;int res = select(maxfd+1,&tempfds,NULL,NULL,NULL); //阻塞等待集合中的事件产生if(res == -1){perror("select error");return -1;}else if(res == 0){printf("time out\n");return -1;}//当程序执行到此处说明集合中有事件产生,此时集合中只剩下本次触发事件的描述符for(int i=0;i<=maxfd;i++){//如果不是触发事件的文件描述符,直接跳过if(!FD_ISSET(i,&tempfds)){continue;}//程序执行至此,表示当前i文件描述符触发了事件//判断sfd是否触发事件if(i == sfd){if((newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen)) == -1){perror("accept error");return -1;}printf("[%s %d]:发来连接请求\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));//将客户端地址信息结构体放入容器中cin_arr[newfd] = cin;//将newfd放入readfds容器中参与检测FD_SET(newfd,&readfds);//可能要更新的maxfdif(newfd > maxfd){maxfd = newfd;}}//判断0号文件描述符是否触发事件else if(0 == i){fgets(sbuf,sizeof(sbuf),stdin); //终端输入sbuf[strlen(sbuf)-1] = 0;printf("触发了键盘输入事件:%s\n",sbuf);//将消息发送给所有客户端for(int i = 4;i<= maxfd;i++){send(i,sbuf,sizeof(sbuf),0);}printf("发送成功\n");}else{//说明某个客户端发来消息了,遍历所有客户端,判断是哪个发来的消息//5、收发数据char rbuf[128] = "";//接收客户端发送的信息//将容器清空bzero(rbuf,sizeof(rbuf));//memset(rbuf,0,sizeof(rbuf));//从套接字中读取数据int res = recv(i,rbuf,sizeof(rbuf)-1,0);if(res == 0){			printf("客户端已下线\n");//关闭与客户端通信的套接字close(i);//将当前文件描述符移除容器FD_CLR(i,&readfds);//可能要更新maxfdfor(int k=maxfd;k>=sfd;k--){if(FD_ISSET(k,&readfds)){maxfd = k;break;}}continue;}printf("[%s  %d]:%s\n",inet_ntoa(cin_arr[i].sin_addr),ntohs(cin_arr[i].sin_port),rbuf);//加个收到再回回去strcat(rbuf," <Got it>!");send(i,rbuf,strlen(rbuf),0);printf("回复成功\n");}}}//6、关闭服务器close(sfd);return 0;
}

三、poll函数实现TCP客户端

#include<myhead.h>#define SER_PORT 8888 	//服务器端口号
#define SER_IP "192.168.117.116" 	//服务器IP
#define CLI_PORT 9999 	//客户端端口号
#define CLI_IP "192.168.117.116" 	//客户端IPint main(int argc, const char *argv[])
{//1、创建一个套接字int cfd = -1;cfd = socket(AF_INET,SOCK_STREAM,0);//参数1:表示创建的是网络通信的套接字//参数2:表示使用的是TCP通信协议//参数3:参数2指定了协议,参数3填0即可if(cfd == -1){perror("socket error");return -1;}printf("%d success cfd = %d\n",__LINE__,cfd);  //3//设置端口号快速重用int reuse = 1;if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) == -1){perror("setsockopt error");return -1;}printf("端口号快速重用成功\n");//2、绑定IP和端口号//2.1填充客户端信息结构体struct sockaddr_in cin;cin.sin_family = AF_INET; 	//地址族cin.sin_port = htons(CLI_PORT); 	//端口号cin.sin_addr.s_addr = inet_addr(CLI_IP); //IP地址//2.2绑定if(bind(cfd,(struct sockaddr *)&cin,sizeof(cin))==-1){perror("bind error");return -1;}printf("bind success\n");//3、连接服务器//3.1、填充要连接服务器的地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET; //地质族sin.sin_port = htons(SER_PORT); //服务器端口号sin.sin_addr.s_addr = inet_addr(SER_IP); //服务器的IP地址//3.2、连接服务器if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin)) ==-1){perror("connect error");return -1;}printf("连接成功!\n");//使用poll完成0号文件描述符和cfd文件描述符的多路复用//11、准备文件描述符容器struct pollfd pfds[2];pfds[0].fd = 0; //文件描述符pfds[0].events = POLLIN; //检测读事件pfds[1].fd = cfd; //文件描述符pfds[1].events = POLLIN; //检测读事件//4、收发数据char wbuf[128] = "";while(1){int res = poll(pfds,2,-1); //阻塞检测集合中是否有事件发生if(res == -1){perror("poll error");return -1;}else if(res == 0){printf("time out\n");return -1;}//程序执行至此,说明检测的文件描述符集合中有事件发生//判断是否为0号文件描述符产生的事件if(pfds[0].revents == POLLIN){fgets(wbuf,sizeof(wbuf),stdin); //终端读取wbuf[strlen(wbuf)-1] = '\0'; //将换行换成'\0'//判断输入的字符串if(strcmp(wbuf,"quit") == 0){break;}//将数据发送给服务器send(cfd,wbuf,strlen(wbuf),0);}//判断是否为cfd文件描述符中产生了事件if(pfds[1].revents == POLLIN){//将字符数组清空bzero(wbuf,sizeof(wbuf));recv(cfd,wbuf,sizeof(wbuf)-1,0);printf("收到的服务器消息为:%s\n",wbuf);}}//5、关闭套接字close(cfd);return 0;
}

四、select函数实现的TCP并发服务器连接poll函数实现的TCP客户端

五、模拟面试

TCP通信中的三次握手和四次挥手过

三次握手:
1、客户端向服务器发送同步序列编号(SYN)的数据包,表示请求建立连接,并选择一个初始序列号(ISN)。

2、服务器收到客户端的SYN请求后,会发送一个带有SYN和ACK标志的数据包作为应答,确认客户端的SYN,并选择自己的初始序列号,并对客户端的初始序列号进行确认。

3、客户端收到服务器的确认后,会发送一个带有ACK标志的数据包作为确认,表示连接建立成功。

四次挥手:

1、客户端已经发送完所有数据,想要关闭连接,于是发送一个带有FIN标志的数据包给服务器,表示不再发送数据。

2、服务器收到客户端的FIN后,会发送一个带有ACK标志的数据包作为确认,表示已收到关闭请求,但仍可以发送数据。

3、服务器发送完所有数据后,也想要关闭连接,于是发送一个带有FIN标志的数据包给客户端,表示不再发送数据。

4、客户端收到服务器的FIN后,会发送一个带有ACK标志的数据包作为确认,表示已收到关闭请求,连接终止。

并发和并行的区别

并发是指多个任务交替地快速执行,而并行是指多个任务同时执行


并发指的是系统具有同时处理多个任务的能力。在单处理器系统中,通过快速地在不同任务之间进行切换,使得似乎多个任务在几乎同时执行。在多处理器系统中,不同的处理器可以同时执行不同的任务,这也是一种并发。并发一般是通过时间片轮转或者事件驱动的方式实现的。


并行指的是系统真正同时执行多个任务。在单处理器系统中是不可能真正实现并行的,只能通过并发来模拟。但在多处理器系统中,多个处理器可以同时执行多个任务,这就是真正的并行。

阻寒IO和非阻塞IO的区别

阻塞 I/O 会导致程序在进行 I/O 操作时被挂起,而非阻塞 I/O 允许程序在等待 I/O 操作的同时继续执行其他任务

在阻塞 I/O 中,当程序发起一个 I/O 操作时,程序会被阻塞,直到操作完成才能继续执行后续代码。

非阻塞 I/O 允许程序在发起一个 I/O 操作后立即返回,而不会等待操作完成。程序可以继续执行后续代码,然后再通过轮询或者事件通知等方式来检查 I/O 操作是否完成,如果完成则处理结果,否则继续进行其他任务。

同步和异步的区别

同步是顺序执行的,需要等待上一个操作完成才能执行下一个操作;而异步则是并发执行的,可以同时进行多个操作,不需要等待每个操作的完成

在同步操作中,当一个操作被发起后,必须等待这个操作完成后才能继续执行后续的操作。

在异步操作中,当一个操作被发起后,不需要等待这个操作完成,可以继续执行后续的操作。

详细描述IO多路复用的原理

①将多个阻塞任务的文件描述符统一放入一个检测容器中。

②用一个阻塞函数进行管理。

③如果检测容器中有一个或者多个文件描述符对应的事件产生,就去解除阻塞,执行相应函数。

广播的相关内容

在IPv4网络中,广播地址通常是特定的IP地址,网络号+主机号全为1,表示向本地网络中的所有设备发送广播数据包。对发送端套接字设置为允许广播,发送端类似于UDP客户端,接收端类似于UDP服务器端。UDP广播的优点是简单直接,可以方便地向局域网中的所有设备发送消息。

组播的相关内容

组播地址是D类网络地址,需要对接收端设置加入多播组属性。

发送端类似于UDP的客户端,接收端类似于UDP的服务器端,实现的一对多的通信,不同于广播的是范围不同,只有加入多播组的主机才能通信了。

在使用套接字通信时,客户端就一定不需要绑
定操作吗
不一定,对于报式域套接字而言,如果客户端不绑定套接字文件,系统不会自动绑定,可以发送数据,但是服务器端不能向客户端发送消息。
目前学习的进程间通信方式有哪些套接字,共享内存,有名管道,无名管道,信号,信号灯集,消息队列
线程的同步互斥机制

线程的同步互斥机制是指在多线程环境中控制线程之间执行顺序和共享资源访问,以确保线程之间的协调和正确性

同步互斥机制:

互斥锁(Mutex)

互斥锁是最常见的同步机制之一,用于保护临界区(一次只允许一个线程访问的资源或代码段),确保同一时间只有一个线程可以访问被锁定的资源。当一个线程获得了互斥锁后,其他线程需要等待直到该线程释放锁

相关文章:

  • 推免保研夏令营/预推免面试记录—北大软微
  • Linux基础开发工具之yum与vim
  • 【Jenkins】data stream error|Error cloning remote repo ‘origin‘ 错误解决【亲测有效】
  • K8s 集群高可用master节点ETCD挂掉如何恢复?
  • 学习vue3第五节(reactive 及其相关)
  • 计算机原理
  • 面试官:你说说Kafka是怎么保证消息可靠性的
  • 网络原理(3)——TCP协议
  • vue 中 清除form 校验状态
  • 关于继承是怎么样的?那当然是很好理解之
  • 解决Linux中Eclipse启动时找不到Java环境的问题
  • vue+elementUI实现指定列的单元格可编辑
  • 【09】进阶JavaScript事件循环Promise
  • C语言救赎之路,有些鸟儿是困不住的!(其4) (逻辑运算符+函数)
  • SpringCloudGateway之限流集成篇
  • Android单元测试 - 几个重要问题
  • docker python 配置
  • Intervention/image 图片处理扩展包的安装和使用
  • java第三方包学习之lombok
  • Java读取Properties文件的六种方法
  • scala基础语法(二)
  • Selenium实战教程系列(二)---元素定位
  • Vue 2.3、2.4 知识点小结
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 高程读书笔记 第六章 面向对象程序设计
  • 观察者模式实现非直接耦合
  • 规范化安全开发 KOA 手脚架
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 基于web的全景—— Pannellum小试
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 源码安装memcached和php memcache扩展
  • 整理一些计算机基础知识!
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • ​一、什么是射频识别?二、射频识别系统组成及工作原理三、射频识别系统分类四、RFID与物联网​
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #考研#计算机文化知识1(局域网及网络互联)
  • (C语言)fgets与fputs函数详解
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (zhuan) 一些RL的文献(及笔记)
  • (附源码)php新闻发布平台 毕业设计 141646
  • (三)c52学习之旅-点亮LED灯
  • (转)linux下的时间函数使用
  • (转)创业家杂志:UCWEB天使第一步
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • .CSS-hover 的解释
  • .gitignore文件_Git:.gitignore
  • .NET 8.0 中有哪些新的变化?
  • .net web项目 调用webService
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .NetCore项目nginx发布
  • .NET简谈设计模式之(单件模式)
  • .NET设计模式(11):组合模式(Composite Pattern)