(自用)网络编程

OSI七层协议模型 (open system interconnection)

应用层————为应用数据提供服务

表示层————数据格式转化,数据加密

会话层————建立、维护和管理会话

传输层————建立、维护和管理端到端的链接,控制数据传输的方式

网络层————数据传输线路选择,IP地址及路由选择

数据链路层———物理通路的发送和数据包的划分,附加Mac地址到数据包

物理层———01比特流的转换

数据传输由顶向下,下层为上层提供服务

TCP/IP四层协议模型

应用层———负责处理特定的应用程序细节, 如ftp,http ,smtp,ssh 等

运输层———主要为两台主机上的应用提供端到端的通信, 如TCP,UDP

网络层(互联网层)———处理分组在网络中的活动,比如分组的选路。

链路层(数据链路层/网络接口层)———包括操作系统中的设备驱动程序、计算机中对应的   网络接口卡,01比特流的转换

协议封装

下层协议通过封装为上层协议提供服务。应用程序数据在发送到物理网络上之前,将沿着协议栈从上往下依次传递。每层协议都将在上层数据的基础上加上自己的头部信息(有时也包括尾部信息),以实现该层的功能。

传输层协议为TCP/UDP,加上TCP/UDP头部

网络层协议为IP,加上IP头部.......

只有数据链路层要加上头部和尾部

TCP 协议头部

源端口号和目的端口号:再加上Ip首部的源IP地址和目的IP地址可以唯一确定一个TCP连接

端口号长度为2字节,16位;所以端口号最多不能超过65535

数据序号表示在这个报文段中的第一个数据字节序号

确认序号仅当ACK标志为1时有效。确认号表示期望收到的下一个字节的序号(这个下面再详细分析)

偏移:就是头部长度,有4位,跟IP头部一样,以4字节为单位。最大是60个字节

保留位:6位,必须为0

6个标志位:

URG-紧急指针有效

ACK-确认序号有效

PSH-接收方应尽快将这个报文交给应用层

RST-连接重置

SYN-同步序号用来发起一个连接

FIN-终止一个连接

窗口字段:16位,代表的是窗口的字节容量,也就是TCP的标准窗口最大为2^16 - 1 = 65535个字节

校验和:源机器基于数据内容计算一个数值,收信息机要与源机器数值 结果完全一样,从而证明数据的有效性。检验和覆盖了整个的TCP报文段:这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证的。

紧急指针:是一个正偏移量,与序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式

选项与填充(必须为4字节整数倍,不够补0):

最常见的可选字段的最长报文大小MSS(Maximum Segment Size),每个连接方通常都在一个报文段中指明这个选项。它指明本端所能接收的最大长度的报文段。

该选项如果不设置,默认为536(20+20+536=576字节的IP数据报)

三次握手(建立TCP连接)和四次挥手(断开TCP连接)

感觉下面这篇文章总结的很好!

【通俗易懂】三次握手与四次挥手_三次握手和四次挥手-CSDN博客

滑动窗口

黄色绿色

表示要发送的包已经在缓存中。

正常情况

我们能不能把第一个和第二个包发过去后,收到第一个确认包就把第三个包发过去呢?而不

是去等到第二个包的确认包才去发第三个包。这样就很自然的产生了我们"滑动窗口"的实

现。

在图中,我们可看出灰色1号2号3号包已经发送完毕,并且已经收到Ack。这些包就已经

是过去式。4、5、6、7号包是黄色的,表示已经发送了。但是并没有收到对方的Ack,所以

也不知道接收方有没有收到。8、9、10号包是绿色的。是我们还没有发送的。这些绿色也

就是我们接下来马上要发送的包。 可以看出我们的窗口正好是7格。后面的11-16还没有

被读进内存。要等4号-10号包有接下来的动作后,我们的包才会继续往下发送。

可以看到4号包对方已经被接收到,所以被涂成了灰色。“窗口”就往右移一格,这里只要

保证“窗口”是7格的。 我们就把11号包读进了我们的缓存。进入了“待发送”的状态。

8、9号包已经变成了黄色,表示已经发送出去了。接下来的操作就是一样的了,确认包后,

窗口往后移继续将未发送的包读进缓存,把“待发送“状态的包变为”已发送“。

丢包情况

有可能我们包发过去,对方的Ack丢了。也有可能我们的包并没有发送过去。从发送方角度

