Avatar billede quiw Nybegynder
15. januar 2006 - 18:02 Der er 12 kommentarer og
1 løsning

Chat server problemer.

Nu har jeg rodet med den kode som jeg fik her på eksperten .. Og ville prøve at lave en lille chat server.. Men den virker ikke helt optimalt .. Halvldelen af beskederne kommer nemlig ikke frem til hinanden, og andre af dem kommer frem på deres egen skærm .. Kun meget få blir sendt frem og tilbage ..
- Hvordan kan det være?  Har jeg fejl i min kode?, den compilleres fint .. Den ser sådan her ud:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;

#include <windows.h>

const char *WELCOME="Welcome\r\n";
const char *QUIT="quit\r\n";
const char *BYEBYE="Bye bye\r\n";
const char *UNKNOWN="Unknown command\r\n";
const char *NEWLINE="\r\n";
const char *CHATMSG="";
const char *OLDCHATMSG="";

DWORD __stdcall msgsend(void *p)
{
    while(1)
    {
            if ( strcmpi ( CHATMSG, OLDCHATMSG ) == 0 )
            {
            }
            else
            {
                int sd2=*((int*)p);
                send(sd2,CHATMSG,strlen(CHATMSG),0);
                OLDCHATMSG = CHATMSG;
            }
    }
}


DWORD __stdcall client(void *p)
{
    int sd2=*((int*)p);
    send(sd2,WELCOME,strlen(WELCOME),0);
    char buf[80];
    memset(buf,0x00,sizeof(buf));
    int ix=0;
    int len;
    DWORD id2;
    CreateThread(0, 0, msgsend, (LPVOID)&sd2, 0, &id2);
    while((len=recv(sd2,buf+ix,sizeof(buf)-ix,0))>0)
    {
        ix=ix+len;
        if(strstr(buf,NEWLINE)!=NULL)
        {
            char cmd[80];
            memset(cmd,0x00,sizeof(cmd));
            int cmdlen = (strstr(buf,NEWLINE)-buf)+strlen(NEWLINE);
            memmove(cmd,buf,cmdlen);
            memmove(buf,buf+cmdlen,ix-cmdlen);
            ix=ix-cmdlen;
            memset(buf+ix,0x00,sizeof(buf)-ix);
            if(strcmpi(cmd,QUIT)==0)
            {
                send(sd2,BYEBYE,strlen(BYEBYE),0);
                closesocket(sd2);
                cout << "Client disconnected" << endl;
                break;
            }
            CHATMSG = cmd;
            cout << "Besked der blev tastet paa chatten: "<<CHATMSG<< " \n";
        }
    }
    return 0;
}

const int MAXCLI=10;

