利用dup dup2函数实现简单的CGI服务器原理

dup函数和dup2函数

dup,dup2都是属于创建文件描述符的函数,所以返回值也是一个文件描述符。有时候我们希望把标准输入重定向到一个文件的时候,或者把标准输出重定向到一个网络连接的时候,就可以通过这两个函数实现。简而言之,这两个函数就是复制文件描述符的。dup(oldfd)执行以后会返回一个新的并且是当前系统可用的最小整数值作为新的文件描述符,此时这个新的文件描述符将指向oldfd指向的同一个文件,共享所有的锁定、读写和各项权限和标志位。dup(oldfd,newfd)的意思就是,将文件描述符newfd指向oldfd(或者说oldfd被复制之后新的文件描述符被设定为newfd),dup2多出来的就是第二个参数可以指定新文件描述符的数值,因此dup2的返回值一定是不小于newfd的整数数值的。

  1. 函数原型(使用man函数手册可见)
  2. 函数意义及其参数意义
  3. 结合标准输出文件、标准输入文件、标准错误输出文件理解

标准输入文件的文件描述符: 0

标准输出文件的文件描述符:1

标准错误输出的文件描述符:2

例1:

int fd1=2; int fd2; fd2 = dup(fd1); //此时新生成的文件描述符fd2 就是文件描述符fd1的拷贝。此时已经把标准错误输出重定向到了文件描述符为fd2的文件中了。

例2:

int fd = open("1.txt" , (O_RWDR|O_CREATE) , 0644); dup2(fd , 1); //此时文件描述符1就是文件描述符fd的拷贝。所以此时文件描述符1将指向文件描述符fd所指向的文件,此时标准输出文件已经重定向到1.txt中了。所以写到stdout中的东西会全部写到文件1.txt中。

 

CGI定义及其简单工作原理

  1. CGI 是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。CGI 应用程序能与浏览器进行交互,还可通过数据库API 与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。简而言之CGI是Web服务器和外部程序之间的一个接口。
  2. 当客户端向web服务器发起一个CGI程序的请求的时候,web服务器就会激活客户端所请求的CGI程序,web服务器就相当于CGI和客户端的一个桥梁,而CGI又相当于外部程序和web服务器的接口标准。

 

                             

参考博客:https://blog.csdn.net/nyist327/article/details/41049699

对CGI也没有太多了解,这次的代码只是对dup ,dup2函数的一个理解例子。。。

 

代码:

CGI.h文件

#ifndef _CGI_H
#define _CGI_H
#include<iostream>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<stdio.h>
using namespace std;
const char *ip = "127.0.0.1";
const int port = 4507;
#endif

CGI_server.cpp文件

#include"CGI.h"
class server
{
private:
    int sock;
public:
    server();
    void create_socket();
};

server::server()
{
    int sock = 0;
}

void server::create_socket()
{
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);
    
    sock = socket(PF_INET, SOCK_STREAM, 0);
    assert(sock >= 0);

    int ret = bind(sock,(struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);

    ret = listen(sock,5);
    assert(ret != -1);

    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    socklen_t client_addrlength = sizeof(client);
    int connfd = accept(sock, (struct sockaddr*)&client, &client_addrlength);
    if(connfd<0)
    {
        cout << "errno is :" << errno << endl; 
    }
    else{
        close(1);//close(STDOUT_FILENO)
        int fd=dup2(connfd,1);
        printf("new filled:\n",fd);//fd会将返回到客户端的socket上
        close(connfd);
    }
    close(sock);
}

int main()
{
    server CGI;
    CGI.create_socket();
    return 0;
}

在代码中,我们首先关闭了文件描述符为1的STDOUT_FILENO.然后复制了socket文件描述符connfd.因为dup总是返回系统中最小的可用文件描述符,所以返回值实际上也是1,也就是之前关闭的标准输出文件描述符的值。这样的话,服务器输出到标准输出的内容(在这里是将dup2返回的新的文件描述符发送到了客户端的socket上,也就是printf的内容会到达客户端的接收缓冲区中)就会直接发送到与客户连接的socket上,因此此处的printf调用的输出将被客户端获取而不是显示在服务器程序的终端上。这就是CGI服务器的基本工作原理。(为了测试客户端的socket是否有数据,多此一举写了一个客户端)

CGI_client.cpp

#include"CGI.h"
class client{
private:
    int sock;
public:
    client();
    void create_client();
};

client::client()
{
    int sock = 0;
}

void client::create_client()
{
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family=AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);
    
    sock = socket(PF_INET, SOCK_STREAM, 0);
    assert(sock >= 0);

    if((connect(sock,(struct sockaddr*)&address, sizeof(address))) != -1)
    {
        char buf[30];
        cout << "connect successful!\n";
        if(recv(sock,buf,sizeof(buf),0) > 0)//测试socket接收缓存上是否接收到"new filefd:fd"
        {
            cout << buf << endl;
        }
        else{
            cout << "the errno:" << errno << endl;
        }
    }
    else{
        cout << "the errno:" << errno <<endl;
    }
    close(sock);
}

int main()
{
    client CGI;
    CGI.create_client();
    return 0;
}

测试截图:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值