Avatar billede quiw Nybegynder
05. januar 2007 - 00:31 Der er 13 kommentarer og
1 løsning

Chat server volder en del problemer.

Hejsa igen igen eksperter, jeg har arbejdet med en chat i en del uger nu, og er desværre stødt ind i nogle problemer jeg absolut ikke kan løse, har stort set forsøgt alt og kigget alle steder. Chatten er et server program som enhver kan logge på via telnet, og skrive løs ud fra det nick de har valgt, men jeg har følgende problemstillinger.

Problemstillinger:
- Min chat crasher hvis MAXCLI bliver højere end 0, og den fungere faktisk fint selvom MAXCLI er = 0, men det udgøre bare at der kun kan være omkring 100 clienter online på samme tid.

- /tell funktionen virker ikke optimalt, den virker egentlig udmærket, men hvis personen logger ud, og man skriver til personen siger den "Personen er ikke online", men hvis så personen logger ind igen og vil skrive til personen, siger den stadig "Personen er ikke online".

- Folk er begyndt at bruge piletasterne hvilket gør at markøren flytter sig i telnet, det får chatten til at se totalt åndssvag ud når folk skriver oven i hinanden osv. Har forsøgt at skrive:
else if(strstr(input,"0xFF4B[A")=='\0')
og:
else if(strstr(input,"[A")=='\0')