int main()
{
    int start;
    WSADATA WSAData;
    WSAStartup(0x0101,&WSAData);
    int sd=socket(AF_INET,SOCK_STREAM,0);
    if(sd<0)
    {
        cout << "Could not create socket: " << strerror(errno) << endl;
    }
    int status;
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port = htons(1234);
    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;
    }
    status=listen(sd,5);
    if(status<0)
    {
        cout << "Could not listen to socket: " << strerror(errno) << endl;
    }
    int sd2[MAXCLI];
    int n=0;
    while(1)
    {
        sd2[n]=accept(sd,0,0);
        if(sd2[n]<0)
        {
            cout << "Could not accept socket: " << strerror(errno) << endl;
        }
        cout << "Client connected" << endl;
        DWORD id;
        CreateThread(0, 0, client, (LPVOID)&sd2[n], 0, &id);
    }
    closesocket(sd);
    WSACleanup();
    return 0;
}
Avatar billede quiw Nybegynder
17. januar 2006 - 11:16 #1
Ingen svar? :(
- For svært spørgsmål?
- For lidt point?
- For dumt et indlæg?

- Bør jeg lukke det igen? .. Hehe ..
Avatar billede bertelbrander Novice
17. januar 2006 - 20:05 #2
Jeg havde håbet at arne_v kom forbi...

I msgsend sammenligner man CHATMSG & OLDCHATMSG, men disse er fælles for alle tråde/clienter?

msgsend vil stjæle alt ledig cpu tid, måske skulle man lave en mutex som fik den til at vente indtil der var noget at sende.

Hvis der kommer en ny besked inden den er færdig med at sende den første går det galt.
Avatar billede quiw Nybegynder
17. januar 2006 - 21:52 #3
Bertelbrander - Uh dejligt at høre svar, men hvad er mutex helt eksakt? Well, hvis der kommer en ny besked? Mit problem er egentlig at hvis der er 3 clienter online og den ene indtaster noget ... Så er det kun 1 ud af de 3 clienter som modtager .. Selvom den burde blive sendt til alle, og det er ganske tilfældigt hvem der modtager, det sker sågar at man selv modtager sin egen besked..
Avatar billede bertelbrander Novice
17. januar 2006 - 22:07 #4
Jeg vil foreslå at du glemmer msgsend funktionen/tråden og sender direkte fra client funktionen. Sålænge mængden af data er lille bør det ikke være noget problem, vi kan godt lave en rigtig sende tråd senere.

Start med at slette linien:
CreateThread(0, 0, msgsend, (LPVOID)&sd2, 0, &id2);

Og erstat:
CHATMSG = cmd;
Med:
send(sd2,cmd,strlen(cmd),0);

Check så om ikke det hjælper.
Avatar billede quiw Nybegynder
17. januar 2006 - 22:48 #5
Har jeg gjort nu, men nu modtager jeg beskederne selv .. Skal der ikke være en separeret tråd som kan sende ud til alle?..
- Problemet er at den ikke sender til alle .. :/
Avatar billede bertelbrander Novice
17. januar 2006 - 22:57 #6
Jeg har ikke lavet servere før eller brugt accept, men du har ret, man skal jo sende til alle andre end den socket der modtog...

Så man kunne lave én sende tråd, og have en global liste af aktive forbindelser.
Men man må skulle lave en ny socket for hver forbindelse der er accepteret, til det brug kan de to ekstra argumenter til accept bruges, de bruges til at få en sockaddr der kan bruges til at lave en socket.
Avatar billede quiw Nybegynder
17. januar 2006 - 23:07 #7
Bertelbrander - Præcis, havde lavet en sende tråd som tog en global variable af besked.. Men.. Hvordan finder man aktive forbindelser?
- Og kan du give mig et kort eksempel på hvordan jeg ku ændre sockaddr med de to ekstra argumenter? Ville være meget nyttigt ..
Avatar billede bertelbrander Novice
17. januar 2006 - 23:20 #8
Ved nærmere eftertanke:

I main har vi:
int sd2[MAXCLI];
Denne kommer til at indeholde alle de socket's der returneres fra accept.

Hvis man nu:
1: Kun havde én msgsend tråd.
2: Satte alle socket's i sd2[] til -1
3: Lod msgsend sende til alle socket's i sd2[] der ikke er -1

Det forudsætter at man kan sende og modtage på en socket samtidig.
Avatar billede bertelbrander Novice
17. januar 2006 - 23:30 #9
Jeg lavede en ny version:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;

#include <windows.h>

const char *WELCOME="Welcome\r\n";
const char *QUIT="quit\r\n";
const char *BYEBYE="Bye bye\r\n";
const char *UNKNOWN="Unknown command\r\n";
const char *NEWLINE="\r\n";
const char *OLDCHATMSG="";

const int MAXCLI=10;

int SendSocket[MAXCLI] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
char SendMsg[1024];

DWORD __stdcall msgsend(void *p)
{
    while(1)
    {
      if(SendMsg[0])
      {
          int idx;
          for(idx = 0; idx < MAXCLI; idx++)
          {
            if(SendSocket[idx] >= 0)
            {
                send(SendSocket[idx], SendMsg, strlen(SendMsg),0);
            }
          }
      }
      SendMsg[0] = 0;
    }
}


DWORD __stdcall client(void *p)
{
    int sd2=*((int*)p);
    send(sd2,WELCOME,strlen(WELCOME),0);
    char buf[80];
    memset(buf,0x00,sizeof(buf));
    int ix=0;
    int len;

    while((len=recv(sd2,buf+ix,sizeof(buf)-ix,0))>0)
    {
        ix=ix+len;
        if(strstr(buf,NEWLINE)!=NULL)
        {
            char cmd[80];
            memset(cmd,0x00,sizeof(cmd));
            int cmdlen = (strstr(buf,NEWLINE)-buf)+strlen(NEWLINE);
            memmove(cmd,buf,cmdlen);
            memmove(buf,buf+cmdlen,ix-cmdlen);
            ix=ix-cmdlen;
            memset(buf+ix,0x00,sizeof(buf)-ix);
            if(strcmpi(cmd,QUIT)==0)
            {
                send(sd2,BYEBYE,strlen(BYEBYE),0);
                closesocket(sd2);
                cout << "Client disconnected" << endl;
                break;
            }
            strcpy(SendMsg, cmd);

            cout << "Besked der blev tastet paa chatten: "<< cmd << " \n";
        }
    }
    return 0;
}


int main()
{
    int start;
    WSADATA WSAData;
    WSAStartup(0x0101,&WSAData);
    int sd=socket(AF_INET,SOCK_STREAM,0);
    if(sd<0)
    {
        cout << "Could not create socket: " << strerror(errno) << endl;
    }
    int status;
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port = htons(1234);
    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;
    }
    status=listen(sd,5);
    if(status<0)
    {
        cout << "Could not listen to socket: " << strerror(errno) << endl;
    }
    DWORD id2;
    CreateThread(0, 0, msgsend, 0, 0, &id2);

    int sd2[MAXCLI];
    int n=0;
    while(1)
    {
      sockaddr NewSockAddr;
      int size = sizeof(NewSockAddr);
        sd2[n]=accept(sd,&NewSockAddr, &size);
        SendSocket[n] = socket(AF_INET, SOCK_STREAM, 0);
        bind(SendSocket[n], &NewSockAddr, sizeof(NewSockAddr));
        if(sd2[n]<0)
        {
            cout << "Could not accept socket: " << strerror(errno) << endl;
        }
        cout << "Client connected" << endl;
        DWORD id;
        CreateThread(0, 0, client, (LPVOID)&sd2[n], 0, &id);
    }
    closesocket(sd);
    WSACleanup();
    return 0;
}

