Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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;
}

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした