回顾:
多进程的问题:数据共享。
多进程的问题: 进程的上下文环境(context)
文件描述符号是整数以及对应上下文环境
多进程的问题:上下文环境共享
一.SELECT TCP服务器编程模式
1.select函数
int select(
int fds,//建议是监控的文件描述符号的最大值+1
fd_set *readfds,//读文件描述符号集合
//该参数既是输入,也是输出
//输入:被监控的描述符号
//输出:有数据的描述符号
fd_set *writefds,
fd_set *errfds,
struct timeval*timeout);//指定阻塞时间限制
//为NULL,永久
返回:
>0:发生改变的文件描述符号个数
=0:时间限制过期
=-1:异常
//select用法 #include <stdio.h> #include <fcntl.h> #include <signal.h> #include <unistd.h> #include <sys/select.h> main() { fd_set fds; int r; char buf[100]; while(1) { FD_ZERO(&fds); //清空 FD_SET(0,&fds); //将描述符0加入 r=select(1,&fds,0,0,0); //监控 printf("有数据输入!\n"); r=read(0,buf,99); } }
2.IO能否发出信号?
异步IO就是通过信号工作.
3.应用使用select
4.使用select实现TCP的多客户连接与处理
看个小例子:
//chatServer.c //聊天服务器端 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> main() { int sfd;//服务器描述符号 int fdall[100];//客户描述符号 int count;//客户个数 int r;//返回值(异常处理) struct sockaddr_in dr;//IP地址与端口 fd_set fds;//被select监控的描述符号集合 int maxfd;//最大文件描述符号 int i,j;//循环变量 char buf[1024];//客户聊天数据 //1.建立socket sfd=socket(AF_INET,SOCK_STREAM,0); if(sfd==-1) printf("1:%m\n"),exit(-1); printf("socket ok!\n"); //2.绑定地址与端口 dr.sin_family=AF_INET; dr.sin_port=htons(8866); inet_aton("192.168.180.92",&dr.sin_addr); r=bind(sfd,(struct sockaddr*)&dr,sizeof(dr)); if(r==-1) printf("2:%m\n"),close(sfd),exit(-1); printf("bind ok!\n"); //3.监听 r=listen(sfd,10); if(r==-1) printf("3:%m\n"),close(sfd),exit(-1); printf("listen ok!\n"); //初始化 count=0; maxfd=0; FD_ZERO(&fds); for(i=0;i<100;i++) { fdall[i]=-1; } while(1) { //4.构造监听的描述符号集合 //4.1.清空 FD_ZERO(&fds); maxfd=0; //4.2.加入服务器描述符号 FD_SET(sfd,&fds); maxfd=maxfd>=sfd?maxfd:sfd; //4.3.加入客户描述符号 for(i=0;i<count;i++) { if(fdall[i]!=-1) { FD_SET(fdall[i],&fds); maxfd=maxfd>=fdall[i]?maxfd:fdall[i]; } } //5.使用select循环控制描述符号集合 r=select(maxfd+1,&fds,0,0,0); if(r==-1) { printf("服务器崩溃!\n"); break; } //6.分两种情况处理: //6.1.有客户连接:服务器描述符号 if(FD_ISSET(sfd,&fds)) { fdall[count]=accept(sfd,0,0); if(fdall[count]==-1) { printf("服务器崩溃!\n"); //释放所有客户 break; } printf("有客户连接!\n"); count++; } //6.2.有客户发送数据:客户描述符号 for(i=0;i<count;i++) { //判定改变描述符号是否存在 if( fdall[i]!=-1 && FD_ISSET(fdall[i],&fds)) { //读取数据 r=recv(fdall[i],buf,1023,0); if(r==0){ printf("有客户退出!\n"); close(fdall[i]); fdall[i]=-1; } if(r==-1){ printf("网络故障!\n"); close(fdall[i]); fdall[i]=-1; } if(r>0) { //广播数据 buf[r]=0; printf("广播数据:%s\n",buf); for(j=0;j<count;j++) { if(fdall[j]!=-1) { send(fdall[j],buf,r,0); } } } } } } }
5.poll模式
int poll(
struct pollfd *fds,//监控的描述符号
int nfds,//监控的描述符号的个数
int timeout ); //阻塞超时
#include <stdio.h> #include <fcntl.h> #include <signal.h> #include <unistd.h> #include <sys/poll.h> main() { struct pollfd fds[1]; int r; char buf[100]; fds[0].fd=0; fds[0].events=POLLIN; while(1) { r=poll(fds,1,-1); if(fds[0].revents & POLLIN) { printf("有数据输入!\n"); r=read(0,buf,99); } } }
二.Socket选项设置
1.socket有哪些选项可以设置
ARP
|
IP
|
|-----------------|
UDP TCP
通用选项:
SOL_SOCKET
SO_BROADCAST 广播
SO_RCVBUF 描述符号的缓冲的大小
SO_SNDBUF 描述符号的缓冲的大小
SO_REUSEADDR 地址反复绑定
SO_TYPE 描述符号类型SOCK_STREAM SOCK_DGRAM?
ICMP选项
IPPTOTO_ICMP
ICMP_FILTER
IP选项(干预系统生成IP头)
IPPROTO_IP
......
......
UDP选项
IPPROTO_UDP
......
TCP选项
IPPROTO_TCP
......
setsockopt设置选项
getsockopt获取选项
案例:
判定一个socket的数据类型AF_INET:SOCK_STREAM SOCK_DGRAM SOCK_RAW
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> main() { int fd; int type; int len; len=sizeof(type); fd=socket(AF_INET,SOCK_DGRAM,0); getsockopt(fd,SOL_SOCKET,SO_TYPE,&type,&len); printf("%u:%u\n",SOCK_STREAM,type); if(type & SOCK_STREAM) { printf("流!\n"); } if(type & SOCK_DGRAM) { printf("报文!\n"); } }
//获得缓冲大小 #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> main() { int fd; int type; int len; len=sizeof(type); fd=socket(AF_INET,SOCK_DGRAM,0); getsockopt(fd,SOL_SOCKET,SO_RCVBUF,&type,&len); //在这里把参数SO_TYPE变成SO_RCVBUF即可 printf("缓冲大小:%u\n",type); }
案例:
使用选项进行数据广播.
cast_A发送
建立socket
设置广播选项
发送数据(广播方式发送)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> main() { int fd; int opt=1; int r; struct sockaddr_in dr; //1.选项设置 fd=socket(PF_INET,SOCK_DGRAM,0); if(fd==-1) printf("1:%m\n"),exit(-1); r=setsockopt(fd,SOL_SOCKET,SO_BROADCAST, &opt,sizeof(opt)); if(r==-1) printf("2:%m\n"),exit(-1); dr.sin_family=AF_INET; dr.sin_port=htons(9999); //2.使用广播IP地址 dr.sin_addr.s_addr=inet_addr("192.168.180.255"); r=sendto(fd,"Hello",5,0, (struct sockaddr*)&dr,sizeof(dr)); if(fd==-1) printf("3:%m\n"); close(fd); }
case_B接收
建立socket
设置地址可重用选项
绑定地址
接收数据
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> main() { int fd; int opt=1; char buf[100]; int r; struct sockaddr_in dr; fd=socket(PF_INET,SOCK_DGRAM,0); if(fd==-1) printf("1:%m\n"),exit(-1); //1.选项 r=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR, &opt,sizeof(opt)); if(r==-1) printf("2:%m\n"),exit(-1); dr.sin_family=AF_INET; dr.sin_port=htons(9999); //2.广播地址 dr.sin_addr.s_addr=inet_addr("192.168.180.255"); r=bind(fd,(struct sockaddr*)&dr,sizeof(dr)); if(r==-1) printf("3:%m\n"),exit(-1); r=recv(fd,buf,100,0); if(r>0) { buf[r]=0; printf("广播数据:%s\n",buf); } close(fd); }
三.OOB数据(TCP)
优先数据(带外数据)
send(,MSG_OOB);
recv(,MSG_OOB);
案例:
oob_server.c
recv MSG_OOB
oob_client.c
send MSG_OOB
//oobServer #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <fcntl.h> int fd,cfd; void handle(int s) { char data[100]; int r; if(s==SIGURG) { r=recv(cfd,data,100,MSG_OOB); data[r]=0; printf("$$%s\n",data); } } main() { int opt=1; char buf[100]; int r; struct sockaddr_in dr; fd=socket(PF_INET,SOCK_STREAM,0); if(fd==-1) printf("1:%m\n"),exit(-1); printf("1\n"); dr.sin_family=AF_INET; dr.sin_port=htons(10000); dr.sin_addr.s_addr=inet_addr("192.168.180.92"); r=bind(fd,(struct sockaddr*)&dr,sizeof(dr)); if(r==-1) printf("2:%m\n"),exit(-1); printf("2\n"); r=listen(fd,10); if(r==-1) printf("3:%m\n"),exit(-1); printf("3\n"); signal(SIGURG,handle); cfd=accept(fd,0,0); fcntl(cfd,F_SETOWN,getpid()); if(cfd==-1) printf("4:%m\n"),exit(-1); printf("4\n"); while(1) { r=recv(cfd,buf,100,0); if(r>0) { buf[r]=0; printf("接收数据:%s\n",buf); } else { break; } } close(cfd); close(fd); }
//oobClient #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> main() { int fd; int opt=1; char buf[100]; int r; struct sockaddr_in dr; fd_set fds; fd=socket(PF_INET,SOCK_STREAM,0); if(fd==-1) printf("1:%m\n"),exit(-1); printf("1\n"); dr.sin_family=AF_INET; dr.sin_port=htons(10000); dr.sin_addr.s_addr=inet_addr("192.168.180.92"); r=connect(fd,(struct sockaddr*)&dr,sizeof(dr)); if(r==-1) printf("2:%m\n"),exit(-1); while(1) { FD_ZERO(&fds); FD_SET(fd,&fds); select(fd+1,0,&fds,0,0); send(fd,"Hello",5,MSG_OOB); } close(fd); }
OOB总结:
1.OOB数据只能一个字符
2.普通数据使用一般方式接收与发送,OOB数据使用MSG_OOB接收与发送
3.一个数据使用MSG_OOB,则最后一个是OOB,其他非OOB数据
4.问题:OOB数据是优先数据。优先体现在什么地方? 接收OOB数据的时候,会产生一个URG信号
四.HTTP协议以及应用
1.HTTP协议版本HTTP1.0 HTTP1.1
2.HTTP是应用协议
3.HTTP协议分成:
请求协议
响应协议
4.请求协议的格式:
请求行(请求方法 请求资源 协议版本)
请求体(请求头:请求值)
空行
数据(querystring:key=value&key=value)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> main() { int fd; struct sockaddr_in dr; char strreq[1024]; char buf[10*1024]; int r; //建立socket fd=socket(AF_INET,SOCK_STREAM,0); //连接服务器192.168.0.72 dr.sin_family=AF_INET; dr.sin_port=htons(80); dr.sin_addr.s_addr=inet_addr("192.168.0.72"); r=connect(fd,(struct sockaddr*)&dr,sizeof(dr)); //构建http请求字符串 sprintf(strreq, "GET /index.php HTTP/1.1\r\n" "Host: 192.168.0.72:80\r\n" "User-Agent: Tarena5.0\r\n" "Accept: text/html,image/png\r\n" "Accept-Language: zh-cn\r\n" "Accept-Charset: gb2312,utf-8\r\n" "Keep-Alive: 300\r\n" "Connection: keep-alive\r\n" "\r\n"); //发送http请求字符串 r=send(fd,strreq,strlen(strreq),0); //等待服务器响应 //while(1) //{ r=recv(fd,buf,1024,0); //if(r<=0) break; printf("========================\n"); printf("%s\n",buf); printf("========================\n"); //} close(fd); }
5.响应协议的格式
响应行(协议版本 响应码 响应码的文本描述)
响应体(响应头: 响应值)
空行
数据(普通数据/分块数据)
响应码:
1XX 正在处理
2XX 响应成功(200表示完全成功)
3XX 继续处理
4XX 客户错误
5XX 服务器错误
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <fcntl.h> #include <string.h> int fd,cfd; main() { char buf[1024]; int r; struct sockaddr_in dr; char strres[1024]; fd=socket(PF_INET,SOCK_STREAM,0); if(fd==-1) printf("1:%m\n"),exit(-1); printf("1\n"); dr.sin_family=AF_INET; dr.sin_port=htons(10000); dr.sin_addr.s_addr=inet_addr("192.168.180.92"); r=bind(fd,(struct sockaddr*)&dr,sizeof(dr)); if(r==-1) printf("2:%m\n"),exit(-1); printf("2\n"); r=listen(fd,10); if(r==-1) printf("3:%m\n"),exit(-1); printf("3\n"); cfd=accept(fd,0,0); if(cfd==-1) printf("4:%m\n"),exit(-1); printf("4\n"); sprintf(strres, "HTTP/1.1 200 OK\r\n" "Server: tarena2.0\r\n" "Content-Type: text/html\r\n" "Content-Length: 28\r\n" "Connection: keep-alive\r\n" "\r\n" "<font color=red>靓崽!</font>"); while(1) { r=recv(cfd,buf,1024,0); if(r>0) { buf[r]=0; printf("接收数据:%s\n",buf); send(cfd,strres,strlen(strres),0); } else { break; } } close(cfd); close(fd); }
五.ioctl函数
实现ifconfig工具
总结:
重点:
select
广播
了解:
OOB数据
HTTP协议
应用:
独立编写TCP服务器端的select模式
编写广播
能够请求一个网页,并且解析响应
作业:
1.把聊天程序使用poll实现
2.使用UDP的广播,发送一个文件
3.随意挑选网站,把主页下载并保存成html文件