TCP/IP网络编程 第二十四章:制作HTTP服务器端

实现简单的Web服务器端

现在开始在HTTP协议的基础上编写Web服务器端。先给出Windows平台下的示例,再给出Linux下的示例。在这里我假设各位都有了有关HTTP的知识,如果不知道HTTP协议的具体内容可以参考的往期博客,有了这些基础就不难分析源代码。因此,除了一些简单的注释外,不再另行说明代码。

实现基于Windows的多线程Web服务器端

Web服务器端采用HTTP协议,即使用IOCP或epoll模型也不会大幅提升性能(当然并不是完全没有)。客户端和服务器端交换1次数据后将立即断开连接,没有足够时间发挥IOCP或epoll的优势。在服务器端和客户端保持较长连接的前提下频繁发送大小不一的消息时(最典型的就是网游服务器端),才能真正发挥出这2种模型的优势。

因此,我通过多线程模型实现Web服务器端。也就是说,客户端每次请求时,都创建1个新线程响应客户端请求。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<winsock2.h>
#include <process.h>

#define BUF_SIZE 2048
#define BUF_SMALL 100

unsigned WINAPI RequestHandler(void* arg);
char* ContentType(char* file);
void SendData(SOCKET sock,char* ct, char* fileName);
void SendErrorMSG(SOCKET sock);
void ErrorHandling(char *message);

int main(int argc, char *argv[]){
    WSADATA wsaData;
    SOCKET hServSock,hClntSock;
    SOCKADDR_IN servAdr, clntAdr;
    HANDLE hThread;
    DWORD dwThreadID;
    int clntAdrSize;

    if(argc!=2){
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }

    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)ErrorHandling("WSAStartup() error!");
 
    hServSock=socket(PF_INET,SOCK_STREAM, 0);
    memset(&servAdr,0, sizeof(servAdr));
    servAdr.sin_family=AF_INET;
    servAdr.sin_addr.s_addr=htonl(INADDR_ANY);
    servAdr.sin_port=htons(atoi(argv[1]));

    if(bind(hServSock,(SOCKADDR*)&servAdr, sizeof(servAdr))==SOCKET_ERROR)
       ErrorHandling("bind() error");
    if(listen(hServSock, 5)==SOCKET_ERROR)ErrorHandling("listen() error");

    /*请求及响应*/
    while(1){
       clntAdrSize=sizeof(clntAdr);
       hClntSock=accept(hServSock,(SOCKADDR*)&clntAdr, &clntAdrSize);
       printf("Connection Request : 
       %s:%d\n",inet_ntoa(clntAdr.sin_addr),ntohs(clntAdr.sin_port));

       hThread=(HANDLE)_beginthreadex(NULL,0,RequestHandler,(void*)hClntSock,0,(unsigned 
       *)&dwThreadID);
    }
    closesocket(hServSock);
    WSACleanup();
    return 0;
}

unsigned WINAPI RequestHandler(void *arg){
    SOCKET hClntSock=(SOCKET)arg;
    char buf[BUF_SIZE];
    char method[BUF_SMALL];
    char ct[BUF_SMALL];
    char fileName[BUF_SMALL];

    recv(hclntsock, buf, BUF_SIZE, 0);

    if(strstr(buf,"HTTP/")==NULL){//查看是否是HTTP提出的请求
       SendErrorMSG(hClntSock);
       closesocket(hClntSock);
       return 1;
    }

    strcpy(method,strtok(buf,"/"));
    if(strcmp(method,"GET"))//我这里只实现了GET的请求方式
       SendErrorMSG(hClntSock);

    strcpy(fileName,strtok(NULL,”/“)); //查看请求文件名
    strcpy(ct,ContentType(fileName));   //查看Content-type
    SendData(hClntSock, ct, fileName);  //响应
    return 0;
}

void SendData(SOCKET sock, char* ct, char* fileName){
    char protocol[]="HTTP/1.0 200 OK\r\n";
    char servName[]="Server:simple web server\r\n";
    char cntLen[]="Content-length:2048\r\n";
    char cntType[BUF_SMALL];
    char buf[BUF_SIZE];
    FILE* sendFile;

    sprintf(cntType,"Content-type:%s\r\n\r\n",ct);
    if((sendFile=fopen(fileName,"r"))==NULL){
       SendErrorMSG(sock);
       return;
    }

    /*传输头信息*/
    send(sock, protocol, strlen(protocol),0);
    send(sock, servName, strlen(servName),0);
    send(sock, cntLen,strlen(cntLen),0);
    send(sock, cntType, strlen(cntType), 0);

    /*传输请求数据*/
    while(fgets(buf, BUF_SIZE, sendFile)!=NULL)
       send(sock,buf, strlen(buf),0);
    closesocket(sock);//由HTTP协议响应后断开
}

void SendErrorMSG(SOCKET sock){//发生错误时传递消息
    char protocol[]="HTTP/1.0 400 Bad Request\r\n";
    char servName[]="Server:simple web server\r\n";
    char cntLen[]="Content-length:2048\r\n";
    char cntType[]="Content-type:text/html\r\n\r\n";
    char content[]="<html><head><title>NETWORK</title></head>"
    "<body><font size=+5><br>发生错误!查看请求文件名和请求方式!"
    "</font></body></html>";
         
    send(sock, protocol, strlen(protocol),0);
    send(sock,servName,strlen(servName),0);
    send(sock, cntLen, strlen(cntLen),0);
    send(sock, cntType,strlen(cntType),0);
    send(sock, content,strlen(content),0);
    closesocket(sock);
}