Det er ikke testet, men burde give en idé
Der er stadig med et problem hvis man modtager flere beskeder ca samtidig, og et problem at sendmsg tråden vil æde alt cpu tid, men det er til at løse.
Avatar billede quiw Nybegynder
18. januar 2006 - 13:46 #10
Hm, Ja kan godt se hvad du mener. Tror du vi kan få arne_v til at kigge på det?
- Vil nemlig gerne have lavet 2 funktioner.. En til f.eks. Send_all(messages); også Send_user(messages); eventuelt dele users op efter ID.
Avatar billede bertelbrander Novice
18. januar 2006 - 19:56 #11
Det burde ikke være noget større problem at lave de funktioner.
Jeg har detsværre ikke så meget tid lige nu, men du kunne prøve at lave dem, så kan jeg hjælpe hvis der opstår problemer.
Avatar billede bertelbrander Novice
19. januar 2006 - 00:33 #12
Jeg fik lavet en ny version der burde løse diverse problemer.
Det er IKKE testet, så du skal nok påregne lidt aflusning.

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>

using namespace std;

#include <windows.h>

const char *WELCOME="Welcome\r\n";
const char *QUIT="quit\r\n";
const char *BYEBYE="Bye bye\r\n";
const char *NEWLINE="\r\n";

DWORD SendThreadId;

struct AcceptParam
{
  int RxSocket;
  int TxSocket;
};

enum MessageType
{
  AddSocketMessage = 1030,
  SendMsgMessage,
  SendSingleMsgMessage,
  RemoveSocketMessage
};

