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

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

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

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

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

```cpp: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](https://qiita-image-store.s3.amazonaws.com/0/118083/e6e97279-2c44-2df5-72dc-cecfe5276d6d.jpeg)

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

```cpp: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](https://qiita-image-store.s3.amazonaws.com/0/118083/0990c0da-a431-e23b-25a1-ba6c7670e7f7.jpeg)

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

```cpp: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;
}

```


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