char* ContentType(char* file){ //区分Content-type
    char extension[BUF_SMALL];
    char fileName[BUF_SMALL];
    strcpy(fileName, file);
    strtok(fileName,".");
    strcpy(extension, strtok(NULL,"."));
    if(!strcmp(extension, "html")||!strcmp(extension,"htm"))
       return "text/html";
    else
       return "text/plain";
}

void ErrorHandling(char* message){
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

大家可以通过浏览器来访问一下自己的HTTP服务器,尝试一下结果。

实现基于 Linux的多线程Web服务器端

Linux下的Web服务器端与上述示例不同,将使用标准I/O函数。在此列出的目的主要是为了
让各位多复习各种知识点,没有任何特殊含义。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include <pthread.h>

#define BUF_SIZE 1024
#define SMALL_BUF 100

void* request_handler(void* arg);
void send_data(FILE* fp, char* ct, char* file_name);
char* content_type(char* file);
void send_error(FILE* fp);
void error_handling(char* message);

int main(int argc, char *argv[]){
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, clnt_adr;
    int clnt_adr_size;
    char buf[BUF_SIZE];
    pthread_t t_id;

    if(argc!=2){
       printf("Usage :%s <port>\n", argv[0]);
       exit(1);
    }

    serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    if(bind(serv_sock,(struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
       error_handling("bind() error");
    if(listen(serv_sock,20)==-1)
       error_handling("listen() error");

    while(1){
       clnt_adr_size=sizeof(clnt_adr);
       clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_size);

       printf("Connection Request : 
       %s:%d\n",inet_ntoa(clnt_adr.sin_addr),ntohs(clnt_adr.sin_port));

       pthread_create(&t_id, NULL, request_handler, &clnt_sock);
       pthread_detach(t_id);
    }
    close(serv_sock);
    return 0;
}

void* request_handler(void *arg){
    int clnt_sock=*((int*)arg);
    char req_line[SMALL_BUF];
    FILE* clnt_read;
    FILE* clnt_write;

    char method[10];
    char ct[15];
    char file _name[30];

    clnt_read=fdopen(clnt_sock,"r");
    clnt_write=fdopen(dup(clnt_sock),"w");

    fgets(reg_line, SMALL_BUF, clnt_read);
    if(strstr(req_line, "HTTP/")==NULL){
       send_error(clnt_write);
       fclose(clnt_read);
       fclose(clnt_write);
       return;
    }
    
    strcpy(method, strtok(req_line,"/"));
    if(strcmp(method,"GET")!=0){//判断是否为实现的GEI方法,如果不是则结束连接
       send_error(clnt_write);
       fclose(clnt_read);
       fclose(clnt_write);
       return;
    }

    strcpy(file_name, strtok(NULL," /"));
    strcpy(ct,content_type(file_name));

    fclose(clnt_read);//完成读,半关闭
    send_data(clnt_write, ct, file_name);
}

void send_data(FILE* fp, char* ct, char* file _name){
    char protocol[]="HTTP/1.0 200 oK\r\n";
    char server[]="Server:Linux web Server \r\n";
    char cnt_len[]="Content-length:2048\r\n";
    char cnt_type[SMALL_BUF];
    char buf[BUF_SIZE];
    FILE* send_file;

    sprintf(cnt_type, "Content-type:%s\r\n\r\n",ct);
    send_file=fopen(file_name,"r");

    if(send_file==NULL){
       send_error(fp);
       return;
    }

    /*传输头信息*/
    fputs(protocol, fp);
    fputs(server,fp);
    fputs(cnt_len, fp);
    fputs(cnt_type, fp);

    /*传输请求数据*/
    while(fgets(buf, BUF_SIZE,send_file)!=NULL){
       fputs(buf,fp);
       fflush(fp);
    }

    fflush(fp);
    fclose(fp);
}

char* content_type(char* file){
    char extension[SMALL_BUF];
    char file_name[SMALL_BUF];
    strcpy(file_name, file);
    strtok(file_name,".");
    strcpy(extension,strtok(NULL,"."));
    if(!strcmp(extension,"html")||!strcmp(extension,"htm"))
       return "text/html";
    else
       return"text/plain";
}
       
void send_error(FILE* fp){
    char protocol[]="HTTP/1.0 400 Bad Request\r\n";
    char server[]="Server:Linux Web Server Ir\n";
    char cnt_len[]="Content-length:2048\r\n";
    char cnt_type[]="Content-type:text/html\r\n\r\n";
    char content[]="<html><head><title>NETWORK</title></head>"
    "<body><font size=+5><br>发生错误!查看请求文件名和请求方式!"
    "</font></body></html>";

    fputs(protocol,fp);
    fputs(server,fp);
    fputs(cnt_len, fp);
    fputs(cnt_type,fp);
    fflush(fp);
}

void error_handling(char* message){
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

以上就是网络编程的所有知识,大家也可以选择继续深入学习。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Reol520

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

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

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

打赏作者

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

抵扣说明:

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

余额充值