看就是我们没有收到Ack。

一般情况:一直在等Ack。如果一直等不到的话,我们也会把读进缓存的待发送的包也一

起发过去。但是,这个时候我们的窗口已经发满了。所以并不能把12号包读进来,而是始

终在等待5号包的Ack

如果我们这个Ack始终不来怎么办呢? 采用超时重传机制解决:

发送端每发送一个报文段,就启动一个定时器并等待确认信息;接收端成功接收新数据后返回确认信息。若在定时器超时前数据未能被确认,TCP就认为报文段中的数据已丢失或损坏,需要对报文段中的数据重新组织和重传。(重传超时时间: RTO)

UDP通信(速度快,不保证可靠传输)

1. TCP与UDP

    当使用网络套接字通信时,

    套接字的“域”都取AF_INET;

    套接字的type:

          SOCK_STREAM   此时,默认使用TCP协议进行通信。

          SOCK_DGRAM    此时,默认使用UDP协议进行通信。

    

    TCP通信,是一个有序的、可靠的、面向连接的

    UDP通信,是不保证有序到达的数据报服务。(在局域网内,使用UDP已很可靠)

2. 使用UDP通信与TCP通信使用上的区别:

    1)创建套接字时的type(参数2)不同。

         TCP通信,使用SOCK_STREAM

         UDP通信,使用SOCK_DGRAM

    

    2)发送数据和接收数据时,使用的接口不同

         TCP通信,发送数据,使用write(或send)

                       接收数据,使用read(或recv)

         UDP特性,发送数据,使用sendto      

                  接收数据,服务器端使用recvfrom   

                             客户端使用recv

                       

   3)不需要使用listen

   4)不需要先建立连接(TCP客户端和服务器端分别使用connect和receive建立连接)

   

   步骤总结:

     基于UDP的网络套接字通信

     服务器端

        (1) 创建一个网络套接字

        (2) 设置服务器地址

        (3) 绑定该套接字,使得该套接字和对应的端口关联起来     

        (4) 循环处理客户端请求

              使用recvfrom等待接收客户端发送的数据

              使用sendto发送数据至客户端

   

     客户端

       (1) 创建一个套接字

       (2) 设置服务器地址

       (3) 使用sendto向服务器端(接收端)发送数据

       (4) 使用recv接受数据

   

3. sendto与recvfrom、recv

    1) sendto

        功能:UDP服务器或客户端用于发送数据

        原型:int  sendto (int sockfd,                  //  套接字

                   void *buff,            // 发送缓存区

                   size_t len,            // 发送缓冲区的长度

                   init  flags,           //  标志,一般取0

                   struct sockaddr *to,   // 目的主机地址

                  socklen_t  tolen        // 目的主机地址长度

                      );

         返回值:成功,返回实际发送数据的字节数

                    失败,返回-1

                    

    2) recvfrom

         功能:UDP服务器用于接收数据

         原型: 与sendto类似。

            int  recvfrom (int sockfd,                  //  套接字

                 void *buff,                 // 接收缓存区

                 size_t len,                 // 接受缓冲区的长度

                 init  flags,                 //  标志,一般取0

                 struct sockaddr *to,   // 源主机地址

                 socklen_t  *tolen         // 源主机地址长度

                 );

         注意:参数6必须要初始化为对应地址的长度!

     3) recv

         功能:UDP客户端用于接收数据

         原型: ssize_t  recv (int sockfd,  void *buf,  size_t len,   int flags);             

         注意: 该调用的参数不需要指定地址。

    

因为当使用udp时,对应的套接字被自动绑定在一个短暂的动态的端口上。   

实例

实例1: 服务器接收、客户端发送

client1.c

#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>#define BUFF_SIZE 1024int main(void)
{int sockfd;struct sockaddr_in server_addr;int ret;int c;char buff[BUFF_SIZE];// 创建一个套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);// 设置服务器地址server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr("10.10.0.9");server_addr.sin_port = htons(9000);// 向服务器发送数据strcpy(buff, "hello world");ret = sendto(sockfd, buff, strlen(buff) + 1, 0, (struct sockaddr*)&server_addr, sizeof(server_addr));if (ret == -1) {perror("sendto");exit(errno);}printf("ret = %d\n", ret);return 0;	
}

 server1.c

