LoginSignup
3
3

More than 5 years have passed since last update.

簡単なHTTPサーバーをSDL_netで作る1

Last updated at Posted at 2016-06-13

SDLはクロスプラットフォームなゲーム製作ライブラリです。
SDL_netはSDLに付いているソケット通信ライブラリです。

私はこのライブラリでHTTPサーバーを製作中です。
徐々に細部を作り上げて完成した順にここに記載します。

これらのプログラムをコンパイルして起動した後にブラウザを開き
http://localhost/
にアクセスして動作を確認できます。
場合によっては対策ソフトがウイルスと勘違いします。
無視してください。

リクエストが来たらsuccessと返す。

main.cpp
#include<stdio.h>
#include<SDL_net.h>
//####注意####下記3つのライブラリをリンク。
//####注意####本物のmain関数はSDL2main.libに入っている。
//#pragma comment(lib,"SDL2_net.lib")
//#pragma comment(lib,"SDL2main.lib")
//#pragma comment(lib,"SDL2.lib")
int main(int argc, char *argv[]){
    IPaddress ip;
    TCPsocket accepting,accepted;
//####注意####ヘッダと中身は一行空ける。
    const   char    bufsend[]=
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: bytes\n"
        "Content-Length: 42\n"
        "Connection: Keep-Alive\n"
        "Content-Type: text/html\n"
        "\n"
        "<html><body><p>success</p></body></html>\n";
    if(!SDLNet_Init()){
        if(!SDLNet_ResolveHost(&ip,NULL,80)){
            if(accepting=SDLNet_TCP_Open(&ip)){
//####注意####accepted!=acceptingであり。acceptedを使う。
//####注意####SDLNet_TCP_Acceptは何度も呼び出すべし。
                while((accepted=SDLNet_TCP_Accept(accepting))==NULL){
                    SDL_Delay(100);
                }
                SDLNet_TCP_Close(accepting);
                while(true){
                    char    bufrecv[256];
                    memset(bufrecv,0,sizeof(bufrecv));
                    if(SDLNet_TCP_Recv(accepted,bufrecv,sizeof(bufrecv))){
                        for(int i=0;i<sizeof(bufrecv);i++){
                            if(bufrecv[i]){
                                putchar(bufrecv[i]);
                            }else{
                                SDLNet_TCP_Send(accepted,bufsend,sizeof(bufsend));
                            }
                        }
                    }else{
                        break;
                    }
                }
                SDLNet_TCP_Close(accepted);
            }
        }
        SDLNet_Quit();
    }
    return  0;
}

下成功例
01.JPG

リクエストの内容を解析してsuccessと返す。

リクエストの種類。
求められているファイル。
接続を継続するか否か。
情報を圧縮して通信できるか。
上記を取得します。

main.cpp
//####注意####下記3つのライブラリをリンク。
//####注意####本物のmain関数はSDL2main.libに入っている。
//#pragma comment(lib,"SDL2_net.lib")
//#pragma comment(lib,"SDL2main.lib")
//#pragma comment(lib,"SDL2.lib")

#include<stdio.h>
#include<SDL_net.h>
#include<string>
#pragma comment(lib,"SDL2_net.lib")
#pragma comment(lib,"SDL2main.lib")
#pragma comment(lib,"SDL2.lib")
using   namespace   std;

//####関数####
//区切り文字pまで受信する。
//input
//  t:受信先文字列
//  s:受信用ソケット
//  p:区切り文字(末端\0も判定対象)
//output
//  区切られた文字

char    recv(string&t,TCPsocket s,char*p="\n"){
    t.clear();
    while(true){
        char    c;
        SDLNet_TCP_Recv(s,&c,1);
        for(int i=0;true;i++){
            if(c==p[i])return   c;
            if(!p[i])break;
        }
        //無視文字
        switch(c){
            case    '\r':
                break;
            default:
                t+=c;
                break;
        }
    }
    return  0;
}

int main(int argc, char *argv[]){
    IPaddress ip;
    TCPsocket accepting,accepted;
    //####注意####ヘッダと中身は一行空ける。
    const   char    bufsend[]=
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: bytes\n"
        "Content-Length: 42\n"
        "Connection: Keep-Alive\n"
        "Content-Type: text/html\n"
        "\n"
        "<html><body><p>success</p></body></html>\n";
    if(!SDLNet_Init()){
        if(!SDLNet_ResolveHost(&ip,NULL,80)){
            if(accepting=SDLNet_TCP_Open(&ip)){
                //####注意####accepted!=acceptingであり。acceptedを使う。
                //####注意####SDLNet_TCP_Acceptは何度も呼び出すべし。
                while((accepted=SDLNet_TCP_Accept(accepting))==NULL){
                    SDL_Delay(100);
                }
                SDLNet_TCP_Close(accepting);
                string  t;
                recv(t,accepted," ");
                if(t=="GET"){
                    printf("type get\n");
                }
                recv(t,accepted," ");
                printf("request %s\n",t.c_str());
                recv(t,accepted);
                printf("rest %s\n",t.c_str());
                while(true){//一行毎に回す
                    recv(t,accepted," \n");
                    if(t==""){
                        break;//最後の行。
                    }else   if(t=="Connection:"){
                        recv(t,accepted);
                        printf("connection is %s\n",t.c_str());
                    }else   if(t=="Accept-Encoding:"){
                        while(true){
                            if(recv(t,accepted,", \n")=='\n'){
                                break;
                            }else   if(t=="gzip"){
                                printf("it can use gzip\n");
                            }
                        }
                    }else{
                        recv(t,accepted);
                    }
                }
                SDLNet_TCP_Send(accepted,bufsend,sizeof(bufsend));
                SDLNet_TCP_Close(accepted);
            }
        }
        SDLNet_Quit();
    }
    return  0;
}

