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;
}
リクエストの内容を解析して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;
}
リクエストにて要求されているファイルを返す。
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;
}
上手く動くと達成感が有ります。