+ -
当前位置:首页 → 问答吧 → 会C,JavaScript的大侠,编写最简单的交互式Web服务器,求助!

会C,JavaScript的大侠,编写最简单的交互式Web服务器,求助!

时间:2010-08-19

来源:互联网

现在在做Web server ,再看领导给我的C代码,公司领导让我添加页面,我做到了,但是源代码里有很多函数,我搞不明白流程。

是个控制台的程序,我想自己实现一个最简单的Web Server,必须具备点击按钮后,送到服务器,服务器接受到后再送回来,显示在我的页面上。我已经写了个最简单的,但功能不能达到我的要求。

我的程序一运行,第一次可以在浏览器上看到一个页面,但是点击按钮后get发送出去,并没有被服务器正确处理。
recv被阻塞,等待一会后返回 -1,查看WSAGetLastError 说:远程软件关闭连接,看来是 ie 这边关闭了。

失败了。

WSAStartup //大致步骤如下
socket  
bind
listen
accept
while(1)
{
  recv
  send
}
closesocket
closesocket

HTML code

<html>
<head><title></title>
<script type="text/javascript" >
function HTTPGet()
{
  var request=new ActiveXObject("MSXML2.XMLHTTP.3.0");
  //...
}
</script>
<head>
<body>
<input type="textarea" id="textarea1"></textarea>
<input type="textarea" id="textarea2"></textarea>
<input type="button" id="button1" value="get" onclick="HTTPGet();" />
</body>
</html>



get送出去后没反应。纠结,脑子里乱了。

各位大侠前辈们,谁能给个最简单的交互式Web服务器,带CGI的。

链接也可以,最好能麻烦您顺手写一个。

附上我的源代码,您可以在上面修改。

C/C++ code

// Web Server.cpp : 定义控制台应用程序的入口点。
//



#include <winsock2.h>    // 为了使用Winsock API函数
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>

// 告诉连接器与WS2_32库连接
#pragma comment(lib,"WS2_32.lib")

int readFile(char *fileSrc,char **pszText,DWORD *pfileSize)
{
    FILE *fp=NULL;
    char buf[1024+2]={0};
    int size=0,len=0;
    HANDLE hFile=NULL;
    DWORD fileSize=0;
    hFile=::CreateFile(fileSrc,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        printf("open file error.\n");
        return -1;
    }
    fileSize=::GetFileSize(hFile,NULL);
    *pfileSize=fileSize;
    ::CloseHandle(hFile);
    if(fileSize != 0)
    {
        *pszText=(char *)malloc(sizeof(char)*fileSize);
    }
    else
        *pszText="";
    fp=fopen(fileSrc,"rb");
    if(fp == NULL)
    {
        printf("open file error.\n");
        return -1;
    }

    do
    {
        size=fread(buf,sizeof(char),1024,fp);
        if(size > 0)
            len+=sprintf(*pszText+len,"%s",buf);
        memset(buf,0,1024);
    }while(size > 0);
    fclose(fp);
    return 0;
}

void getGMTTime(char *pSystemTime)
{
    SYSTEMTIME st;
    char wkday[16]={0},month[16]={0};
    ::GetSystemTime(&st);
    switch(st.wDayOfWeek)
    {
    case 0:strcpy(wkday,"Sun");break;
    case 1:strcpy(wkday,"Mon");break;
    case 2:strcpy(wkday,"Tue");break;
    case 3:strcpy(wkday,"Wed");break;
    case 4:strcpy(wkday,"Thu");break;
    case 5:strcpy(wkday,"Fri");break;
    case 6:strcpy(wkday,"Sat");break;
    default:break;
    }
    switch(st.wMonth)
    {
    case 1:strcpy(month,"Jan");break;
    case 2:strcpy(month,"Feb");break;
    case 3:strcpy(month,"Mar");break;
    case 4:strcpy(month,"Apr");break;
    case 5:strcpy(month,"May");break;
    case 6:strcpy(month,"Jun");break;
    case 7:strcpy(month,"Jul");break;
    case 8:strcpy(month,"Aug");break;
    case 9:strcpy(month,"Sep");break;
    case 10:strcpy(month,"Oct");break;
    case 11:strcpy(month,"Nov");break;
    case 12:strcpy(month,"Dec");break;
    default:break;
    }
    sprintf(pSystemTime,"%3s, %02d %3s %04d %02d:%02d:%02d GMT",
        wkday,
        st.wDay,
        month,
        st.wYear,
        st.wHour,
        st.wMinute,
        st.wSecond
        );
}
int _tmain(int argc, _TCHAR* argv[])
{
    int size=0,len=0,packet_size=0;
    DWORD fileSize=0;
    char *szText=NULL;
    char system_time[128]={0};
    if(readFile("Myhtml.htm",&szText,&fileSize) == -1)
        return -1;


    // 初始化WS2_32.dll
    WSADATA wsaData;
    WORD sockVersion = MAKEWORD(2, 0);
    ::WSAStartup(sockVersion, &wsaData);

    // 创建套节字
    SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(s == INVALID_SOCKET)
    {
        printf("Failed socket() \n");
        ::WSACleanup();
        return 0;
    }

    // 填充sockaddr_in结构
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8888);
    //sin.sin_addr.S_un.S_addr = INADDR_ANY;
    sin.sin_addr.S_un.S_addr=inet_addr("192.168.102.155");

    // 绑定这个套节字到一个本地地址
    if(::bind(s, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        printf("Failed bind() \n");
        ::WSACleanup();
        return 0;
    }

    // 进入监听模式
    if(::listen(s, 2) == SOCKET_ERROR)
    {
        printf("Failed listen()");
        ::WSACleanup();
        return 0;
    }
    printf("listen  ... \n");
    // 循环接受客户的连接请求
    sockaddr_in remoteAddr; 
    int nAddrLen = sizeof(remoteAddr);
    SOCKET client;
    char HTTPHead[512]={0};
    char recvbuf[1024*4]={0};

    while(TRUE)
    {
        // 接受一个新连接
        client = ::accept(s, (SOCKADDR*)&remoteAddr, &nAddrLen);
        if(client == INVALID_SOCKET)
        {
            printf("Failed accept()");
            continue;
        }

        printf("accept 1 connect:    %s \r\n", inet_ntoa(remoteAddr.sin_addr));
        while(1)
        {
            system("pause");
            int recvSize=::recv(client,recvbuf,sizeof(recvbuf),0);
            if(recvSize== SOCKET_ERROR)
            {
                printf("error=%d\n",::WSAGetLastError());
                system("pause");
            }
            printf("%d\n",recvSize);
            system("pause");
            printf("%s\n",recvbuf);
            getGMTTime(system_time);
            sprintf(HTTPHead,"HTTP/1.1 200 OK\r\nDate: %s\r\nServer: WebServer\r\nContent-Length: %d\r\nContent-Type: text/html;charset=gb2312\r\nCache-Control: no-cache\r\nConnection: Keep-Alive\r\n\r\n",
                system_time,
                strlen(szText));
            ::send(client,HTTPHead,strlen(HTTPHead),0);
            printf("%s\n",HTTPHead);
            // 向客户端发送数据
            //::send(client, szText, strlen(szText), 0);

            len=0;
            size=0;
            packet_size=1024;
            do
            {
                if(fileSize - len > 1024)
                    packet_size = 1024;
                else if(fileSize - len <= 1024)
                    packet_size = fileSize - len;
                size=::send(client,szText+len,packet_size,0);
                len+=size;
            }while(size != SOCKET_ERROR && packet_size != 0);

        }
        printf("data send finished,closed.\n");
        // 关闭同客户端的连接
        ::closesocket(client);
    }

    // 关闭监听套节字
    ::closesocket(s);
    // 释放WS2_32库
    ::WSACleanup();    
    free(szText);
    return 0;
}