#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>#define BUFF_SIZE 1024int main(void)
{int server_sockfd;int client_sockfd;char ch;int ret;int recv_len;char buff[BUFF_SIZE];//用于UNIX系统内部通信的地址, struct sockaddr_unstruct sockaddr_in server_addr;struct sockaddr_in client_addr;int client_addr_len =sizeof(struct sockaddr_in);server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);// 设置服务器地址server_addr.sin_family = AF_INET;  //地址的域,相当于地址的类型, AF_UNIX表示地址位于UNIX系统内部server_addr.sin_addr.s_addr = INADDR_ANY;  //inet_addr("10.10.0.9");server_addr.sin_port = htons(9000);// 绑定该套接字,使得该套接字和对应的系统套接字文件关联起来。ret = bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));if (ret == -1) {perror("bind");exit(1);}// 创建套接字队列, 保存进入该服务器的客户端请求。//ret = listen(server_sockfd, 5);// 循环处理客户端请求while (1) {printf("server waiting\n");// 等待并接收客户端请求//client_sockfd = accept(server_sockfd,  (struct sockaddr*)&client_addr, &client_addr_len);recv_len = recvfrom(server_sockfd, buff, sizeof(buff) , 0, (struct sockaddr*)&client_addr, &client_addr_len);if (recv_len < 0) {perror("recvfrom");exit(errno);}printf("received: %s\n", buff);	}close(server_sockfd);return 0;	
}

同步IO和异步IO

场景1: 小明去打开水,而开水塔此时没有水,小明在现场一直等待开水到来,或者不断的轮询查看是否有开水,直到有开水取到水为止,这是同步IO的一种案例!

同步IO的特点:

同步IO指的是用户进程触发I/O操作并等待或者轮询的去查看I/O操作是否就绪。

同步IO的执行者是IO操作的发起者。执行者=发起者

同步IO需要发起者进行内核态到用户态的数据拷贝过程,所以这里必须阻塞

场景2: 小明去打开水,而开水塔此时没有水,开水塔的阿姨叫小明把水壶放到现场,来水后会帮他打好水,并打电话叫他来取,这是异步IO的一种案例!

异步IO的特点:

异步IO是指用户进程触发I/O操作以后就立即返回,继续开始做自己的事情,而当I/O操作已经完成的时候会得到I/O完成的通知。

异步IO的执行者是内核线程,内核线程将数据从内核态拷贝到用户态,所以这里阻塞

五种网络IO模式

read操作数据流向:

1.数据准备(水塔没水)  2.内核拷贝到进程(水塔打水到水桶)

linux系统产生了下面五种网络模式的方案

1、阻塞IO(blocking IO)

2、非阻塞IO(nonblocking IO)

3、IO多路复用(IO multiplexing)

4、信号驱动IO(signal driven IO)不常用

5、异步IO (asynchronous IO)

以去打开水,且水还没烧热为背景

阻塞IO

在原地等水烧热(准备数据),等水杯装满(内核到进程)。

且这种IO模型是同步的。

在linux 中,默认情况下所有的socket都是blocking IO, 一个典型的读操作流程:

非阻塞IO 

去打开水(检查有无数据),发现水都是凉的(数据没准备好);走开去做其他事,过一会儿回来(再次检查有无数据),发现有开水了,在原地等开水装满(数据从内核态到用户空间)。

只有是检查有无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(等着水将水杯装满),因此它还是同步IO

在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。

非阻塞IO如何设置?

方式一: 创建socket 时指定

int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

方式二: 在使用前通过如下方式设定

fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
 代码示例
 
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>#define BUFF_SIZE 1024int main(void)
{int server_sockfd;int client_sockfd;char ch;int ret;int recv_len;char buff[BUFF_SIZE];//用于UNIX系统内部通信的地址, struct sockaddr_unstruct sockaddr_in server_addr;//服务器地址,用来bindstruct sockaddr_in client_addr;//客户端地址用来recvfromint client_addr_len = sizeof(struct sockaddr_in);server_sockfd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0);// 设置服务器地址server_addr.sin_family = AF_INET;  //地址的域,相当于地址的类型, AF_UNIX表示地址位于UNIX系统内部server_addr.sin_addr.s_addr = INADDR_ANY;  //inet_addr("10.10.0.9");server_addr.sin_port = htons(9000);// 绑定该套接字,使得该套接字和对应的系统套接字文件关联起来。ret = bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));if (ret == -1) {perror("bind");exit(1);}// 创建套接字队列, 保存进入该服务器的客户端请求。//ret = listen(server_sockfd, 5);// 循环处理客户端请求while (1) {printf("server waiting\n");// 等待并接收客户端请求//client_sockfd = accept(server_sockfd,  (struct sockaddr*)&client_addr, &client_addr_len);recv_len = recvfrom(server_sockfd, buff, sizeof(buff), 0,(struct sockaddr*)&client_addr, &client_addr_len);if (recv_len < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK){sleep(2);continue;}perror("recvfrom");exit(errno);}printf("received: %s\n", buff);}close(server_sockfd);return 0;
}

