機能している最低限のIRCdを記述する


8

少し変わったものですが、ねえ、なぜですか?:)

目的:最小限の文字で最小限の機能を提供する、選択した言語で機能するIRCデーモンを記述します。以下の基準を満たしている限り、IRC RFCに完全に準拠する必要はありません(これにより、課題の面白さが大幅に減少します)。

要件:

  • クライアントは、ポート6667に接続して使用できる必要があります。少なくともirssiとXChatは、デフォルトの構成で正常に接続できる必要があります。
  • ユーザーは、自分のニックネームを指定し、すでに接続しているときにそれを変更し、チャンネルに参加し、チャンネルを離れ、正常に終了できる必要があります(つまりQUIT)。
  • チャネルは通常のように作成する必要があります-ユーザーがいないチャネルに参加すると、「作成」されます。彼らは永続的である必要はありません。
  • ユーザーは、チャネルメッセージとプライベートメッセージの両方を(つまり、他のユーザーに)送信できる必要があります。
  • WHOISこのコマンドは、実装されなければならないだけでなく、PING/ PONG、LIST、およびNAMES(ほとんどが幸せな顧客を維持するために)。

制限:

  • サードパーティのライブラリ(非コアイベントI / Oライブラリを含む)を使用することはできません。使用するプラットフォームに付属する標準ライブラリのみが許可されています。
  • 実装に標準ライブラリのIRCの明示的なサポートが含まれている場合、それも使用できません。もちろん、標準ライブラリのネットワーク機能は問題ありません。
  • 提出物は独立して実行できる必要があります。mIRCスクリプティングでは賢くありません:)

モード、キックなどを実装する必要はありません(上記のクライアントで動作させるために必要でない限り)。SSLも必要ありません。上記の最小限の機能で、チャレンジを短く楽しいものにしてください。

IRCが何であるかの詳細についてここでは、RFCは1459年と2812年(私が原因評判の私の不足のためにそれらに直接リンクすることはできません)です。機能的で要件に準拠した最短の提出が優先されます!


2
IRCに慣れていない人のために、IRCd(またはIRC)が何であるかについての背景を説明したいと思うかもしれません。
— マーティンエンダー2014

2
どれだけの時間がかかり、どのくらいのコードが関与するかを理解するために自分で書いたことがありますか?ゴルフではないサンプルコードは、これがたまたま空いている時間の長さに対してこれが適切なサイズの質問であるかどうかを推定するのに役立ちます。
— trichoplax 14

@MartinBüttner投稿を編集しました。RFCに直接リンクするには十分な評判がありませんが、RFC番号があれば、見つけるのは難しくありません。
— Sven Slootweg、2014

@githubphagocyteああ、私は以前自分で書いてみたことがあります(それは何よりも実験的なものですが)。動的言語(Python、Node.jsなど)の経験豊富な開発者は、このような基本的な機能を備えたIRCdを、ゴルフではない形式で最大1〜2時間で組み立てることができると思います。既にRFCに精通している場合は、おそらく少なくなります。
— Sven Slootweg 14

サーバーはどのポートをリッスンする必要がありますか?6667、194または他の何か?
— nyuszika7h 2014

回答:


3

C ++(部分的にゴルフ)5655(CRLFカウント1)

これはVS 2013でコンパイルされます(auto、lamdas、およびwinsockを使用)。ゴルフを始める前は機能しているようでした。それが非常に大きい理由の1つは、返す数値応答にRFCで指定されたテキストが含まれていることです。これが必要かどうかはわかりません。ポータブルで実行するため、私はKVircでテストしました(PCにソフトウェアをインストールすることはできません!)KVircは私のサーバーで動作するようですが、他のクライアントについては知りません過小評価されているので、うまくいけば私はそれを正しく理解しました。

サーバーは、DIE、KILL、NICK、USER、MODE、WHOIS、WHO、JOIN、PART、TOPIC、LIST、NAMES、PRIVMSG、USERS、PING、PONG、QUITをさまざまな程度で処理します。それらのほとんどについて、指定されたエラー応答を返すために必要なほとんどのチェックを行うことを含め、必要な応答を返します。それらのいくつかのために私はごまかします:

  • USERSは常に446を返します "ユーザーは無効になっています"
  • チャネルMODEメッセージは常に477「チャネルはモードをサポートしていません」を返します
  • ユーザーMODEメッセージは適切に機能しますが、フラグは他のコマンドでは使用されません