作者: LENOVO_   发布时间: 2010-08-19

这是HTTP服务器端吧?说几点看着不合理的地方

1,while(1)是什么意思?有一个客户端请求后,不停地循环接收客户端数据?而且没有用线程,当然阻塞了,这个程序会造成N个客户端请求的话,那么必须一个个的来。socket没有设置接收和发送超时的毫秒数,如果有客户端不自动关闭连接,那么recv会在60秒后超时,这时返回SOCKET_ERROR才能退出while(1),最后才能继续accept下一个客户端请求

建议:
A, accept后,另建线程,将socket传到线程里去完成while(1)包含的内容
B, 取消while(1)语句,recv一次即可,服务器端发送完数据后,关闭连接,退出线程
C, 设置socket接收和发送超时的毫秒数

2,system("pause");是debug用的么?服务器端还要按任意键才能继续操作?
建议:取消system("pause");有printf来调试已经够用了

作者: mocom   发布时间: 2010-08-19

mocom

大侠的建议很好,我会修改的。

我现在的问题不光出在这里,如何处理浏览器发送过来的 GET 请求?再发送些什么数据到浏览器那边去?

作者: LENOVO_   发布时间: 2010-08-20

按照HTTP协议来解析吧,例如访问http://192.168.102.155:8888/root/path/name.html,客户端发送的数据包就是这样一种格式

GET /root/path/name.html HTTP/1.1
Host: 192.168.102.155:8888
Accept-Language: zh-cn, en-us
...其他MIME头
Connection: Close

[如果是POST请求,那么POST的数据包在这里显示,例如:name=abc&passwd=123,CONTENT_TYPE为application/x-www-form-urlencoded的数据包格式与QUERY_STRING一致,multipart/form-data格式参考一些上传程序]

你recv后,需要对HTTP头进行解析,获取请求方式:GET or POST,请求目标:/root/path/name.html,协议版本:HTTP/1.1 or HTTP/1.0,版本的区别不大,不用考虑太多,不是特别的要求,大部分一致,然后就是MIME头,MIME头占一行,格式为:name: value,即name后有个冒号和空格,然后才是MIME内容,HTTP头以\r\n为结束符,不是写太严格的HTTP服务器的话,获取到请求方式和目标即可,假设目标为:/root/path/name.html,那么你必须配置一个站点根目录,例如:D:\www,那么/root/path/name.html对应的目标文件就是:D:\www\root\path\name.html,将其send到客户端链接即可,这就是一个完整的HTTP请求和应答过程,而CGI的支持则是更高的要求了,也不用考虑这么多,只考虑怎么发静态文件

作者: mocom   发布时间: 2010-08-20

mocom

大侠,我发送静态页面到浏览器已经成功了。

我发此贴的目的是想知道如何处理浏览器页面上点击按钮后发送过来的 get请求。

获取第一个文本区的内容然后放到第二个文本区中,当然这完全可以在本地完成,用 javascript 可以完成。

但我想模拟服务器处理过程。

能帮帮忙吗?HTTP 头我会写。就是那个web server 怎么接受和发送数据实现动态交互。

不会每次都是重发一次html 页面吧?

作者: LENOVO_   发布时间: 2010-08-20

获取第一个文本区的内容然后放到第二个文本区中

是什么意思?举例来说明下

作者: mocom   发布时间: 2010-08-20