注意:recvfrom的返回值<0的时候,可能是非阻塞IO没有读到数据,不应该报错退出。

应该对errno进行判断,若errno为EAGIN或EWOULDBLOCK则跳过本轮循环.

IO多路复用(多了一个阿姨)

有多个水龙头,且有一个阿姨,当水龙头全都没水时,我去干别的事情;等水龙头有水了阿姨再喊我。

两种情况:

情况1: 阿姨只告诉来水了,但没有告诉我是哪个水龙头来水了,要自己一个一个去尝试。(select/poll 场景)

情况2: 舍管阿姨会告诉我哪几个水龙头有水了,小明同学不需要一个个打开看(epoll 场景)

还是同步IO 

所以,IO多路复用的特点是通过一种机制,一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入就绪状态,select()函数就可以返回。

select

在一段指定的时间内,监听用户感兴趣的文件描述符上可读、可写和异常等事件。

#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

nfds    最大的文件描述符加1。(同时监听多个socket)

readfds 用于检查可读的。

writefds:用于检查可写性

exceptfds:用于检查异常的数据

timeout:一个指向timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待。

timeval结构的定义:

struct timeval{

long tv_sec; // seconds

long tv_usec; // microseconds

}

返回值:  >0  是已就绪的文件句柄的总数, =0 超时, <0 表示出错,错误: 并设置errno

#include <sys/select.h> 
int FD_ZERO(fd_set *fdset); //一个 fd_set类型变量的所有位都设为 0 
int FD_CLR(int fd, fd_set *fdset); //清除某个位时可以使用 
int FD_SET(int fd, fd_set *fd_set); //设置变量的某个位置位 
int FD_ISSET(int fd, fd_set *fdset); //测试某个位是否被置位 

代码示例

服务端

server.c

#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <sys/time.h> 
#include <sys/ioctl.h> 
#include <unistd.h> 
#include <stdlib.h>int main()
{int server_sockfd, client_sockfd;int server_len, client_len;struct sockaddr_in server_address;struct sockaddr_in client_address;int result;fd_set readfds, testfds;server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socket server_address.sin_family = AF_INET;server_address.sin_addr.s_addr = htonl(INADDR_ANY);server_address.sin_port = htons(9000);server_len = sizeof(server_address);bind(server_sockfd, (struct sockaddr*)&server_address, server_len);listen(server_sockfd, 5); //监听队列最多容纳5个 FD_ZERO(&readfds);FD_SET(server_sockfd, &readfds);//将服务器端socket加入到集合中while (1){char ch;int fd;int nread;testfds = readfds;//将需要监视的描述符集copy到select查询队列中,select会对其修改,所以一定要分开使用变量 printf("server waiting\n");/*无限期阻塞,并测试文件描述符变动 */result = select(FD_SETSIZE, &testfds, (fd_set*)0, (fd_set*)0, (struct timeval*)0); //FD_SETSIZE:系统默认的最大文件描述符if (result < 1){perror("server5");exit(1);}/*扫描所有的文件描述符*/for (fd = 0; fd < FD_SETSIZE; fd++){/*找到相关文件描述符*/if (FD_ISSET(fd, &testfds)){/*判断是否为服务器套接字,是则表示为客户请求连接。*/if (fd == server_sockfd){client_len = sizeof(client_address);client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_address, &client_len);FD_SET(client_sockfd, &readfds);//将客户端socket加入到集合中printf("adding client on fd %d\n", client_sockfd);}/*客户端socket中有数据请求时*/else{ioctl(fd, FIONREAD, &nread);//取得数据量交给nread/*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 */if (nread == 0){close(fd);FD_CLR(fd, &readfds); //去掉关闭的fdprintf("removing client on fd %d\n", fd);}/*处理客户数据请求*/else{read(fd, &ch, 1);sleep(5);printf("serving client on fd %d\n", fd);ch++;write(fd, &ch, 1);}}}}}return 0;
}

客户端

client.c

#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <stdlib.h>
#include <sys/time.h>int main()
{int client_sockfd;int len;struct sockaddr_in address;//服务器端网络地址结构体 int result;char ch = 'A';client_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立客户端socket address.sin_family = AF_INET;address.sin_addr.s_addr = inet_addr("127.0.0.1");address.sin_port = htons(9000);len = sizeof(address);result = connect(client_sockfd, (struct sockaddr*)&address, len);if (result == -1){perror("oops: client2");exit(1);}//第一次读写write(client_sockfd, &ch, 1);read(client_sockfd, &ch, 1);printf("the first time: char from server = %c\n", ch);sleep(5);//第二次读写write(client_sockfd, &ch, 1);read(client_sockfd, &ch, 1);printf("the second time: char from server = %c\n", ch);close(client_sockfd);return 0;
}

poll
poll 和select 区别

select 有文件句柄上线设置,值为FD_SETSIZE而poll 理论上没有限制!

函数原型
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
输入参数

fds://可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回

            struct pollfd {

                  int fd;                /*文件描述符   open打开的那个*/

                  short events;     /*请求的事件类型,监视驱动文件的事件掩码*/  POLLIN | POLLOUT

                  short revents;    /*驱动文件实际返回的事件*/

            }

            nfds:  //监测驱动文件的个数 = 要监听的最大的文件描述符+1

            timeout://超时时间,单位是ms 当timeout时间内没有时间发生,poll就会返回

事件类型events 可以为下列值:

        POLLIN           有数据可读

        POLLRDNORM 有普通数据可读,等效与POLLIN

        POLLPRI         有紧迫数据可读

        POLLOUT        写数据不会导致阻塞

        POLLER          指定的文件描述符发生错误

        POLLHUP        指定的文件描述符挂起事件

        POLLNVAL      无效的请求,打不开指定的文件描述符

返回值

        有事件发生  返回revents域不为0的文件描述符个数

        超时:return 0

        失败:return  -1   错误:errno

代码示例

server_poll.c

