标签:
1、UDP流程2
提醒:客户端的sendto永远成功,即使服务端没运行,也不报错,所以有了第二种流程。
服务器 客户端
socket(SOCK_DGRAM) socket(SOCK_DGRAM)
| |
bind connect
| |
while(1){ while(1) {
| |
recvfrom(保存对方地址) send
| |
sendto recv}
} |
| close
close
备注:connect不是建立连接(抓不到三次握手的数据包),是固定接收方,假如接收方没运行,send报错
connect函数只能用于TCP程序??不对
2. 套接字缓冲区
---流式套接字在内核中有发送缓冲区和接收缓冲区
---send函数把数据发送到发送缓冲区,就成功返回。若发送缓冲区满了,send函数阻塞。
----send发送的数据,是先发到对方的接收缓冲区,把对方接收缓冲区填满后,自己的发送缓冲区开始累积,直到自己的发送缓冲区满,send才阻塞
----netstat -anpt (该命令可以看套接字的状态,比如地址、缓冲区等)
---recv函数是从接收缓冲区中取出数据。若接收缓冲区空,recv函数阻塞。
---数据报套接字没有发送缓冲区,所以UDP的发送,永远不会阻塞
3.IO模型
--阻塞IO(假如对方出故障,可能无限制阻塞,默认)
--非阻塞IO(循环调函数,浪费CPU,用的少)
---通过fcntl可以把套接字设置成非阻塞模式
int flag;
flag = fcntl(sockfd, F_GETFL, 0);
flag |= O_NONBLOCK; //#define O_NONBLOCK 00004000 倒数11位(从0开始数)默认是0,代表套接字默认阻塞,改成1就非阻塞
fcntl(sockfd, F_SETFL, flag);
---I/O多路复用
核心思想:使用单进程实现多进程的效果。
实现方法:
---构造一张有关描述符的表
fd_set fs; //相当于int fs[1024];数组的值是不确定的
FD_ZERO(&fs); // 数组清零 fs[0]=0.....
FD_SET(0,&fs); //数组写入1 fs[0]=1
FD_SET(listendfd,&fs);//fs[listendfd]=-1
---调用select函数
----selsct调用,进程会阻塞
----任何一个描述符有数据(就绪),进程都会被唤醒(select用一个进程,实现了多进程的效果)
----返回值,有数据的描述符个数(通过返回值不知道那个描述符有数据)
----描述符表中,没数据的1,被清0(是1的位置,有数据)
---循环处理描述符表中保留的1
for(i=0;i<=maxfd;i++)
{
if(FD_ISSET(i,&fs)==1)//(fs[i]==1)
{
if(i==0)
{
fgets();
}
if(i==listenfd)
{
connfd=accept(listenfd);
close(connfd);
}
}
}
---信号驱动IO
----特指信号SIGIO,编号29
----异步模式, 有数据时,内核给进程发SIGIO信号,程序执行信号函数
4.服务器模型
---循环服务器
---TCP的循环服务器很少用(客户端独占服务器,退出后,别的客户端才能连上)
---UDP的循环服务器可以使用(UDP协议无连接,可以同时和多个客户端通信)
---并发服务器(TCP)
思想:服务器接受客户端的连接请求后创建子进程来为客户端服务
缺点:为了响应客户机的请求,服务器要创建子进程来处理。 如果有多个客户端的话,服务器端需要创建多个子进程。过多的子进程会影响服务器端的运行效率。
--流程如下:
备注:当给同一个进程,在很短的时间内连续发送同样的信号,操作系统会吧很多信号合并,少发,所以要while(waitpid()>0);当没有僵尸时,wait返回0,信号函数执行结束
void handel_zombie(int sig)
{
while(waitpid(-1,NULL,WNOHANG)>0);
}
socket(...);
signal(....);
listen(...);
while(1) {
accept(...);
if (fork() = = 0) {
while(1) { recv(...); process(...); send(...); }
close(...);
exit(...);
}
close(...);
}
非阻塞IO,信号驱动io
I/O模型
标签:
原文地址:http://blog.csdn.net/qq_27205523/article/details/51953863