私はゴルフがあまり得意ではないので、部分的にゴルフをしていると思います。大きなものを見つけたら、答えを編集して修正してください。

こちらがゴルフ版です

#include<time.h>
#include<map>
#include<list>
#include<vector>
#include<string>
#include<sstream>
#include<iostream>
#include<algorithm>
#include<winSock2.h>
#pragma comment(lib,"ws2_32.lib")
#define P m.p[0]
#define Q m.p[1]
#define E SOCKET_ERROR
#define I INVALID_SOCKET
#define T c_str()
#define H second
#define Y first
#define S string
#define W stringstream
#define G else
#define J G if
#define A auto
#define Z bool
#define B empty()
#define K return
#define N 513
#define X(n,x)if(x){r=n;goto f;};
#define U(x,i)for(A i=x.begin();i !=x.end();++i)
#define L(x)U(x,i)
#define V(x)for(A i=x.begin();i!=--x.end();++i)
#define M(x)FD_ZERO(&x);FD_SET(t.s,&x);L(l){FD_SET(i->s,&x);}
#define R(a,b,...){M v={a,b,{__VA_ARGS__}};w(d,v);}
#define F(x)}J(!_stricmp(m.c.T,x)){
using namespace std;struct C{S t;list<S>n;};struct M{S f;S c;vector<S>p;};struct D{SOCKET s;SOCKADDR_IN a;int m,l,i;char b[N];S n,u,h,r;time_t p,q;};map<S,C>c;list<D>l;void w(D d,M m);void x(D&t,S r,Z n){L(c)i->H.n.remove(t.n);L(l){A d=*i;if(d.n!=t.n)R(d.n,"QUIT",t.n,r)J(n)R("","ERROR","QUIT",r)}closesocket(t.s);t.s=I;}void w(D d,M m){S s=(!m.p.B?":"+m.f+" ":"")+m.c;V(m.p)s+=" "+*i;s+=" :"+*m.p.rbegin()+"\r\n";int c=0;do{int b=send(d.s,s.T+c,s.size()-c,0);if(b>0)c+=b;G x(d,"send error",0);}while(s.size()-c>0);}Z e(D&d,M m){A z=m.p.size();if(!_stricmp(m.c.T,"DIE")){K 1;F("KILL")if(z<1)R("","461",d.n,"USER","Not enough parameters")G{Z f=0;L(l)if(i->n==P){f=1;x((*i),P,1);}if(f==0)R("","401",d.n,P,"No such nick/channel")}F("NICK")if(z<1)R("","431",d.n,"No nickname given")G{Z f=0;L(l)if(i->n==P)f=1;if(f==1)R("","433",d.n,"Nickname is already in use")G d.n=P;}F("USER")if(z<4)R("","461",d.n,"USER","Not enough parameters")G{Z f=0;L(l)if(i->u==P)f=1;if(f==1)R("","462",d.n,"Unauthorized channel (already registered)")G{d.u=P;d.m=atoi(Q.T);d.h=m.p[2];d.r=m.p[3];R("","001",d.n,"Welcome to the Internet Relay Network "+d.n+"!"+d.u+"@"+d.h)}}F("MODE")if(z<1)R("","461",d.n,"MODE","Not enough parameters")J(P==d.n){if(z<2)R("","221",d.n,S("")+(d.m&2?"+w":"-w")+(d.m&3?"+i":"-i"))G{A x=(147-Q[1])/14;if(Q[0]=='+'){d.m|=1<<x;}G{d.m&=~(1<<x);}}}G R("","477",d.n,P,"Channel doesn't support modes")F("WHOIS")if(z<1)R("","431",d.n,"No nickname given")G{Z f=0;L(l)if(i->n==P){f=1;R("","311",d.n,(i->n,i->u,i->h,"*",i->r))}if(f==1)R("","318",d.n,P,"End of WHOIS")G R("","401",d.n,P,"No such nick/channel")}F("WHO")L(c[P].n)U(l,j)if(*i==j->n)R("","352",d.n,P,j->u,j->h,"*",j->n,"",j->r)R("","315",d.n,P,"End of WHO")F("JOIN")if(z<1)R("","461",d.n,"JOIN","Not enough parameters")J(P=="0")L(c){U(i->H.n,j)if(*j==d.n)R("","PART",i->Y,d.n)i->H.n.remove(d.n);}G{A&C=c[P];Z f=0;L(C.n)if(*i==d.n){f=1;}if(f==0){C.n.push_back(d.n);R(d.n,"JOIN",P)if(C.t.B)R("","331",d.n,P,"No topic is set")G R("","332",d.n,P,C.t)S q;L(C.n)q+=(q.B?"":" ")+*i;R("","353",d.n,"=",P,q)R("","366",d.n,P,"End of NAMES")}}F("PART")if(z<1)R("","461",d.n,"PART","Not enough parameters")G{Z f=0;A&C=c[P];L(C.n)if(*i==d.n)f=1;C.n.remove(d.n);if(f){if(z<2)m.p.push_back(d.n);R(d.n,"PART",P,Q)}G R("","442",d.n,P,"You're not on that channel")}F("TOPIC")if(z<1)R("","461",d.n,"TOPIC","Not enough parameters")G{A&C=c[P];if(z<2){C.t="";R("","331",d.n,P,"No topic is set")}G{C.t=Q;R("","332",d.n,P,C.t)}}F("LIST")if(z<1){L(c){W ss;ss<<i->H.n.size();R("","322",d.n,i->Y,ss.str(),i->H.t.B?"No topic is set":i->H.t)}R("","323",d.n,"End of LIST")}G{W ss;ss<<c[P].n.size();R("","322",d.n,P,ss.str(),c[P].t.B?"No topic is set":c[P].t)R("","323",d.n,"End of LIST")}F("NAMES")if(z<1){L(c){S q;U(i->H.n,j)q+=(q.B?"":" ")+*j;R("","353",d.n,"=",i->Y,q)}R("","366",d.n,"End of NAMES")}G{S q;L(c[P].n)q+=(q.B?"":" ")+*i;R("","353",d.n,"=",P,q)R("","366",d.n,P,"End of NAMES")}F("PRIVMSG")if(z<1)R("","411",d.n,"No recipient given(PRIVMSG)")J(z<2)R("","412",d.n,"No text to send")G{Z f=0;A from=d.n;L(c)if(i->Y==P){f=1;U(i->H.n,k)U(l,j)if(*k==j->n){A d=*j;R(from,"PRIVMSG",d.n,Q)}}if(f==0)L(l)if(i->n==P){f=1;A d=*i;R(from,"PRIVMSG",d.n,Q)}if(f==0)R("","401",d.n,P,"No such nick/channel")}F("USERS")R("","446",d.n,"USERS has been disabled")F("PING")R("","PONG",P,Q)F("PONG")d.p=time(NULL)+60;d.q=0;F("QUIT")if(!z)m.p.push_back(d.n);x(d,P,1);}G{R("","421",d.n,m.c,"Unknown command")}K 0;}M g(char*d){M m;char*n=d;while(*d!='\0'){if(m.c.B){if(*d==':'){for(;*d!='\0'&&*d!=' ';++d);*d='\0';m.f=n+1;n=++d;}for(;*d!='\0'&&*d!=' ';++d);*d='\0';m.c=n;n=++d;}J(*d==':'){for(;*d!='\0';++d);m.p.push_back(n+1);n=++d;}G{for(;*d!='\0'&&*d!=' ';++d);*d='\0';m.p.push_back(n);n=++d;}}K m;}int main(){int r;WSADATA u;SOCKADDR_IN la;la.sin_family=AF_INET;la.sin_port=htons(6667);la.sin_addr.s_addr=htonl(INADDR_ANY);timeval h;h.tv_sec=0;h.tv_usec=10000;fd_set rs,ws,es;D t;t.n="IRCd";X(1,(0!=WSAStartup(MAKEWORD(2,2),&u)))X(2,(I==(t.s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))))X(3,(E==bind(t.s,(SOCKADDR*)&la,sizeof(la))))X(4,(E==listen(t.s,SOMAXCONN)))while(1){M(rs)M(ws)M(es)X(5,(E==select(0,&rs,&ws,&es,&h)))X(6,(FD_ISSET(t.s,&es)))if(FD_ISSET(t.s,&rs)){D d={};d.l=sizeof(d.a);d.s=accept(t.s,(SOCKADDR*)&d.a,&d.l);X(7,(I==d.s))W s;s<<inet_ntoa(d.a.sin_addr)<<":"<<ntohs(d.a.sin_port);d.n=s.str();d.p=time(NULL)+60;d.q=0;l.push_back(d);}L(l){D&d=*i;if(d.p>0&&time(NULL)>d.p){R("","PING",d.n)d.p=0;d.q=time(NULL)+60;}if(d.q>0&&time(NULL)>d.q)x(d,"PONG",1);if(FD_ISSET(d.s,&es))x(d,"select except",0);if(FD_ISSET(d.s,&rs)){int b=recv(d.s,d.b+d.i,sizeof(d.b)-d.i-1>0,0);if(b>0)d.i+=b;G x(d,"recv error",0);char*y=d.b+d.i-2;if(!strcmp(y,"\r\n")){*y++='\0';*y='\0';M m=g(d.b);memset(d.b,0,N);d.i=0;if(d.p>0&&time(NULL)<d.p){d.p=time(NULL)+60;d.q=0;}if(e(d,m))X(0,1)}}}l.remove_if([](const D&d){K d.s==I;});}r=0;f:L(l)x(*i,"exit",0);x(t,"exit",0);WSACleanup();K r;}

