Linux 嵌入式开发 网络编程: day4

 2023-09-06 阅读 30 评论 0

摘要:【1】IO多路复用(并发服务器) 想实现服务器处理多个客户端连接请求或数据收发的话,(实现并发) 1. 多进程的方式; 2. 多线程的方式; 3. IO多路复用 a. select 弊端: 1. 一个进程最多只能监听1024个文件描述符 (千级别࿰
【1】IO多路复用  (并发服务器)
想实现服务器处理多个客户端连接请求或数据收发的话,(实现并发)
1. 多进程的方式;
2. 多线程的方式;
3. IO多路复用
a. select
弊端: 1. 一个进程最多只能监听1024个文件描述符 (千级别)
2. select是一种轮询的机制;
3. 涉及到用户态和内核态的数据拷贝;
b. poll
1. 优化文件描述符个数的限制;
2. poll是一种轮询的机制;
3. 涉及到用户态和内核态的数据拷贝;
  int poll(struct pollfd *fds, nfds_t nfds, int timeout);
基本思想:同select;
参数:
struct pollfd *fds
关心的文件描述符数组struct pollfd fds[N];
nfds:个数
timeout: 超时检测
毫秒级的:如果填1000就是1秒
如果填-1,阻塞
问题:
我想检测是键盘事件(标准输入 文件描述如为0 ),
还是鼠标事件(文件描述符是/dev/input/mouse1);                  
1. 创建一个结构体数组
struct pollfd fds[2];
2. 将你关心的文件描述符加入到结构体成员中
struct pollfd {
int   fd;         // 关心的文件描述符;
short events;     // 关心的事件,读
short revents;    // 如果产生事件,则会自动填充该成员的值
};
// 键盘
fds[0].fd = 0;
fds[0].events = POLLIN;
//鼠标
fds[1].fd = mouse1_fd;
fds[1].events = POLLIN;
3. 调用poll函数
如果返回表示有事件产生;
poll(fds,2,1000)
4. 判断具体是哪个文件描述符产生了事件
if(fds[0].revents == POLLIN)
{
....
}
 c. epoll
1. 没有文件描述符的限制
2. 异步IO,当有事件产生,文件描述符主动调用callback(每一个文件描                                                                                      述符都有自己的callback)
3. 不用数据拷贝;
3个功能函数:
#include <sys/epoll.h>
 int epoll_create(int size);//创建红黑树根节点
//成功时返回epoll文件描述符,失败时返回-1
//控制epoll属性
  int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd:epoll_create函数的返回句柄。
op:表示动作类型。有三个宏 来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中
EPOLL_CTL_MOD:修改已注册fd的监听事件
EPOLL_CTL_DEL:从epfd中删除一个fd
FD:需要监听的fd。
event:告诉内核需要监听什么事件
EPOLLIN:表示对应文件描述符可读
EPOLLOUT:可写
EPOLLPRI:有紧急数据可读;
EPOLLERR:错误;
EPOLLHUP:被挂断;
EPOLLET:触发方式,电平触发;
ET模式:表示状态的变化;
//成功时返回0,失败时返回-1
//等待事件到来
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
功能:等待事件的产生,类似于select的用法
epfd:句柄;
events:用来从内核得到事件的集合;
maxevents:表示每次能处理事件最大个数;
timeout:超时时间,毫秒,0立即返回,-1阻塞
//成功时返回发生事件的文件描述数,失败时返回-1
【2】服务器模型
1、循环服务器:
依次只能处理一个客户端,等这个客户端所有请求都完成后(客户端关闭),
才能处理下一个客户端
缺点: 循环服务器所处理的客户端,不能做耗时动作
socket(...);
bind(...);
listen(...);
while(1)
{    
accept(...);
while(1)
{          
recv(...);
process(...);
send(...);
}     
close(...);
}
2、并发服务器:
可以同时处理多个客户端的请求,创建子进程/子线程来处理跟客户端的请求
流程如下:
void handler(int sigo)
{
while (waitpid(-1, NULL, WNOHANG) > 0);     //一个SIGCHLD可能对应多个僵尸进程,循环收尸
}
int sockfd = socket(...);
bind(...);
listen(...);
signal(SIGCHLD, handler);  //注册SIGCHLD信号,当产生SIGCHLD信号时调用handler函数
while(1) {
int connfd = accept(...);
if (fork() == 0) {
close(sockfd);
while(1)
{
recv(...);
process(...);
send(...);
}
close(connfd);           
exit(...);
}
close(connfd);
}
效果: 父进程--只做链接    子进程--跟客户端的通信(耗时)            
【3】    wireshark 抓包工具
windows版本或ubuntu版本
1. 安装
sudo apt-get install wireshark
2. 运行
sudo wireshark
3. 过滤
tcp.port == 8888
4. 抓的是流经eth0网卡的数据
服务器端代码运行在ubuntu
客户端代码运行在windows下
ip.addr == 192.168.1.31
【4】三次握手和四次挥手
ACK: 确认包;握手
SYN:  同步包;握手
FIN: 结束包;挥手    
三次握手:
在建立连接的时候进行。客户端主动方,连接服务器;
第一次握手(connect),有客户端向服务器发送SYN同步包,告诉服务器我要连接了
第二次握手,服务器收到SYN后,要回复一个确认包,告诉客户端已经收到了,
并发送同步包(确认一下服务器发的包是否能发出去);
第三次握手,客户端发送确认包给服务器;
四次挥手:
TCP套接字是全双工的;close时,两端都要关闭
客户端和服务器都可以作为主动方;
第一次挥手;客户端调用close,发送FIN包;
第二、三次挥手;服务器端,先回复确认包,确认已经收到断开连接的请求,
并且再发FIN结束包;
第四次挥手;客户端再次发送ACK确认包;        
【5】网络信息检索和套接字属性设置
getsockopt()/setsockopt() 获取/设置一个套接口选项
bind failed.: Address already in use
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
功能:设置套接字选项
参数:    
sockfd:套接字
level: 层次  
SOL_SOCKET  应用层
IPPROTO_IP  IP层/网络层
IPPRO_TCP  传输层
optname:  具体值
SO_REUSEADDR
optval:值 (有对应的类型)
重用本地地址和端口        
允许或不允许
optlen:
大小
允许地址端口重用,代码如下:
int optval = 1;
socklen_t optval_len = sizeof(optval);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,optval_len);

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/4132.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息