Men så sletter den stort set alle taster.. Tasterne som jeg vil undgå er: ←[A og ←[B og ←[C og ←[D

Og sourcekode:
#include <iostream>
#include <windows.h>
#include <string>
#include <map>

using namespace std;

const int MAXCLI=0;

//Server variables.
bool server_online;
int server_port;

//Client variables.
char clients_chatmsg[200];
char clients_online_users[21][MAXCLI];
int clients_newchatmsg = 0;

bool client_online[MAXCLI];
map<string,int> onlinemap;
int client_timeout[MAXCLI];

// Messages sender.
DWORD __stdcall msgsend(void *p)
{
    int sd2=*((int*)p);
    while(client_online[sd2]==true)
    {
                if(clients_newchatmsg==1)
                {
                    send(sd2,clients_chatmsg,strlen(clients_chatmsg),0);
                    Sleep(2);
                    clients_newchatmsg=0;
                }
                else
                {
                    Sleep(2);
                }
    }
    return 0;
}

//Timeout system.
DWORD __stdcall timeout(void *p)
{
    int sd2=*((int*)p);
    while(1)
    {
              Sleep(600000);
              if(client_timeout[sd2]==1)
              {
                    client_timeout[sd2]=0;
              }
              else if(client_timeout[sd2]==2)
              {
                  break;
              }
              else
              {
                    send(sd2,"\r\n#Disconnected due timeout.\r\n",strlen("\r\n#Disconnected due timeout.\r\n"),0);
                    Sleep(5000);
                    client_online[sd2]=false;
                    closesocket(sd2);
                    sprintf(clients_chatmsg,"\r\n#Server: %s leaved due timeout. \r\n",clients_online_users[sd2]);
                    clients_newchatmsg=1;
                    break;
              }
    }
    return 0;
}

DWORD __stdcall client(void *p)
{
    int n;
    int sd2=*((int*)p);
    int socket;
    int i;
    int x;
   
    char input[5] = "";
    char buf[150] = "";
    char argu1[120];
    char argu2[20];
    char argu3[100];
    char name[20];
   
    // Setting clients online status, and setting the timeout thread to 1.
    client_online[sd2]=true;
    client_timeout[sd2]=1;
   
    //Starting the timeout thread.
    DWORD id4;
    CreateThread(0, 0, timeout, (LPVOID)&sd2, 0, &id4);
   
    //Welcome messages.
    send(sd2,"Welcome to Quiw's telnet chat server, enter name: ",strlen("Welcome to Quiw's telnet chat server, enter name: "),0);
   
    //Starting to receive name input.
    while(strstr(input,"\n")=='\0')
    {
          n = recv(sd2, input, sizeof(input),0);
          if(strlen(buf)<=20)
          {
                input[n] = '\0';
                strcat(buf,input);
                           
                //Backspace system.
                if(strcmp(input,"\b")=='\0')
                {
                    buf[strlen(buf)-2] = '\0';
                    send(sd2," \b",strlen(" \b"),0);
                }
                           
                //If connection closed wrong, send to closesystem.
                else if(strcmp(input,"\0")=='\0')
                {
                    goto client_closeconnection;
                }
          }
          else
          {
                //If the maximum of 20 is reached, overwrite existing char.
                send(sd2,"\b\b",strlen("\b\b"),0);
                buf[strlen(buf)-1] = '\0';
          }
    }
   
    //Setting name = input and removing the \n char.
    sprintf(name,"%s",buf);
    name[strlen(name)-2] = '\0';
    sprintf(clients_online_users[sd2],"%s",name);
    onlinemap.insert(make_pair(name,sd2));
   
    //Cleaning up buf and input, to prevent garbage in input system.
    sprintf(input,"");
    sprintf(buf,"");
   
    sprintf(clients_chatmsg,"%s%s%s","\r\nServer: ",clients_online_users[sd2]," has connected. \r\n");
    clients_newchatmsg=1;
   
    //Hello and welcome messages.
    send(sd2,"\r\n",strlen("\r\n"),0);
    send(sd2,"Hello ",strlen("Hello "),0);
    send(sd2,name,strlen(name),0);
    send(sd2,", go chatting :) (Type: /help to see commands.)\r\n",strlen(", go chatting :) (Type: /help to see commands.\r\n"),0);
    send(sd2,"\r\n----------- Chat ---------- \r\n",strlen("\r\n ----------- Chat ---------- \r\n"),0);
   
    //Starting to receive the chat messages.
    DWORD id2;
    CreateThread(0, 0, msgsend, (LPVOID)&sd2, 0, &id2);
   
    //The loop where all command inputs comes.
    while(client_online[sd2]==true)
    {
                x=0;
                // Resetting the client timeout system.
                client_timeout[sd2]=1;
   
                //Sleep 201 sec, to prevent chatmessages to print when cmd are printing.
                Sleep(201);
               
                //The prompt.
                send(sd2,"cmd: ",strlen("cmd: "),0);
               
                //Starting to receive.
                while(strstr(input,"\n")=='\0')
                {
                    n = recv(sd2, input, sizeof(input),0);
                    if(strlen(buf)<=120)
                    {
                            input[n] = '\0';
                            strcat(buf,input);
                            //Backspace system.
                            if(strcmp(input,"\b")=='\0')
                            {
                                buf[strlen(buf)-2] = '\0';
                                send(sd2," \b",strlen(" \b"),0);
                            }
                           
                            //If connection closed wrong, send to closesystem.
                            else if(strcmp(input,"\0")=='\0')
                            {
                                closesocket(sd2);
                                goto client_closeconnection;
                            }
                    }
                    else
                    {
                            //If the maximum of 120 is reached, overwrite existing char.
                            send(sd2,"\b\b",strlen("\b\b"),0);
                            buf[strlen(buf)-1] = '\0';
                    }
                }
                cout << buf;
                //Removing \n and cleaning up the chars.
                buf[strlen(buf)-2] = '\0';
                sprintf(input,"");
               
                //Putting the arguments in variables.
                sscanf(buf,"%s %s %s",&argu1,&argu2,&argu3);
               
                //Commands:
                if(client_online[sd2]==false)
                {
                    break;
                }
               
                //Tell system, uses the arguments.
                else if(strstr(argu1,"/tell")==argu1)
                {
                            if (onlinemap[argu2]<=0)
                            {
                                send(sd2,"\r\n#system: User is not online. \r\n",strlen("\r\n#system: User is not online. \r\n"),0);
                            }
                            else
                            {
                                sprintf(buf,"%s",buf+strlen(argu1)+strlen(argu2)+2);
                                sprintf(argu3,"\r\n%s tells you: %s \r\n",name,buf);
                                send(onlinemap[argu2],argu3,strlen(argu3),0);
                            }
                }
               
                //quit system.
                else if (strstr(buf,"/quit")==buf)
                {
                    send(sd2,"\r\nYou have Disconnected.\r\n",strlen("\r\nYou have Disconnected.\r\n"),0);
                    closesocket(sd2);
                    sprintf(clients_chatmsg,"\r\n#server: %s leaved. \r\n",clients_online_users[sd2]);
                    clients_newchatmsg=1;
                    goto client_closeconnection;
                }
                else if (strstr(buf,"/help")==buf)
                {
                    send(sd2,"\r\n/----- Help ------------------\r\n",strlen("\r\n----- Help ------------------\r\n"),0);
                    send(sd2,"\r\n/tell username messages - Send a private messages. ",strlen("\r\n/tell username messages - Send a private messages. "),0);
                    send(sd2,"\r\n/help - this message.",strlen("\r\n/help - this message."),0);
                    send(sd2,"\r\n/quit - logout.\r\n",strlen("\r\n/quit - logout.\r\n"),0);
                    send(sd2,"\r\n/-----------------------------\r\n",strlen("\r\n-----------------------------\r\n"),0);
                    send(sd2,"\r\n",strlen("\r\n"),0);
                    Sleep(100);
                }
               
                //Else.
                else
                {
                    sprintf(clients_chatmsg,"\r\n%s says: %s\r\n",name,buf);
                    clients_newchatmsg=1;
                }
               
                //Cleaning up buf.
                sprintf(buf,"");
    }
   
    //Close connection system.
    client_closeconnection:
    cout << "Client: ?? - "<<clients_online_users[sd2]<<" disconnected \n";
   
    //Cleaning up the name maps.
    client_timeout[sd2]=2;
    client_online[sd2]=false;
    sprintf(clients_online_users[sd2],"\0");
    onlinemap.erase(name);
    return 0;
}

//The start server part.
DWORD __stdcall startserver(void *p)
{
      int sd2[MAXCLI];
      int n=0;
      int len;
      WSADATA WSAData;           
      WSAStartup(0x0101,&WSAData);
      int sd=socket(AF_INET,SOCK_STREAM,0);
      if(sd<0)
      {
          cout << "#Could not create socket: " << strerror(errno) << endl;
      }
      else
      {
          cout << "#Socket created succesfull. \n";
      }
      int status;
      struct sockaddr_in local;
      struct sockaddr_in remote;
      local.sin_family=AF_INET;
      local.sin_port = htons(server_port);
      local.sin_addr.s_addr = INADDR_ANY;
      status=bind(sd,(struct sockaddr *)&local,sizeof(local));
      if(status<0)
      {
          cout << "#Could not bind socket: " << strerror(errno) << endl;
      }
      else
      {
          cout << "#Socket binded succesfull. \n";
      }
      status=listen(sd,5);
      if(status<0)
      {
          cout << "#Could not listen to socket: " << strerror(errno) << endl;
      }
      else
      {
          cout << "#Socket listen succesfull. \n";
      }
      cout << "#Server online. \n";
     
      // Accept loop.
      while(server_online == true)
      {
          sd2[n]=accept(sd,0,0);
          if(sd2[n]<0)
          {
              cout << "#Could not accept socket: " << strerror(errno) << endl;
          }
         
          //Starting client thread.
          DWORD id;
          CreateThread(0, 0, client, (LPVOID)&sd2[n], 0, &id);
         
          // Receiving the IP from client.
          len = sizeof(remote);
          status=getpeername(sd2[n],(struct sockaddr *)&remote,&len);
          if(status<0)
          {
              printf("Error getting socket address: %s\n",strerror(errno));
          }
          printf("Client: ID:%d - %d.%d.%d.%d connected\n",sd2[n],remote.sin_addr.s_net,remote.sin_addr.s_host, remote.sin_addr.s_lh,remote.sin_addr.s_impno);
      }
     
      //Clean up.
      WSACleanup();
      cout << "server tråden er stoppet \n";
      return 0;
}

int main()
{
  int cmd_number;
  int sd2[MAXCLI];
  char cmd[100];
  bool server=true;
  cout << "#Quiw's chatserver 1.0 \n";
  cout << "----------------------> \n";
  cout << "- Type /help for more information. \n\n";
  while(server==true)
  {
        Sleep(1000);                 
        cout << "\r\ncmd: ";
        cin.getline(cmd,sizeof(cmd));
        if(strstr(cmd,"/startserver")==cmd)
        {
                        if (server_online==true)
                        {
                            cout << "\r\n#The server is already running \n";
                        }
                        else
                        {
                            server_port=atoi((cmd+13));
                            if (server_port>0)
                            {
                                  cout << "\r\n#Starting server... \n";
                                  server_online=true;
                                  DWORD id3;
                                  CreateThread(0, 0, startserver, (LPVOID)&sd2,0, &id3);
                                  Sleep(50);
                            }
                            else
                            {
                                  cout << "#Port number is not given \n";
                            }
                        }
        }
        else if(strstr(cmd,"/shutdown")==cmd)
        {
                        (cmd+11);
                        if (server_online==false)
                        {
                            cout << "#There is not a server running \n";
                        }
                        else
                        {
                            cout << "#shutting down server... \n";
                            server_online=false;
                            Sleep(1000);
                            cout << "#Server offline. \n";
                        }
        }
        else if(strstr(cmd,"/whois")==cmd)
        {
                        sprintf(cmd,"%s",(cmd+7));
                        if (server_online==true)
                        {
                            if (onlinemap[cmd]<=0)
                            {
                                cout << "User not exist. \n";
                            }
                            else
                            {
                                cout << "Name: "<< cmd <<" has ID: "<<onlinemap[cmd]<<"\n";
                            }
                        }
                        else
                        {
                            cout << "#There is not a server running \n";
                        }
        }
        else if(strstr(cmd,"/help")==cmd)
        {
                        cout << "\nHelp info: \n";
                        cout << "----------------- \n";
                        cout << "/shutdown - Shutting down the server. \n";
                        cout << "/startserver port - Start the server at a given port number \n";
                        cout << "/quit - Shuts down the program \n";
                        cout << "/stats - Shows starts about running server \n";
                        cout << "/whois - Who is user \n";
                        cout << "/help - This message \n";
                        cout << "/kick - kicks a given user \n";
        }
        else if(strstr(cmd,"/stats")==cmd)
        {
                        if(server_online==true)
                        {
                              cout << "Server online: true\n";
                              cout << "Server port: "<<server_port<<"\n";
                        }
                        else
                        {
                            cout << "#The server is not running \n";
                        }
        }
        else if(strstr(cmd,"/kick")==cmd)
        {
                        if(server_online==true)
                        {
                            cmd_number=atoi((cmd+6));
                            if (cmd_number>0)
                            {
                                  send(cmd_number,"\r\n#You got kicked. \r\n",strlen("\r\n#You got kicked. \r\n"),0);
                                  Sleep(3000);
                                  closesocket(cmd_number);
                                  sprintf(clients_chatmsg,"\r\n%s%s%s","\nSystem: ",clients_online_users[cmd_number]," has been kicked. \r\n");
                                  clients_newchatmsg=1;
                                  client_timeout[cmd_number]=2;
                                  client_online[cmd_number]=false;
                                  cout << "#Socket ID: "<<cmd_number<<" has been kicked \n";
                            }
                            else
                            {
                                  cout << "#ID argument is not given \n";
                            }
                        }
                        else
                        {
                            cout << "#The server is not running \n";
                        }
        }
        else if(strstr(cmd,"/quit")==cmd)
        {
                        (cmd+7);
                        if(server_online==true)
                        {
                            cout << "#Server is already running, are you sure? Y/N \n";
                            wrongchoice:
                            cin.getline(cmd,sizeof(cmd));
                            if(strstr(cmd,"y")==cmd ||(cmd,"Y")==cmd)
                            {
                                  server_online=false;
                                  cout << "#Server offline. \n";
                                  cout << "#Closing .. \n";
                                  Sleep(1000);
                                  return 0;
                            }
                            else if(strstr(cmd,"n")==cmd || (cmd,"N")==cmd)
                            {
                                  cout << "#Returning back to console .. \n";
                            }
                            else
                            {
                                cout << "#Unknown choice. \n";
                                cout << "Please retry, Y/N. \n";
                                goto wrongchoice;
                            }
                        }
                        else
                        {
                                  cout << "#Closing .. \n";
                                  Sleep(1000);
                                  return 0;
                        }
        }
        else
        {
            cout << "#Unknown command: '"<<cmd<<"'\n";
        }
        cout << "\n";
  }
  return 0;
}

Håber der er nogle som har nogle ideer eller løsninger, på forhånd Tak.
Avatar billede quiw Nybegynder
05. januar 2007 - 00:34 #1
Note: Det ser ikke ud til at eksperten vil vise piltegnet under de "Forbudte" tegn. Følgende tegn ser således ud:
pil[A
pil[B
pil[C
pil[D

pil = Et tegn som viser en pil der peger mod venstre.
Avatar billede segmose Nybegynder
05. januar 2007 - 10:59 #2
Hmm

MAXCLI må ikke være 0 da alle arrays skal have en størrelse på 1 eller over, f.ex.
int client_timeout[MAXCLI];
ville være
int client_timeout[0]; // dette ville lave et array af 0 elementer ...

sæt MAXCLI til 100 f.ex.

Du bruger sprintf, brug snprintf istedet for at hindre at du skriver ud over enden på dine arrays.

Har du checket at sd2 har en lovlig værdi før du bruger den til at indexere noget,
(0 <= sd2) && (sd2 < MAXCLI)

Du afslutter ikke serveren hvis den ikke får en socket, du skriver bare en meddelelse, er det ok?


Nu til det egentlige problem, ved hvilken linie/variable crasher den?
Avatar billede quiw Nybegynder
05. januar 2007 - 11:50 #3
Segmose - Den fungere egentlig udmærket selvom MAXCLI er = 0, hvilket undre mig .. Og der er ikke en desideret linje i koden som gør den crasher, det gør den på et "tilfældigt" tidspunkt .. Ikke når noget specielt hænder .. Ikke så vidt jeg lige kan spore.
- Men når jeg kommer hjem vil jeg lige undersøge det som du har skrevet :)
Avatar billede quiw Nybegynder
05. januar 2007 - 15:07 #4
Angående MAXCLI, så virker det omvendt, jo lavere MAXCLI er, jo flere klienter kan der være online inden programmet crasher.. Testede således:
MAXCLI = 0 = 150 Klienter
MAXCLI = 50 = 60 Klienter
MAXCLI = 100 = 20 Klienter
Avatar billede segmose Nybegynder
05. januar 2007 - 15:21 #5
Men MAXCLI skulle være antal af klienter ikke?

jeg gætter på at en af de arrays der er defineret ud fra MAXCLI får lavet en læsning eller skrivning til et forbudt område alt efter hvad MAXCLI er sat til.

char clients_online_users[21][MAXCLI];

skulle det være

char clients_online_users[MAXCLI][21];
?
Avatar billede quiw Nybegynder
05. januar 2007 - 15:29 #6
clients_online_users er brugernavne der er online .. Derfor max 21 fordi det er maks antal tegn i deres navn .. Også MAXCLI fordi der max må være et antal af dem ..
Avatar billede quiw Nybegynder
05. januar 2007 - 15:29 #7
segmose - Må jeg eventuelt bede om din msn?.. Har på fornemmelsen at det kommer til at tage lidt tid at skrive over eksperten.
- Til andre med samme problem, vil selvfølgelig poste hvad jeg finder ud af :)
Avatar billede segmose Nybegynder
09. januar 2007 - 14:13 #8
Når jeg nu ikke kan huske det der med arrays må jeg ty til min Kernighan & Ritchie, exemplet fra A8.6.3:

static int x3d[3][5][7];

x3d is an array of 3 items; each item is an an array of 5 arrays; east of the latter arrays is an array of seven integers.

dvs. sidste index er af array typen, så i dit exemple vil du have MAXCLI arrays af 21 char, så det må være

char clients_online_users[MAXCLI][21];

Nej jeg har har ikke noget msn, det ligner noget farligt smitsomt noget.
Avatar billede quiw Nybegynder
10. januar 2007 - 19:01 #9
Har ændret:
const int MAXCLI= 100;

Og:
if((0 <= sd2[n]) && (n > MAXCLI))

Og:
char clients_online_users[MAXCLI][21];

- Men umiddelbart har det ikke løst nogle af problemerne, har sågar rodet en del timer med det .. Men det ser ud til at den stadig crasher efter ukendt tidspunkt .. Og den crasher også når et ukendt antal klienter logger på, den nægter heller ikke flere acceptions hvis MAXCLI er over 100 =/

Og:
Avatar billede segmose Nybegynder
11. januar 2007 - 13:54 #10
Denne betingelse kan jeg ikke lige forstår ...
if((0 <= sd2[n]) && (n > MAXCLI))

Jeg skrev tidligere:
(0 <= sd2) && (sd2 < MAXCLI) // dette sikre at sd2 er et lovligt index for alle array med MAXCLI elementer

det kunne så være en assert, eller en betingelse, f.ex.

const int USERLEN = 21;
char clients_online_users[MAXCLI][USERLEN];
if (0 <= sd2) && (sd2 < MAXCLI) {
if (USERLEN == snprintf(clients_online_users[sd2], USERLEN,"%s",name)
  BUG("String too long");
} else {
BUG("Index sd2 nok ok for clients_online_users");
}

BUG er så en funktion eller macro der kan give dig info om fejlen og evt. stoppe programmet.
Hvis du bruger en debugger medens programmet køre bør den stoppe på fejlen og vise dig den.

Essensen er at du skal checke hver eneste index for validitet og streng for bufferoverflow, ikke kun sprintf men også strcat bør checkes.
Derudover skal alle functions kald kontrolleres for thread safety i din compiler, herunder cout.

Hvor tæller du dine brugerere op? i  // Accept loop.?

bruger du socket numre til at indeksere dine arrays? og dermed dine brugerer
Avatar billede quiw Nybegynder
11. januar 2007 - 14:55 #11
Jeg indexere mine arrays med socket nummer ja :)
Avatar billede segmose Nybegynder
11. januar 2007 - 15:49 #12
Så er de første pladser vist optaget af default sockets så du får mindre end MAXCLI reele pladser, socket numret skal checkes for at der er en tilladt index til dine arrays.
Avatar billede quiw Nybegynder
04. juni 2008 - 10:06 #13
Kan se at jeg har haft denne topic åben længe, og skylder vist nogle point, smid venligst et svar ..
Avatar billede quiw Nybegynder
21. november 2008 - 16:10 #14
Hm, det ser ikke ud til der er respons, jeg acceptere mit eget svar.
- Skriv endelig, hvis nogle protesterer :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester