• windows下的C++ socket服务器(4)


    void handleAccept(int socket_fd)
    {
        char buf[1024] = { '' };
        string cmd;
        string filename;
        recv(socket_fd, buf, sizeof(buf), 0);//1
        stringstream sstream;//2
        sstream << buf;
        sstream >> cmd;
        sstream >> filename;
        cout << cmd << " " << filename << endl;
        if (cmd=="GET")//3
        {
            ifstream file;
            filename = filename.substr(1, filename.length() - 1);
            file.open(filename ,ifstream::binary);//4
            string head = "HTTP/1.0 200 OK
    Content - type:text/plain
    
    ";//5
            if (!file)
            {
                cout << "fail" << endl;
                closesocket(socket_fd);
                return;
            }
            if (filename.find(".html")!=string::npos|| filename.find(".htm")!=string::npos)
            {
                head = "HTTP/1.0 200 OK
    Content - type:text/html
    
    ";
            }        
            if (filename.find(".png")!=string::npos)
            {
                head = "HTTP/1.0 200 OK
    Content - type:image/png
    
    ";    
            }
            if (filename.find(".jpg")!=string::npos)
            {
                head = "HTTP/1.0 200 OK
    Content - type:image/jpg
    
    ";        
            }
            send(socket_fd, head.c_str(), strlen(head.c_str()), 0);
            while (!file.eof())//6
            {    
                char buf[1024];
                memset(buf, 0, sizeof(buf));
                file.read(buf,sizeof(buf)-1);
                int n = file.gcount();
                send(socket_fd, buf,n,0);//1
            }
            file.close();//7
        }
        closesocket(socket_fd);//7
        
    }

    1 recv(socket_fd, buf, sizeof(buf), 0)和send(socket_fd, buf,n,0);

    recv用于接收从客户端发送来的消息,send用于向服务端发送消息

    recv/send函数原型如下

    int recv(SOCKET s,char FAR * buf,int len,int flags)/int send(SOCKET s,const char FAR * buf,int len,int flags);

    第一个参数表示代表对方的socket,

    第二个参数为接收读取的信息的字符串

    第三个参数为该字符串的大小

    第四个参数可以用来控制读写操作

    该值可以为一下几种

    0

    MSG_DONTROUTE:不查找路由表/* send without using routing tables */
    MSG_OOB:接受或发送带外数据 /* process out-of-band data */

    MSG_PEEK:查看数据,并不从系统缓冲区移走数据/* peek at incoming message */
    MSG_WAITALL :等待任何数据/* do not complete until packet is completely filled */

    etc…

    解释:
    MSG_DONTROUTE:是send函数使用的标志.这个标志告诉IP协议.目的主机在本地网络上面,没有必要查找路由表.这个标志一般用网络诊断和路由程式里面。
    MSG_OOB:表示能够接收和发送带外的数据.关于带外数据我们以后会解释的.
    MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容。这样下次读的时候,仍然是相同的内容。一般在有多个进程读写数据时能够使用这个标志。

    MSG_WAITALL:是recv函数的使用标志,表示等到任何的信息到达时才返回。使用这个标志的时候recv会一直阻塞,直到指定的条件满足,或是发生了错误。

        1)当读到了指定的字节时,函数正常返回,返回值等于len

        2)当读到了文档的结尾时,函数正常返回.返回值小于len

        3)当操作发生错误时,返回-1,且配置错误为相应的错误号(errno)
    其他的几个选项,但是我们实际上用的很少.

    关于其他的发送和接收函数

    recvfrom/sendto

    这两个函数一般用在UDP中。

    函数原型如下

    int  recvfrom(SOCKET s,char FAR * buf,int len,int flags,struct sockaddr FAR * from,int FAR * fromlen);

    int  sendto(SOCKET s,const char FAR * buf,int len,int flags,const struct sockaddr FAR * to,int tolen);

    2 stringstream sstream;

    字符串读写流,这里用于将char buf[1024]的数据按默认的间隔符读到cmd和filename中,关于cmd和filename的具体含义见3

    也可以用来将数字转换为字符串

    例如

    stringstream sstream;

    sstream <<123456789;
    string a;
    sstream >> a;
    cout << a << endl;

    将数字123456789转换为了字符串

    3 HTTP协议请求

    当客户端连接到服务端时,会发出一个http请求

    http请求由三部分组成,分别是:请求行、消息报头、请求正文

    这里只对请求行进行介绍

    请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:Method Request-URI HTTP-Version CRLF  

    其中 Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。

    请求方法(所有方法全为大写)有多种,各个方法的解释如下:
    GET     请求获取Request-URI所标识的资源
    POST    在Request-URI所标识的资源后附加新的数据
    HEAD    请求获取由Request-URI所标识的资源的响应消息报头
    PUT     请求服务器存储一个资源,并用Request-URI作为其标识
    DELETE  请求服务器删除Request-URI所标识的资源
    TRACE   请求服务器回送收到的请求信息,主要用于测试或诊断
    CONNECT 保留将来使用
    OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求

    在本程序中只对GET请求进行处理,将请求方法读入cmd中,和Request-URL读入filename中,判断是否为GET,并获请求的资源名称

    4 file.open(filename ,ifstream::binary);

    打开客户所请求的文件,这里使用二进制的方式打开是为了方便对图片进行传输

    5 string head = "HTTP/1.0 200 OK Content - type:text/plain ";

    在接收和解释请求消息后,服务器返回一个HTTP响应消息。

    HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文

    1)、状态行格式如下:
    HTTP-Version Status-Code Reason-Phrase CRLF

    其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。

    常见状态代码、状态描述、说明:

    200 OK      //客户端请求成功
    400 Bad Request  //客户端请求有语法错误,不能被服务器所理解
    401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 
    403 Forbidden  //服务器收到请求,但是拒绝提供服务
    404 Not Found  //请求资源不存在,eg:输入了错误的URL
    500 Internal Server Error //服务器发生不可预期的错误
    503 Server Unavailable  //服务器当前不能处理客户端的请求,一段时间后可能恢复正常

    Content-Type表示正在传输的类型

    Content - type:text/plain:普通文本

    Content - type:text/html:html网页

    Content - type:image/png:png图片

    Content - type:image/jpg:jpg图片

    2)响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request-URI所标识的资源进行下一步访问的信息。在这不做详细介绍

    3)响应正文就是服务器返回的资源的内容

     关于http协议更详细的部分可以参照http://blog.csdn.net/gueter/article/details/1524447 HTTP协议详解

    6 获得响应正文并写到客户端

    char buf[1024];
    memset(buf, 0, sizeof(buf));//初始化

    file.read(buf,sizeof(buf)-1);//由于我们使用二进制的方式打开的文件所以使用了read方法

    int n = file.gcount();//gcount()返回最后一个非格式化的抽取方法读取的字符数,因为有时候读到的会小于1023个

    send(socket_fd, buf,n,0);//将buf中的n个字符发送到客户端

    7

    file.close()和closesocket(socket_fd)

    关闭打开的文件和socket_fd

     程序代码下载:http://files.cnblogs.com/magicsoar/WebServer.rar

    p.s

    程序做的还不是很完善,对一些情况比如send,reve是否正在执行也没有进行检查,客户端请求的文件不存在也没有返回404,

    我会在以后对程序进行完善的,并将一些新的心得写出来。

    第一次写博客,希望大家能指出我的不足来,我会虚心接受并改进的。

    接下来可能会将自己大一大二做的软件,游戏拿出来,写一写,与大家分享,或者是读书的心得等,也可能会是我目前正在学习C++网络爬虫。

  • 相关阅读:
    Hive任务优化--控制hive任务中的map数和reduce数
    qq浏览器如何全屏截图
    清华差生10年奋斗经历
    hive优化之------控制hive任务中的map数和reduce数
    Hive新功能 Cube, Rollup介绍
    Spring自定义类扫描器 ClassPathScanningCandidateComponentProvider Spring生命周期 Constructor > @PostConstruct > InitializingBean > init-method
    Spring生命周期 Constructor > @PostConstruct > InitializingBean > init-method
    ThreadGroup解读
    Java线程池ExecutorService
    @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
  • 原文地址:https://www.cnblogs.com/magicsoar/p/3587351.html
Copyright © 2020-2023  润新知