01.JPG

リクエストにて要求されているファイルを返す。

main.cpp

#include<stdio.h>
#include<SDL_net.h>
#include<string>
#include<vector>
#include<algorithm>
//#pragma comment(lib,"SDL2_net.lib")
//#pragma comment(lib,"SDL2main.lib")
//#pragma comment(lib,"SDL2.lib")
using   namespace   std;

//####関数####
//区切り文字pまで受信する。
//input
//  t:受信先文字列
//  s:受信用ソケット
//  p:区切り文字(末端\0も判定対象)
//output
//  区切られた文字

char    recv(string&t,TCPsocket s,char*p="\n",char*m="\r"){
    t.clear();
    while(true){
        char    c;
        if(SDLNet_TCP_Recv(s,&c,1)>0){
            for(int i=0;true;i++){
                if(c==p[i])return   c;
                if(!p[i])break;
            }
            for(int i=0;true;i++){
                if(c==m[i]){break;}
                if(!m[i]){t+=c;break;}
            }
        }else{
            return  -1;
        }
    }
    return  0;
}
//####関数####
//拡張子からMINEタイプを返す
//input
//  url:ファイル名
//output
//  mineタイプ名
string  mine(string url){
    string  e=url.substr(url.find_last_of(".")+1);
    transform(e.begin(),e.end(),e.begin(),tolower);
    #define mine(a,b)   if(e==a)return  b;
    mine("txt","text/plain");
    mine("htm","text/html");
    mine("html","text/html");
    mine("xml","text/xml");
    mine("js","text/javascript");
    mine("css","text/css");
    mine("gif","image/gif");
    mine("jpg","image/jpeg");
    mine("jpg","image/jpg");
    #undef  mine
    return  "text/plain";
}
int main(int argc, char *argv[]){
    IPaddress ip;
    TCPsocket accepting,accepted;
    //####注意####ヘッダと中身は一行空ける。
    const   char    bufsend[]=
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: bytes\n"
        "Content-Length: 42\n"
        "Connection: Keep-Alive\n"
        "Content-Type: text/html\n"
        "\n"
        "<html><body><p>success</p></body></html>\n";
    if(!SDLNet_Init()){
        if(!SDLNet_ResolveHost(&ip,NULL,80)){
            if(accepting=SDLNet_TCP_Open(&ip)){
                //####注意####accepted!=acceptingであり。acceptedを使う。
                //####注意####SDLNet_TCP_Acceptは何度も呼び出すべし。
                while(true){
                    while((accepted=SDLNet_TCP_Accept(accepting))==NULL){
                        SDL_Delay(100);
                    }
                    bool    keep,gzip;
                    vector<string>value;
                    string  t;
                    //get
                    if(recv(t,accepted," ")){
                        //value
                        value.clear();
                        while(true){
                            char    c=recv(t,accepted,"? ");
                            value.push_back(t);
                            if(c!='?'){
                                break;
                            }
                        }
                        recv(t,accepted);
                        while(true){//一行毎に回す
                            recv(t,accepted," \n");
                            if(t==""){
                                break;//最後の行。
                            }else   if(t=="Connection:"){
                                recv(t,accepted);
                                keep=(t=="keep-alive")||(t=="Keep-Alive");
                            }else   if(t=="Accept-Encoding:"){
                                gzip=false;
                                while(true){
                                    if(recv(t,accepted,", \n")=='\n'){
                                        break;
                                    }else   if(t=="gzip"){
                                        gzip=true;
                                    }
                                }
                            }else{
                                recv(t,accepted);
                            }
                        }
                        FILE*f;
                        if(fopen_s(&f,value[0].c_str()+1,"rb")){
                            char    t[]="HTTP/1.1 404 not found\nAccept-Ranges: bytes\nContent-Length: 42\nConnection: Close\nContent-Type: text/html\n\n<html><body><p>failed.</p></body></html>\n";
                            SDLNet_TCP_Send(accepted,t,sizeof(t));
                        }else{
                            char    t[512];
                            fseek(f,0,SEEK_END);
                            SDLNet_TCP_Send(accepted,t,sprintf_s(t,sizeof(t),"HTTP/1.1 200 OK\nAccept-Ranges: bytes\nContent-Length: %d\nConnection: Close\nContent-Type: %s\n\n",(int)ftell(f),mine(value[0]).c_str()));
                            fseek(f,0,SEEK_SET);
                            while(!feof(f))SDLNet_TCP_Send(accepted,t,fread(t,1,sizeof(t),f));
                        }
                    }else{
                        printf("close");
                    }
                    SDLNet_TCP_Close(accepted);
                }
                SDLNet_TCP_Close(accepting);
            }
        }
        SDLNet_Quit();
    }
    return  0;
}

上手く動くと達成感が有ります。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3