C ++ (golfe parcialmente) 5655 (com CRLF contando por 1)
Isso é compilado no VS 2013 (usa auto, lambdas e winsock). Parecia estar funcionando antes de eu jogar o golfe, a menos que eu amassasse, ainda assim deveria estar ok. Uma das razões pelas quais é tão grande é que as respostas numéricas que estou retornando incluem o texto especificado na RFC - não sei se isso é necessário ou não. Testei-o com o KVirc porque ele é executado de forma portável (não é permitido instalar software no meu PC!) O KVirc parece funcionar com meu servidor, mas não conheço outros clientes - fiz o que achava que a RFC dizia, mas muito disso está subespecificado, então espero que eu entenda direito.
O servidor lida com DIE, KILL, NICK, USER, MODE, WHOIS, WHO, JOIN, PART, TOPIC, LIST, NAMES, PRIVMSG, USERS, PING, PONG e QUIT em vários graus. Para a maioria deles, retorno as respostas necessárias, incluindo a maioria das verificações necessárias para retornar as respostas de erro especificadas. Para alguns deles eu trapaceio:
- USERS sempre retorna 446 "USERS foi desativado"
- a mensagem MODO do canal sempre retorna 477 "O canal não suporta modos"
- a mensagem MODO do usuário funciona corretamente, mas os sinalizadores não são usados pelos outros comandos
Eu acho que é apenas parcialmente jogado, porque eu não sou muito bom em jogar golfe; se você vir algo grande, edite a resposta e corrija-a.
Aqui está a versão golfada
#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;}
Aqui está a versão mais não destruída (ainda usa algumas macros):
#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;
}