これはほとんどゴルフされていないバージョンです(まだいくつかのマクロを使用しています):

#include <time.h>
#include <map>
#include <list>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <winSock2.h>
#pragma comment(lib, "ws2_32.lib")
#define READ_BUFFER_SIZE 513
#define EXIT_IF(n,x) if (x) { retval=n; goto finished; };
#define LOOPX(x,it) for (auto it = x.begin(); it != x.end(); ++it)
#define LOOP(x) LOOPX(x,it)
#define LOOP2(x) for (auto it = x.begin(); it != --x.end(); ++it)
#define MAKE_SET(x) FD_ZERO(&x); FD_SET(listener.socket, &x); LOOP(socket_list) { FD_SET(it->socket, &x); }
#define RESPOND(a, b, ...) { message response = {a, b, {__VA_ARGS__}}; tell(data, response); }
#define CASE(x) } else if (!_stricmp(msg.command.c_str(),x)) { std::cout << "Received " << x << " from " << data.nickname << std::endl;
struct channel { std::string topic;  std::list<std::string> nicknames; };
struct message { std::string prefix; std::string command; std::vector<std::string> params; };
struct socket_data { SOCKET socket; SOCKADDR_IN address; int mode,address_length,read_buffer_index; char read_buffer[READ_BUFFER_SIZE]; std::string nickname,username,servername,realname; time_t ping_timer,pong_timer; };
std::map<std::string,channel> channels;
std::list<socket_data> socket_list;
void tell(socket_data data, message msg);
void disconnect(socket_data& target, std::string reason, bool notify)
{
    LOOP(channels) it->second.nicknames.remove(target.nickname);
    LOOP(socket_list)
    {
        auto data = *it;
        if (data.nickname != target.nickname) RESPOND(data.nickname, "QUIT", target.nickname, reason)
        else if (notify) RESPOND("", "ERROR", "QUIT", reason)
    }
    closesocket(target.socket);
    target.socket = INVALID_SOCKET;
    std::cout << "Disconnected " << target.nickname << " reason=" << reason << std::endl;
}
void print(socket_data data, message msg, char *heading)
{
    std::cout << heading << ":\n  " << inet_ntoa(data.address.sin_addr) << ":" << ntohs(data.address.sin_port) << "\n";
    if (!msg.prefix.empty()) std::cout << "  Prefix=" << msg.prefix << "\n";
    std::cout << "  Command=" << msg.command;
    int count = 0; LOOP(msg.params) std::cout << "\n  Param[" << count++ << "]=" << *it;
    std::cout << std::endl;
}
void tell(socket_data data, message msg)
{
    print(data, msg, "Response");

    std::string str = (!msg.prefix.empty() ? ":" + msg.prefix + " " : "") + msg.command;
    LOOP2(msg.params) str += " " + *it;
    str += " :" + *msg.params.rbegin() + "\r\n";

    int start = 0;
    do
    {
        int bytes = send(data.socket, str.c_str() + start, str.size() - start, 0);
        if (bytes > 0) start += bytes; else disconnect(data, "send error", 0);
    }
    while (str.size() - start > 0);
}
bool process(socket_data &data, message msg)
{
    print(data, msg, "Request");

    auto size = msg.params.size();
    auto first = size<1 ? "" : msg.params[0], second = size<2 ? "" : msg.params[1];
    if (!_stricmp(msg.command.c_str(), "DIE")) { return true;
    // and now all the cases
    CASE("KILL")    if (size<1)
                        RESPOND("", "461", data.nickname, "USER", "Not enough parameters")
                    else
                    {
                        bool found = false;
                        LOOP(socket_list) if (it->nickname == first) { found = true; disconnect((*it), first, 1); }
                        if (found == false) RESPOND("", "401", data.nickname, first, "No such nick/channel")
                    }
    CASE("NICK")    if (size<1)
                        RESPOND("", "431", data.nickname, "No nickname given")
                    else
                    {
                        bool found = false;
                        LOOP(socket_list) if (it->nickname == first) found = true;
                        if (found == true) RESPOND("", "433", data.nickname, "Nickname is already in use")
                        else data.nickname = first;
                    }
    CASE("USER")    if (size<4)
                        RESPOND("", "461", data.nickname, "USER", "Not enough parameters")
                    else
                    {
                        bool found = false;
                        LOOP(socket_list) if (it->username == first) found = true;
                        if (found == true) RESPOND("", "462", data.nickname, "Unauthorized command (already registered)")
                        else
                        {
                            data.username = first; data.mode = atoi(second.c_str()); data.servername = msg.params[2];  data.realname = msg.params[3];
                            RESPOND("", "001", data.nickname, "Welcome to the Internet Relay Network " + data.nickname + "!" + data.username + "@" + data.servername)
                        }
                    }
    CASE("MODE")    if (size<1)
                        RESPOND("", "461", data.nickname, "MODE", "Not enough parameters")
                    else if (first == data.nickname)
                    {
                        if (size < 2)
                            RESPOND("", "221", data.nickname, std::string("") + (data.mode & 2 ? "+w" : "-w") + (data.mode & 3 ? "+i" : "-i"))
                        else
                        {
                            auto x = (147 - second[1]) / 14;
                            if (second[0] == '+')
                            {
                                data.mode |= 1 << x;
                                std::cout << "set " << first << " mode bit " << x << "w=2, i=3" << std::endl;
                            }
                            else
                            {
                                data.mode &= ~(1 << x);
                                std::cout << "clear " << first << " mode bit " << x << "w=2, i=3" << std::endl;
                            }
                        }
                    }
                    else
                        RESPOND("", "477", data.nickname, first, "Channel doesn't support modes")
    CASE("WHOIS")   if (size < 1)
                        RESPOND("", "431", data.nickname, "No nickname given")
                    else
                    {
                        bool found = false;
                        LOOP(socket_list) if (it->nickname == first) { found = true; RESPOND("", "311", data.nickname, (it->nickname, it->username, it->servername, "*", it->realname)) }
                        if (found == true) RESPOND("", "318", data.nickname, first, "End of WHOIS")
                        else RESPOND("", "401", data.nickname, first, "No such nick/channel")
                    }
    CASE("WHO")     LOOP(channels[first].nicknames) LOOPX(socket_list, dit) if (*it == dit->nickname)
                        RESPOND("", "352", data.nickname, first, dit->username, dit->servername, "*", dit->nickname, "", dit->realname)
                    RESPOND("", "315", data.nickname, first, "End of WHO")
    CASE("JOIN")    if (size < 1)
                        RESPOND("", "461", data.nickname, "JOIN", "Not enough parameters")
                    else if (first == "0")
                        LOOP(channels) { LOOPX(it->second.nicknames, dit) if (*dit == data.nickname) RESPOND("","PART", it->first, data.nickname) it->second.nicknames.remove(data.nickname); }
                    else
                    {
                        auto& channel = channels[first];
                        bool found = false;
                        LOOP(channel.nicknames) if (*it == data.nickname) { found = true; }
                        if (found == false)
                        {
                            channel.nicknames.push_back(data.nickname);
                            RESPOND(data.nickname, "JOIN", first)
                            if (channel.topic.empty()) RESPOND("", "331", data.nickname, first, "No topic is set")
                            else RESPOND("", "332", data.nickname, first, channel.topic)
                            std::string list; LOOP(channel.nicknames) list += (list.empty() ? "" : " ") + *it;
                            RESPOND("", "353", data.nickname, "=", first, list)
                            RESPOND("", "366", data.nickname, first, "End of NAMES")
                        }
                    }
    CASE("PART")    if (size < 1)
                        RESPOND("", "461", data.nickname, "PART", "Not enough parameters")
                    else
                    {
                        bool found = false;
                        auto &channel = channels[first];
                        LOOP(channel.nicknames) if (*it == data.nickname) found = true;
                        channel.nicknames.remove(data.nickname);
                        if (found)
                        {
                            if (size < 2) msg.params.push_back(data.nickname);
                            RESPOND(data.nickname, "PART", first, second)
                        }
                        else RESPOND("", "442", data.nickname, first, "You're not on that channel")
                    }
    CASE("TOPIC")   if (size < 1)
                        RESPOND("", "461", data.nickname, "TOPIC", "Not enough parameters")
                    else
                    {
                        auto& channel = channels[first];
                        if (size < 2) { channel.topic = ""; RESPOND("", "331", data.nickname, first, "No topic is set") }
                        else { channel.topic = second; RESPOND("", "332", data.nickname, first, channel.topic) }
                    }
    CASE("LIST")    if (size < 1)
                    {
                        LOOP(channels)
                        {
                            std::stringstream ss; ss << it->second.nicknames.size();
                            RESPOND("", "322", data.nickname, it->first, ss.str(), it->second.topic.empty() ? "No topic is set" : it->second.topic)
                        }
                        RESPOND("", "323", data.nickname, "End of LIST")
                    }
                    else
                    {
                        std::stringstream ss; ss << channels[first].nicknames.size();
                        RESPOND("", "322", data.nickname, first, ss.str(), channels[first].topic.empty() ? "No topic is set" : channels[first].topic)
                        RESPOND("", "323", data.nickname, "End of LIST")
                    }
    CASE("NAMES")   if (size < 1)
                    {
                        LOOP(channels)
                        {
                            std::string list; LOOPX(it->second.nicknames, dit) list += (list.empty() ? "" : " ") + *dit;
                            RESPOND("", "353", data.nickname, "=", it->first, list)
                        }
                        RESPOND("", "366", data.nickname, "End of NAMES")
                    }
                    else
                    {
                        std::string list; LOOP(channels[first].nicknames) list += (list.empty() ? "" : " ") + *it;
                        RESPOND("", "353", data.nickname, "=", first, list)
                        RESPOND("", "366", data.nickname, first, "End of NAMES")
                    }
    CASE("PRIVMSG") if (size < 1)
                        RESPOND("", "411", data.nickname, "No recipient given (PRIVMSG)")
                    else if (size < 2)
                        RESPOND("", "412", data.nickname, "No text to send")
                    else
                    {
                        bool found = false;
                        auto from = data.nickname;
                        LOOP(channels) if (it->first == first)
                        {
                            found = true;
                            LOOPX(it->second.nicknames, nit) LOOPX(socket_list, dit) if (*nit == dit->nickname) { auto data = *dit; RESPOND(from, "PRIVMSG", data.nickname, second) }
                        }
                        if (found == false)
                            LOOP(socket_list) if (it->nickname == first)
                            {
                                found = true;
                                auto data = *it; RESPOND(from, "PRIVMSG", data.nickname, second)
                            }
                        if (found == false)
                            RESPOND("", "401", data.nickname, first, "No such nick/channel")
                    }
    CASE("USERS")   RESPOND("", "446", data.nickname, "USERS has been disabled")
    CASE("PING")    RESPOND("", "PONG", first, second)
    CASE("PONG")    data.ping_timer = time(NULL) + 60; data.pong_timer = 0;
    CASE("QUIT")    if (!size) msg.params.push_back(data.nickname);
                    disconnect(data, first, 1);
    // end of the cases
    } else {
        std::cout << "Received invalid message from " << data.nickname << " msg=" << msg.command << std::endl;
        RESPOND("", "421", data.nickname, msg.command, "Unknown command")
    }
    return false;
}
message parse(char *data)
{
    message msg;
    char *pointer = data;
    while (*data != '\0')
    {
        if (msg.command.empty())
        {
            if (*data == ':')
            {
                for (; *data != '\0' && *data != ' '; ++data); *data = '\0';
                msg.prefix = pointer + 1;
                pointer = ++data;
            }
            for (; *data != '\0' && *data != ' '; ++data); *data = '\0';
            msg.command = pointer;
            pointer = ++data;
        }
        else if (*data == ':')
        {
            for (; *data != '\0'; ++data);
            msg.params.push_back(pointer+1);
            pointer = ++data;
        }
        else
        {
            for (; *data != '\0' && *data != ' '; ++data); *data = '\0';
            msg.params.push_back(pointer);
            pointer = ++data;
        }
    }
    return msg;
}
int main()
{
    int retval;
    WSADATA wsaData;
    SOCKADDR_IN listen_address; listen_address.sin_family = AF_INET; listen_address.sin_port = htons(6667); listen_address.sin_addr.s_addr = htonl(INADDR_ANY);
    timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 10000;
    fd_set socket_read_set, socket_write_set, socket_except_set;
    socket_data listener; listener.nickname = "IRCd";
    EXIT_IF(1, (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)))
    EXIT_IF(2, (INVALID_SOCKET == (listener.socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))))
    EXIT_IF(3, (SOCKET_ERROR == bind(listener.socket, (SOCKADDR *)&listen_address, sizeof(listen_address))))
    EXIT_IF(4, (SOCKET_ERROR == listen(listener.socket, SOMAXCONN)))
    while (1)
    {
        MAKE_SET(socket_read_set) MAKE_SET(socket_write_set) MAKE_SET(socket_except_set)
        EXIT_IF(5, (SOCKET_ERROR == select(0, &socket_read_set, &socket_write_set, &socket_except_set, &timeout)))
        EXIT_IF(6, (FD_ISSET(listener.socket, &socket_except_set)))
        if (FD_ISSET(listener.socket, &socket_read_set))
        {
            socket_data data = {}; // zero everything
            data.address_length = sizeof(data.address);
            data.socket = accept(listener.socket, (SOCKADDR *)&data.address, &data.address_length);
            EXIT_IF(7, (INVALID_SOCKET == data.socket))
            std::stringstream ss; ss << inet_ntoa(data.address.sin_addr) << ":" << ntohs(data.address.sin_port); data.nickname = ss.str();
            data.ping_timer = time(NULL)+60; data.pong_timer = 0;
            socket_list.push_back(data);
            std::cout  << "Connected " << data.nickname << " ping=" << data.ping_timer << std::endl;
        }
        LOOP(socket_list)
        {
            socket_data &data = *it;
            if (data.ping_timer > 0 && time(NULL) > data.ping_timer)
            {
                RESPOND("", "PING", data.nickname)
                data.ping_timer = 0; data.pong_timer = time(NULL) + 60;
                std::cout << "Sent PING to " << data.nickname << " pong=" << data.pong_timer << std::endl;
            }
            if (data.pong_timer > 0 && time(NULL) > data.pong_timer) disconnect(data, "PONG", 1);
            if (FD_ISSET(data.socket, &socket_except_set)) disconnect(data, "select except", 0);
            if (FD_ISSET(data.socket, &socket_read_set))
            {
                int bytes = recv(data.socket, data.read_buffer + data.read_buffer_index, sizeof(data.read_buffer) - data.read_buffer_index - 1 > 0, 0);
                if (bytes > 0) data.read_buffer_index += bytes; else disconnect(data, "recv error", 0);
                char *pointer = data.read_buffer + data.read_buffer_index - 2;
                if (!strcmp(pointer, "\r\n"))
                {
                    *pointer++ = '\0'; *pointer = '\0'; // remove the \r\n
                    message msg = parse(data.read_buffer);
                    memset(data.read_buffer, 0, READ_BUFFER_SIZE); data.read_buffer_index = 0;
                    if (data.ping_timer > 0 && time(NULL) < data.ping_timer)
                    {
                        data.ping_timer = time(NULL) + 60; data.pong_timer = 0;
                        std::cout << "Reset ping for " << data.nickname << " ping=" << data.ping_timer << std::endl;
                    }
                    if (process(data, msg)) EXIT_IF(0, true)
                }
            }
        }
        socket_list.remove_if([](const socket_data& data){ return data.socket == INVALID_SOCKET; });
    }
    retval = 0;
finished:
    LOOP(socket_list) disconnect(*it, "exit", 0);
    disconnect(listener, "exit", 0);
    WSACleanup();
    return retval;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.