您的位置:首页 > 运维架构 > Linux

c++ 学习笔记(高级linux编程) day11

2014-08-19 18:35 585 查看
linux高级编程day11 笔记

回顾:

多进程的问题:数据共享。

多进程的问题: 进程的上下文环境(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_STREAMSOCK_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文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: