回顾:
多进程的问题:数据共享。
多进程的问题: 进程的上下文环境(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文件