DWORD __stdcall SendThread(void *)
{
  std::vector<int> SocketList;
  MSG msg;
  while(GetMessage(&msg, 0, 0, 0xFFFF) > 0)
  {
      switch(msg.message)
      {
      case AddSocketMessage:
        SocketList.push_back(msg.wParam);
        break;
      case SendSingleMsgMessage:
        {
            char *p = (char *)msg.wParam;
            int Sock = msg.lParam;
            send(Sock, p, strlen(p), 0);
            delete [] p;
        }
        break;
      case SendMsgMessage:
        {
            char *p = (char *)msg.wParam;
            unsigned int idx;
            for(idx = 0; idx < SocketList.size(); idx++)
            {
              if(SocketList[idx] != msg.lParam) // Don't send to the sender
              {
                  send(SocketList[idx], p, strlen(p), 0);
              }
            }
            delete [] p;
        }
        break;
      case RemoveSocketMessage:
        {
            unsigned int idx;
            for(idx = 0; idx < SocketList.size(); idx++)
            {
              if(SocketList[idx] == (int )msg.wParam)
              {
                  SocketList.erase(SocketList.begin() + idx);
              }
            }
        }
        break;
      }
  }
  return 0;
}

DWORD __stdcall ClientThread(void *p)
{
  AcceptParam *Param = (AcceptParam *)p;
  char *t = new char [strlen(WELCOME) + 1];
  strcpy(t, WELCOME);
  PostThreadMessage(SendThreadId, SendSingleMsgMessage, (WPARAM )t, (LPARAM )Param->TxSocket);
  PostThreadMessage(SendThreadId, AddSocketMessage, (WPARAM )Param->TxSocket, 0);

  char buf[1024];
  int ix = 0;
  int len;

  while((len = recv(Param->RxSocket, buf + ix, sizeof(buf) - ix, 0)) > 0)
  {
      ix = ix + len;
      if(strstr(buf, NEWLINE) != 0)
      {
        buf[ix] = 0;
        if(strcmpi(buf, QUIT)==0)
        {
            t = new char [strlen(BYEBYE) + 1];
            strcpy(t, BYEBYE);
            PostThreadMessage(SendThreadId, SendSingleMsgMessage, (WPARAM )t, (LPARAM )Param->TxSocket);
            PostThreadMessage(SendThreadId, RemoveSocketMessage, (LPARAM )Param->TxSocket, 0);
            closesocket(Param->RxSocket);
            closesocket(Param->TxSocket);
            delete Param;
            cout << "Client disconnected" << endl;
            return 0;
        }
        t = new char [strlen(buf) + 1];
        strcpy(t, buf);
        PostThreadMessage(SendThreadId, SendMsgMessage, (WPARAM )t, (LPARAM )Param->TxSocket);
        cout << "Besked der blev tastet paa chatten: "<< buf << " \n";
      }
      ix = 0;
  }
  return 0;
}


int main()
{
  WSADATA WSAData;
  WSAStartup(0x0101, &WSAData);
  int sd=socket(AF_INET, SOCK_STREAM, 0);
  if(sd<0)
  {
      cout << "Could not create socket: " << strerror(errno) << endl;
  }
  int status;
  struct sockaddr_in local;
  local.sin_family = AF_INET;
  local.sin_port = htons(1234);
  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;
  }
  status = listen(sd, 5);
  if(status < 0)
  {
      cout << "Could not listen to socket: " << strerror(errno) << endl;
  }

  CreateThread(0, 0, SendThread, 0, 0, &SendThreadId);

  while(1)
  {
      sockaddr NewSockAddr;
      int size = sizeof(NewSockAddr);
      int RxSock = accept(sd, &NewSockAddr, &size);
      int TxSock = socket(AF_INET, SOCK_STREAM, 0);
      bind(TxSock, &NewSockAddr, sizeof(NewSockAddr));
      if(TxSock < 0 || RxSock < 0)
      {
          cout << "Could not accept socket: " << strerror(errno) << endl;
      }
      else
      {
        cout << "Client connected" << endl;
        DWORD id;
        AcceptParam *Param = new AcceptParam;
        Param->RxSocket = RxSock;
        Param->TxSocket = TxSock;
        CreateThread(0, 0, ClientThread, (LPVOID )Param, 0, &id);
      }
  }
  closesocket(sd);
  WSACleanup();
  return 0;
}
Avatar billede quiw Nybegynder
04. juni 2008 - 10:09 #13
Fungerer udemærket, mange tak :)

- Jeg husker at du ikke samler på point, så jeg ligger selv et svar :)
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