Linux——socket高级通信 select socket选项 Http

来源:互联网 发布:算法 n=n-1 编辑:程序博客网 时间:2024/06/05 05:10
回顾:
    多进程的问题:数据共享。
    多进程的问题:进程的上下文环境(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:异常                                                            
    2.IO能否发出信号?异步IO就是通过信号工作.

    3.应用使用select

select.c

#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);//加入读描述符r=select(1,&fds,0,0,0);printf("有数据输入!\n");r=read(0,buf,99);//只要0不读取 再次循环到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.建立socketsfd=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.1.116",&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    );    //阻塞超时

poll.c

#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有哪些选项可以设置 man 7 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获取选项

                 int getsockopt(int sockfd,//文件描述符
                                         int level,//哪个选项
                                         int optname,//选项名
                                         void *optval,//返回选项值
                                         socklen_t *optlen);//选项值的长度 

       
案例:

        判定一个socket的数据类型AF_INET:SOCK_STREAM     SOCK_DGRAM SOCK_RAW

socketopt.c

#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);//缓冲大小printf("缓冲大小:%u\n",type);/* 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");}*/}



案例:
        使用选项进行数据广播.
        cast_A发送
                建立socket
                设置广播选项

                发送数据(广播方式发送)

cast_A.c

#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.1.255");//广播地址r=sendto(fd,"Hello",5,0,(struct sockaddr*)&dr,sizeof(dr));if(fd==-1) printf("3:%m\n");close(fd);}


                
        case_B接收
                建立socket
                设置地址可重用选项
                绑定地址

                接收数据

cast_B.c

#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.1.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

#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.1.116");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/*MSG_OOB*/);//如果接收到带外数据则发送个信号SIGURGif(r>0){buf[r]=0;printf("接收数据:%s\n",buf);}else{break;}}close(cfd);close(fd);}



                  

oob_client.c 

#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.1.116");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);//如果频繁往缓冲写 缓冲内容过多而由于                           //只有一个带外数据 体现不出sleep(1);}close(fd);}
        1.OOB数据只能接收/发送成功一个字符
        2.普通数据使用一般方式接收与发送,OOB数据使用MSG_OOB接收与发送
        3.一个数据使用MSG_OOB,则最后一个是OOB,其他非OOB数据
        4.问题:OOB数据是优先数据。优先体现在什么地方?
            
四.HTTP协议以及应用                                                
    1.HTTP协议版本HTTP1.0 HTTP1.1    
    2.HTTP是应用协议
    3.HTTP协议分成:
                请求协议 浏览器发给Web服务器的

                响应协议 Web服务器发给浏览器的

index.html

<html><head><title>Http Protocol</title></head><body><form action="http://192.168.1.116:10000/index.php" target="_self" method="post">姓名:<input type="text" name="user" value="tom"/><br/>口令:<input type="password" name="pass" value="tom"/><br/><input type="submit" value="登录"/></form></body></html>
用浏览器打开这个网页 就向服务器发送信息 服务器就收到以下信息


    4.请求协议的格式:
            请求行(请求方法 请求资源 协议版本)  
            请求体(请求头:请求值)
            空行

            数据(querystring:key=value&key=value)

http_c.c//类似浏览器功能 服务器会跟据请求给出响应

#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; //建立socketfd=socket(AF_INET,SOCK_STREAM,0);//连接服务器192.168.0.72dr.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.响应协议的格式
            响应行(协议版本 响应码 响应码的文本描述)
            响应体(响应头: 响应值)
            空行

            数据(普通数据/分块数据)

http_s.c

#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.1.116");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);}

用浏览器访问http://192.168.1.116:10000/index.html
                    
         1XX     正在处理
         2XX        响应成功200
         3XX        继续处理
         4XX        客户错误

         5XX        服务器错误


五.ioctl函数
        实现ifconfig工具
        
总结:
        重点:
                select
                广播
                
        了解:
                OOB数据
                HTTP协议
        
        应用:
                独立编写TCP服务器端的select模式
                编写广播
                能够请求一个网页,并且解析响应
作业:
        1.把聊天程序使用poll实现
        2.使用UDP的广播,发送一个文件                
        3.随意挑选网站,把主页下载并保存成html文件

 
0 0
原创粉丝点击