#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>#define MAX_FD  8192
struct pollfd  fds[MAX_FD];
int cur_max_fd = 0;int main()
{int server_sockfd, client_sockfd;int server_len, client_len;struct sockaddr_in server_address;struct sockaddr_in client_address;int result;//fd_set readfds, testfds;server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socketserver_address.sin_family = AF_INET;server_address.sin_addr.s_addr = htonl(INADDR_ANY);server_address.sin_port = htons(9000);server_len = sizeof(server_address);bind(server_sockfd, (struct sockaddr*)&server_address, server_len);listen(server_sockfd, 5); //监听队列最多容纳5个//FD_ZERO(&readfds);//FD_SET(server_sockfd, &readfds);//将服务器端socket加入到集合中fds[server_sockfd].fd = server_sockfd;fds[server_sockfd].events = POLLIN;fds[server_sockfd].revents = 0;if(cur_max_fd <= server_sockfd){cur_max_fd = server_sockfd + 1;}while (1){char ch;int i, fd;int nread;//testfds = readfds;//将需要监视的描述符集copy到select查询队列中,select会对其修改,所以一定要分开使用变量printf("server waiting\n");/*无限期阻塞,并测试文件描述符变动 */result = poll(fds, cur_max_fd, 1000);//result = select(FD_SETSIZE, &testfds, (fd_set*)0, (fd_set*)0, (struct timeval*)0); //FD_SETSIZE:系统默认的最大文件描述符if (result < 0){perror("server5");exit(1);}/*扫描所有的文件描述符*/for (i = 0; i < cur_max_fd; i++){/*找到相关文件描述符*/if (fds[i].revents){fd = fds[i].fd;/*判断是否为服务器套接字,是则表示为客户请求连接。*/if (fd == server_sockfd){client_len = sizeof(client_address);client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_address, &client_len);fds[client_sockfd].fd = client_sockfd;//将客户端socket加入到集合中fds[client_sockfd].events = POLLIN;fds[client_sockfd].revents = 0;if(cur_max_fd <= client_sockfd){cur_max_fd = client_sockfd + 1;}printf("adding client on fd %d\n", client_sockfd);//fds[server_sockfd].events = POLLIN;}/*客户端socket中有数据请求时*/else{//ioctl(fd, FIONREAD, &nread);//取得数据量交给nreadnread = read(fd, &ch, 1);/*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 */if (nread == 0){close(fd);memset(&fds[i], 0, sizeof(struct pollfd)); //去掉关闭的fdprintf("removing client on fd %d\n", fd);}/*处理客户数据请求*/else{//read(fds[fd].fd, &ch, 1);sleep(5);printf("serving client on fd %d, read: %c\n", fd, ch);ch++;write(fd, &ch, 1);//fds[fd].events = POLLIN;}}}}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/46450.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机删除的文件能恢复吗?删除不等于永别,3个技巧助你找回

安卓手机中的文件&#xff0c;就像是数字世界里的繁星&#xff0c;记录着我们的点点滴滴。然而&#xff0c;有时我们可能会不小心删除了某些重要的文件&#xff0c;让我们感到惋惜和困惑。删除的文件能恢复吗&#xff1f;别担心&#xff0c;删除并不等于永别&#xff0c;我们也…

CentOS 停服后,服务器 OS 路在何方?

2024 年 6 月 30 日&#xff0c;CentOS Linux 7 终止其生命周期&#xff08;EOL&#xff09;&#xff0c;至此 CentOS 全系列版本也已停止维护&#xff0c;属于 CentOS 的时代彻底终结。CentOS 停止维护后&#xff0c;用户将无法获得包括问题修复和功能更新在内的任何软件维护和…

小程序里面使用vant ui中的vant-field组件,如何使得输入框自动获取焦点

//.wxml <van-fieldmodel:value"{{ userName }}"placeholder"请输入学号"focus"{{focusUserName}}"/>// .js this.setData({focusUserName: true});vant-field

MSVC2017+Qt 打包

在环境变量下配置好 QT 和 MSVC 的路径 相关搜索&#xff1a; 找不到msvcp140.dll 1.搜索 Qt 选择在编译器路径下打开 2. Windeployqt 生成打包&#xff0c;正常情况下生成 VC 相关package&#xff0c; 即 msvcp140.dll 等MSVC 相关 但是lz尝试没有生成 解决办法 先将生成…

数据库作业d8

要求&#xff1a; 一备份 1 mysqldump -u root -p booksDB > booksDB_all_tables.sql 2 mysqldump -u root -p booksDB books > booksDB_books_table.sql 3 mysqldump -u root -p --databases booksDB test > booksDB_and_test_databases.sql 4 mysql -u roo…

MySQL 中的几种锁

MySQL 中的锁 #按锁粒度如何划分? 按锁粒度划分的话&#xff0c;MySQL 的锁有&#xff1a; 表锁&#xff1a;开销小&#xff0c;加锁快&#xff1b;锁定力度大&#xff0c;发生锁冲突概率高&#xff0c;并发度最低;不会出现死锁。行锁&#xff1a;开销大&#xff0c;加锁慢…

电脑压缩软件哪个好?WinRAR、7-Zip、Bandizip 还是360压缩

文件压缩软件已成为我们日常工作中不可或缺的一部分&#xff0c;它不仅能够帮助我们节省存储空间&#xff0c;还能提高文件传输效率。本文简鹿办公小编将对四款主流的电脑压缩软件进行对比&#xff0c;它们分别是 WinRAR、7-Zip、Bandizip 和 360 压缩。 一、WinRAR WinRAR 是…

Qt中文个数奇数时出现问号解决

Qt中文个数奇数时出现问号解决 目录 Qt中文个数奇数时出现问号解决问题背景问题场景解决方案 问题背景 最近在开发一个小工具&#xff0c;涉及到一些中文注释自动打印&#xff0c;于是摸索如何把代码里面的中文输出到csv文件中&#xff0c;出现了乱码&#xff0c;按照网上的攻…

vue2-Django3-iframe解决方案,处理安全策略,事件拦截,处理iframe重载等

目录 简介 实现iframe 后端安全策略 通过Ngnix代理实现SAMEORIGIN iframe的事件拦截&#xff0c;自定义处理 iframe的状态保持&#xff08;解决vue中iframe重载&#xff09; 解决方法 简介 Iframe&#xff08;内联框架&#xff09;是一种HTML元素&#xff0c;用于在网页…

周鸿祎为什么建议Java、前端、大数据、PHP开发都要学一下大模型?_ai大模型全栈工程师跟java有关吗

ChatGPT的出现在全球掀起了AI大模型的浪潮&#xff0c;2023年可以被称为AI元年&#xff0c;AI大模型以一种野蛮的方式&#xff0c;闯入你我的生活之中。 从问答对话到辅助编程&#xff0c;从图画解析到自主创作&#xff0c;AI所展现出来的能力&#xff0c;超出了多数人的预料&…

智慧公厕系统实现人性化与节能化的完美结合

在当今社会&#xff0c;科技的飞速发展正不断改变着我们的生活方式&#xff0c;公厕也不例外。智慧公厕系统的出现&#xff0c;不仅提升了人们的使用体验&#xff0c;更实现了人性化与节能化的完美结合&#xff0c;为城市公共服务带来了全新的变革。 一、人性化&#xff0c;是智…

echarts 实现水利计算模型-雨量,流量,时间分割线

需求背景解决效果ISQQW代码地址index.vue 需求背景 实现水利计算模型-雨量&#xff0c;流量&#xff0c;时间分割线 解决效果 ISQQW代码地址 链接 index.vue <!--/** * author: liuk * date: 2024/06/13 * describe: 洪水预报结果图表 */--> <template><di…

算法篇 滑动窗口 leetcode 长度最小的子数组

长度最小的子数组 1. 题目描述2. 算法图分析2.1 暴力图解2.2 滑动窗口图解 3. 代码演示 1. 题目描述 2. 算法图分析 2.1 暴力图解 2.2 滑动窗口图解 3. 代码演示

数据结构进阶——使用数组实现栈和队列详解与示例(C,C#,C++)

文章目录 1、数组实现栈栈的基本操作C语言实现C#语言实现 2、 数组实现队列队列的基本操作C语言实现C# 语言实现C语言实现 总结 在编程世界中&#xff0c;数据结构是构建高效算法的基石。栈和队列作为两种基本的数据结构&#xff0c;它们的应用非常广泛。本文将带领大家使用C&a…

股票质押约定购回:机制、风险与策略!

​股票质押约定购回&#xff1a;机制、风险与策略 在复杂的金融市场中&#xff0c;股票质押约定购回作为一种常见的融资手段&#xff0c;受到了众多投资者和企业的关注。本文将深入探讨股票质押约定购回的定义、运作机制、潜在风险以及投资者和企业在操作时应采取的策略。 一、…

HackChat匿名聊天室

匿名聊天 聊天室地址 这是一款极简、无干扰的聊天应用程序&#xff0c;可以让你专注于交流而不必担心干扰. 频道通过 url 创建、加入和共享&#xff0c;通过更改问号后的文本来创建自己的频道. hack.chat 服务器上不会保留任何消息历史记录&#xff0c;链接断开消息就会删除. …

尚硅谷大数据技术-数据湖Hudi视频教程-笔记03【Hudi集成Spark】

大数据新风口&#xff1a;Hudi数据湖&#xff08;尚硅谷&Apache Hudi联合出品&#xff09; B站直达&#xff1a;https://www.bilibili.com/video/BV1ue4y1i7na 尚硅谷数据湖Hudi视频教程百度网盘&#xff1a;https://pan.baidu.com/s/1NkPku5Pp-l0gfgoo63hR-Q?pwdyyds阿里…

基于5个K7的多FPGA PCIE总线架构的高性能数据预处理平台

板载FPGA实时处理器&#xff1a;XCKU060-2FFVA15172个QSFP光纤接口&#xff0c;最大支持10Gbps/lane板载DMA控制器&#xff0c;能实现双向DMA高速传输支持x8 PCIE主机接口&#xff0c;系统带宽5GByte/s1个R45自适应千兆以太网口1个FMC子卡扩展接口 基于PCIE总线架构的高性能数据…

DIY系列——自制简易笔记本电脑散热器

前言&#xff1a;为什么要自制笔记本电脑散热器&#xff1f; 夏天到了&#xff0c;电脑的使用频率也在增加。尤其是笔记本电脑&#xff0c;长时间运行后很容易发热&#xff0c;影响性能和寿命。市场上有很多散热器产品&#xff0c;但价格不菲且效果参差不齐。如果你动手能力强…

【原创】springboot+mysql图书共享交流平台设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…