SylixOS软件开发-移植WEB服务(一)

这次我们需要在SylixOS中移植一款web服务,并且支持标准cgi。于是在网上搜集到了一款非常小巧的web服务-tinyhttpd。
这个单c文件的web服务有多小,源码c文件只有16kb,一共有十几个函数,就实现了http服务,是个学习http服务器原理的最佳入手程序。看一天就能理解了,最难的地方可能就是cgi的执行过程,涉及了管道使用。
话不多说。直接开始移植。
首先,创建APP程序,命名随意,然后将tinyhttpd源码拷贝进入主文件,即可完成编译。因为原有代码基本没有编译错误。
在这里插入图片描述
似乎文章到此就结束了,程序已经能够运行起来,监听4001端口了
在这里插入图片描述

问题1,修改监听IP

程序监听没有问题,但是就是访问不了。最后发现需要手动指定监听IP,修改代码如下
在这里插入图片描述

问题2,页面没有渲染

经过和正常web服务对比,发现是tinyhttpd太过简单导致header返回的类型,不够详细,js和css的返回类型不对,此处还可以根据以后需求进行类型增加。

void headers(int client, const char *filename)
{
    char buf[1024];
    (void)filename;  /* could use filename to determine file type */
	const char *pFile;

	pFile = strrchr(filename, '.'); // 判断输入的文件名最后输出.的位置

 	strcpy(buf, "HTTP/1.0 200 OK\r\n");
    send(client, buf, strlen(buf), 0);
    strcpy(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
	//sunjinxiugai ,zengjia
	
	if (strcmp(pFile, ".css") == 0)
	{ 
    	sprintf(buf, "Content-Type: text/css\r\n");
	}
	else if (strcmp(pFile, ".js") == 0)
	{ 
    	sprintf(buf, "Content-Type: application/x-javascript\r\n");
	}
	else
	{
    	sprintf(buf, "Content-Type: text/html\r\n");
	}
	
    send(client, buf, strlen(buf), 0);
    strcpy(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
}

问题3,大文件加载失败

页面渲染还是有问题,结果发现是代码中,读取发送文件的函数,内存不够大,文件没有发送完整,因为fgets是按照行来的,但是有很多js,css是压缩过的,本身就是一行很长的数据,所以,改为内存读取保证了数据完整。

void cat(int client, FILE *resource)
{

    //char buf[1024];

    //fgets(buf, sizeof(buf), resource);
    //while (!feof(resource))
    //{
    //    send(client, buf, strlen(buf), 0);
    //    fgets(buf, sizeof(buf), resource);
   // }
	int rc;
	unsigned char buf[1024];

	while( (rc = fread(buf,sizeof(unsigned char), 1024,resource)) != 0 )
	{
		send(client, buf, rc, 0);
	} 
   
}

问题4,cgi无法调用

同样的代码,在其他linux系统中测试没有问题。更换到SylixOS下之后,cgi编译也没有问题,但是在请求cgi的时候,并不会执行到cgi程序,调试发现,问题出现在下面这里
在这里插入图片描述
一股不祥的预感,感觉fork函数有问题,查询文档发现,sylixOS不支持fork函数……
在这里插入图片描述

通过网上查找资料,发现了一位大神的文章https://my.oschina.net/u/3022273/blog/832350。借一张图镇楼
在这里插入图片描述
将原有代码中的子进程,拆分出来一个child进程,并且将原有能够直接访问的参数,通过参数传递给child进程,子进程和父进程代码替换如下,参数传递用了傻瓜似的字符串方式,因为发现只能这么传递多个参数,如果只传递管道,还可以用官方文档的方式,不过这里参数太多,实在是不够用。下面是替代父子进程部分。

{
	
		char *cmd[9];
		char resp[1024];
		int rsplen=0;
		
        for (i = 0; i < 8; i++) 
		{
			cmd[i]=malloc(1024);
        }
		cmd[8]=NULL;
		sprintf(cmd[0],"%d",cgi_output[0]);
		sprintf(cmd[1],"%d",cgi_output[1]);
		sprintf(cmd[2],"%d",cgi_input[0]);
		sprintf(cmd[3],"%d",cgi_input[1]);
		strcpy(cmd[4],method);
		strcpy(cmd[5],query_string);
		strcpy(cmd[6],path);
		sprintf(cmd[7],"%d",content_length);

		posix_spawnp(&pid, "./jqr_cgi_child", NULL, NULL, cmd, NULL);
		    
		/* parent */
        close(cgi_output[1]);
        close(cgi_input[0]);
        if (strcasecmp(method, "POST") == 0)
        {
            for (i = 0; i < content_length; i++) 
			{
                recv(client, &c, 1, 0);
                write(cgi_input[1], &c, 1);
            }
        }
        memset(resp,0,1024);
        rsplen=read(cgi_output[0], resp, 1024);
		if(rsplen>=1024)
		{
			rsplen=1023;
		}
		resp[rsplen]='\0';
		send(client, resp, strlen(resp), 0);

        close(cgi_output[0]);
        close(cgi_input[1]);
		Lw_Time_MSleep(100);
		for (i = 0; i < 8; i++) 
		{
			free(cmd[i]);
        }
        waitpid(pid, &status, 0);

    }

child进程代码如下,同样是将参数从argv中拿出来,再次使用。

{

    int cgi_output[2];
    int cgi_input[2];
	
	char method[1024];
	char query_string[1024];
	char path[1024];
	int content_length;
	
	char meth_env[255];
	char query_env[255];
	char length_env[255];
	
	cgi_output[0]=atoi(argv[0]);
	cgi_output[1]=atoi(argv[1]);
	cgi_input[0]=atoi(argv[2]);
	cgi_input[1]=atoi(argv[3]);
	strcpy(method,argv[4]);
	strcpy(query_string,argv[5]);
	sprintf(path,"./%s",argv[6]);
	content_length=	atoi(argv[7]);
	
	dup2(cgi_output[1], STDOUT);
	dup2(cgi_input[0], STDIN);
	close(cgi_output[0]);
	close(cgi_input[1]);
	sprintf(meth_env, "REQUEST_METHOD=%s", method);
	putenv(meth_env);
	if (strcasecmp(method, "GET") == 0) 
	{
		sprintf(query_env, "QUERY_STRING=%s", query_string);
		putenv(query_env);
	}
	else 
	{	/* POST */
		sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
		putenv(length_env);
	}
	execl(path, "main.cgi","test", (char *)0);
	exit(0);

    return  (0);
}

至于cgi程序,可以随便选取一个,c语言的是最好用的,也是各个平台都支持的。下一章具体开发cgi功能。
在这里插入图片描述

最后讲个今天遇到的事情

202196日,周一,早高峰,下雨,要多堵有多堵。
正开着车,接到一个物业电话,
对方:您是尾号xxx的车主吗?
我:是啊
对方:麻烦你挪下车…
我:我这堵车也不是故意的,你得让前面的挪,因为我在高速上。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖哥王老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值