zoukankan      html  css  js  c++  java
  • 基于visual c++之windows核心编程代码分析(32)HTTP协议编程

    超文本传送协议 (HTTP) 是一种通信协议,它允许将超文本标记语言 (HTML) 文档从 Web 服务器传送到 Web 浏览器。HTML 是一种用于创建文档的标记语言,这些文档包含到相关信息的链接。您可以单击一个链接来访问其它文档、图像或多媒体对象,并获得关于链接项的附加信息。   客户机和服务器必须都支持 HTTP,才能在万维网上发送和接收 HTML 文档并进行交互。  

     HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版,特别是在代理服务器中。HTTP/1.1的规范化工作正在进行之中,持久连接被默认采用,并能很好地配合代理服务器工作。而且HTTP-NG(Next Generation of HTTP)的建议已经提出。
      HTTP协议的主要特点可概括如下:

      支持客户/服务器模式。 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

    我们来亲自用VC++实现http协议

    #include <stdio.h> 
    #include <winsock2.h> 
    #define  MAXBUFLEN  20480 
    #define  HTTPADDLEN 50 
    #define  TIMEWAIT   2000 
    #pragma comment(lib,"ws2_32.lib")
    SOCKET   Global[1000]; 
    
    DWORD WINAPI  Proxy( LPVOID pSocket); 
    int   ParseHttpRequest(char * SourceBuf,int DataLen,void * ServerAddr); 
    
    
    int main(int argc,char * argv[]) 
    { 
       SOCKET  MainSocket,ClientSocket; 
       struct  sockaddr_in Host,Client; 
       WSADATA WsaData; 
       int  AddLen,i; 
    
       //初始化 
       
       if(WSAStartup(MAKEWORD(2,2),&WsaData) < 0) 
       { 
           printf("初始化失败\n"); 
           return 1; 
       } 
       //创建socket端口 
       MainSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
       if(MainSocket == SOCKET_ERROR) 
       { 
           printf("端口创建错误\n");
    	   return 1;
       } 
       Host.sin_family = AF_INET; 
       Host.sin_port = htons(8080); 
       Host.sin_addr.s_addr = inet_addr("127.0.0.1"); 
       printf("正在工作\n"); 
       //绑定socket
       if(bind(MainSocket,(SOCKADDR *)&Host,sizeof(Host)) != 0) 
       { 
           printf("绑定错误\n");
       } 
       i = 0; 
       //监听 
       if(listen(MainSocket,5) == SOCKET_ERROR) 
       { 
           printf("监听错误\n"); 
       } 
       AddLen = sizeof(Client); 
    
       //连接新的客户 
       i = 0; 
       for(;;) 
       { 
           ClientSocket = accept(MainSocket,(SOCKADDR *)&Client,&AddLen); 
           if(ClientSocket == SOCKET_ERROR) 
           { 
               printf("接受客户请求错误!\n"); 
           } 
           printf("."); 
           i ++ ; 
           if( i >= 1000) 
               i = 0; 
           Global[i] = ClientSocket; 
    
           //对于每一个客户启动不同的线程程进行控制 
           //这个地方在使用ClientSocket的时候,要不要保证在某一时刻内只能有一个进程使用?     
    
    	   CreateThread(NULL,0,Proxy,(LPVOID)Global[i],0,NULL);
    	
            
       } 
    
        
    return 0;
    } 
    DWORD WINAPI Proxy( LPVOID pSocket) 
    { 
       SOCKET ClientSocket; 
       char  ReceiveBuf[MAXBUFLEN]; 
       int  DataLen; 
       struct sockaddr_in  ServerAddr; 
       SOCKET  ProxySocket; 
       int i = 0; 
       int time = TIMEWAIT; 
    
       //得到参数中的端口号信息 
       ClientSocket = (SOCKET)pSocket; 
    //接受第一次请求信息 
       memset(ReceiveBuf,0,MAXBUFLEN); 
       DataLen = recv(ClientSocket,ReceiveBuf,MAXBUFLEN,0); 
    
       if(DataLen == SOCKET_ERROR) 
       { 
           printf("错误\n"); 
           closesocket(ClientSocket); 
          return 0;
       } 
       if(DataLen == 0) 
       { 
           closesocket(ClientSocket); 
          return 0;
       }     
       //处理请求信息,分离出服务器地址 
       if( ParseHttpRequest(ReceiveBuf,DataLen,(void *)&ServerAddr) < 0) 
       { 
           closesocket(ClientSocket); 
           goto error; 
       } 
       //创建新的socket用来和服务器进行连接 
       ProxySocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
       //设置超时时间 
       setsockopt(ProxySocket,SOL_SOCKET,SO_RCVTIMEO,(char *)&time,sizeof(time)); 
       if(ProxySocket == SOCKET_ERROR) 
       { 
           printf("端口创建错误\n"); 
           return 0;
       } 
       if(connect(ProxySocket,(SOCKADDR *)&ServerAddr,sizeof(ServerAddr)) == SOCKET_ERROR) 
       { 
           //printf("连接服务器错误"); 
           goto error; 
       } 
       //开始进行数据传输处理 
       //发送到服务器端 
       if(send(ProxySocket,ReceiveBuf,DataLen,0) == SOCKET_ERROR) 
       { 
           //printf("数据发送错误"); 
           goto error; 
    
       } 
           //从服务器端接受数据 
       while(DataLen > 0) 
       { 
           memset(ReceiveBuf,0,MAXBUFLEN); 
        
           if((DataLen = recv(ProxySocket,ReceiveBuf,MAXBUFLEN,0)) <= 0) 
           { 
               //    printf("数据接受错误"); 
               break; 
                
           } 
           else 
               //发送到客户端 
               if(send(ClientSocket,ReceiveBuf,DataLen,0) < 0) 
               { 
                   //    printf("数据发送错误"); 
                       break; 
               } 
            
       } 
        
    error: 
       closesocket(ClientSocket); 
       closesocket(ProxySocket); 
    
       return 0; 
    
    } 
    int  ParseHttpRequest(char * SourceBuf,int DataLen,void * ServerAddr) 
    { 
    
       char * HttpHead = "http://"; 
       char * FirstLocation = NULL; 
       char * LastLocation = NULL; 
       char * PortLocation = NULL; 
       char  ServerName[HTTPADDLEN]; 
       char  PortString[10]; 
       int   NameLen; 
       struct hostent * pHost; 
       struct sockaddr_in * pServer = (struct sockaddr_in *)ServerAddr; 
       //取得http://的位置 
       FirstLocation = strstr(SourceBuf,HttpHead) + strlen(HttpHead); 
       //取得/的位置 
       printf("%s\n",FirstLocation);
       LastLocation=strstr(FirstLocation,"/"); 
    
       //得到http://和/之间的服务器的名称 
        
       memset(ServerName,0,HTTPADDLEN); 
       memcpy(ServerName,FirstLocation,LastLocation - FirstLocation); 
    
       //有些情况下,请求的地址中带有端口号格式为“:+ 端口号”; 
       //取得 :的位置 
       PortLocation = strstr(ServerName,":"); 
    
        
       //填充server结构 
       pServer->sin_family = AF_INET; 
       //在url中制定了服务器端口 
       if(PortLocation != NULL) 
       { 
           NameLen = PortLocation - ServerName -1; 
           memset(PortString,0,10); 
           memcpy(PortString,PortLocation + 1,NameLen); 
           pServer->sin_port = htons((u_short)atoi(PortString)); 
           *PortLocation = 0;     
       } 
       else//在url中,没有制定服务器端口 
       { 
           pServer->sin_port=htons(80); 
       } 
    
       if(NameLen > HTTPADDLEN) 
       { 
           printf("服务器名字太长\n"); 
           return -1; 
       } 
        
       //得到服务器信息 
       //如果地址信息是以IP地址(192.168.0.1)的形式出现的 
       if(ServerName[0] >= '0' && ServerName[0] <= '9') 
       { 
            
           pServer->sin_addr.s_addr = inet_addr(ServerName); 
       } 
       //以域名的形式出现的(www.sina.com.cn) 
       else 
       { 
           pHost = (struct hostent *)gethostbyname(ServerName); 
           if(!pHost) 
           { 
               printf("取得主机信息错误\n"); 
               printf("%s\n",ServerName); 
               return -1; 
           } 
           memcpy(&pServer->sin_addr,pHost->h_addr_list[0],sizeof(pServer->sin_addr)); 
       } 
        
       return 0; 
    } 


     

  • 相关阅读:
    自定义View的ToolBar布局报错Error:(2) No resource identifier found for attribute 'context' in package 'c
    在学git之主分支 branch
    获取发布版SHA1
    关于开启线程与UI的操作
    播放音频和视频(VideoView控件)
    通知栏Notification的应用
    Android 真机调式 Installation failed with message 远程主机强迫关闭了一个现有的连接。. It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing. WA
    运行程序申请危险权限
    mysql乐观锁总结和实践
    Nginx配置文件nginx.conf中文详解
  • 原文地址:https://www.cnblogs.com/new0801/p/6177792.html
Copyright © 2011-2022 走看看