Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: Limbo: Yksinkertainen chat-serveri ja chat-asiakas

jalski [24.01.2010 22:49:07]

#

Yksinkertainen monisäikeinen chat-serveri ja chat-asiakas Infernolle Limbo-ohjelmointikielellä.

Koodivinkin tarkoituksena on näyttää, miten helppo kanavien avulla on toteuttaa monisäikeinen useamman yhtäaikaisen käyttäjän serveri-ohjelma, sekä tälle yksinkertainen graafinen asiakasohjelma.

Koodi ei ole kauhean nättiä ja chat-komentojen käsittely tarvitsisi oikeasti kunnollisen parserin.

Chat-serveri haluaa kännistysparametrikseen portin, mitä kuuntelee.

Chat-client haluaa käynnistysparametrikseen serverin osoitteen, mikä on muotoa: ip-osoite!portti


Simple Chat Serverin tuetut komennot:

NICK lempinimi
MSG viesti
MSGTO vastaanottaja viesti

lisäksi serveri palauttaa käyttäjälistan:

USERS nimi;nimi;nimi;jne


Simple Chat Server ja Simple Chat Client "äksönissä": http://www.tip9ug.jp/who/jalih/chatti.jpg

#
# Yksinkertainen Chat-serveri.
#
implement SServer;

include "sys.m";
        sys: Sys;
        Connection : import Sys;

include "draw.m";
        draw: Draw;
        Screen, Display, Image, Context, Point, Rect: import draw;

include "tk.m";
        tk: Tk;
        Toplevel: import tk;

include "tkclient.m";
        tkclient: Tkclient;
        Resize, Hide, Help : import tkclient;


SServer : module
{
        init : fn(ctxt : ref Draw->Context, argv : list of string);
};


# Ikkunan debug-tiedolle käyttöliittymän määritykset.
msg_cfg := array[] of {
   # kehykset
    "frame .msg",
   "frame .msg2",

   # tapahtumaikkuna
   "text .msg.t -width 32h -height 15h -state disabled -yscrollcommand {.msg.scroll set} -bg white",
   "scrollbar .msg.scroll -command {.msg.t yview}",

   # käyttäjälista
   "listbox .msg.l -width 15h -height 15h -state disabled -yscrollcommand {.msg.scroll2 set} -bg white",
   "scrollbar .msg.scroll2 -command {.msg.l yview}",

   # viesti-ikkuna
   "text .msg2.t2 -height 15h -state disabled -yscrollcommand {.msg2.scroll3 set} -bg white",
   "scrollbar .msg2.scroll3 -command {.msg2.t2 yview}",

   # pakkaa ja näytä
   "pack .msg.l -side left -expand 1 -fill both",
   "pack .msg.scroll2 -side left -fill y",
   "pack .msg.t -side left -expand 1 -fill both",
   "pack .msg.scroll -side left -fill y",
   "pack .msg -expand 1 -fill both -padx 5 -pady 5",
   "pack .msg2.t2 -side left -expand 1 -fill both",
   "pack .msg2.scroll3 -side left -fill y",
   "pack .msg2 -expand 1 -fill both -padx 5 -pady 5",
   "pack propagate . 0",
   "update"
};

# Asiakkaan tiedot.
Client : adt {
   id : string;
   connection : Connection;
};

# Viestin rakenne.
Message : adt {
   command : string;   # Komento
   msg : string;       # Viesti
   msgto : string;     # Vastaanottaja
   client : Client;    # Asiakkaan tiedot
};

# Komentokanava
commandch : chan of Message;

# Piirto konteksti ja toplevel widget.
ctxt: ref Draw->Context;
main : ref Tk->Toplevel;




init(xctxt : ref Draw->Context, argv : list of string)
{
   sys = load Sys Sys->PATH;

   # Tarkistetaan käynnistysparametrien määrä.
   if(len argv != 2) {
      sys->fprint(sys->fildes(2), "usage: %s port\n", hd argv);
       raise "fail:bad usage";
   }

   # Uusi prosessiryhmä, group id = kutsuvan prosessin pid.
   # Infernon prosessista voidaan käyttää myös säie nimitystä.
   sys->pctl(Sys->NEWPGRP, nil);

   draw = load Draw Draw->PATH;
   tk = load Tk Tk->PATH;
   tkclient = load Tkclient Tkclient->PATH;
   tkclient->init();

   # Piirto konteksti Tk-ikkunaa varten.
   if (xctxt == nil)
      xctxt = tkclient->makedrawcontext();

   ctxt = xctxt; # Talteen globaaliksi.

   # Hae käynnistysparametri (=portti).
   param := tl argv;
   port := hd param;

   # Julkistetaan osoitteeseen
   (n, conn) := sys->announce("tcp!*!" + port);
   if (n < 0)
   {
      sys->fprint(sys->fildes(2), "Server: announce failed\n");
      raise "fail:announce failed";
   }

   # Taustalle kuuntelemaan yhteydenottoja.
   spawn listenthread(conn);

   # Komennot ja viestit tulevat komentokanavaan.
   commandch = chan of Message;

   # Manageri säie taustalle käsittelemään komennot.
   # Kaikki viestiliikenne kulkee managerin kautta.
   # Hoitaa "kirjanpidon".
   spawn managerthread();

   # Top-level ikkuna debug tietoa varten (käyttäjät, yleiset viestit).
   (t, wmctl) := tkclient->toplevel(ctxt, nil, "Simple Chat Server", Tkclient->Appl);
   if(t == nil)
   {
      sys->fprint(sys->fildes(2), "Simple Chat Server: creation of toplevel window failed\n");
      raise "fail:creation of toplevel window failed";
   }

   main = t; # Talteen globaaliksi, jotta voidaan käyttää muista säikeistä suoraan.

   # Kanava ikkunankäsittely viesteille
   cmd := chan of string;
   tk->namechan(main, cmd, "cmd");

   # Ylhäällä sijaitsevasta msg_cfg[] taulukosta luodaan käyttöliittymä.
   for (c:=0; c < len msg_cfg; c++)
      tk->cmd(main, msg_cfg[c]);

   # Tästä alaspäin toteuttaa oikeastaan minimi ikkunankäsittelyn.
   tkclient->startinput(main, "ptr" :: "kbd" :: nil);
   tkclient->onscreen(main, nil);

   for(;;) alt {
         s := <-main.ctxt.kbd =>
            tk->keyboard(main, s);
         s := <-t.ctxt.ptr =>
            tk->pointer(main, *s);
         s := <-main.ctxt.ctl or
         s = <-main.wreq or
         s = <-wmctl =>
            tkclient->wmctl(main, s);

      }

}




# Kuuntelee yhteydenottoja.
# Jokaista yhteyttä varten luodaan palvelemaan oma säie.
listenthread(conn : Connection)
{
   while (1)
   {
      (ok, c) := sys->listen(conn);
      if (ok < 0)
      {
         sys->fprint(sys->fildes(2), "Server: listen failed\n");
         raise "fail:listen failed";
      }

      spawn workerthread(c);

   }

}



# Palvelee asiakasta.
workerthread(conn : Connection)
{
   buf := array [32] of byte; # Pitäisi riittää.

   # sys->listen palauttaa Connection adt:n, jossa dfd = nil
   # Käyttäjän vastuulla on avata data-tiedosto itse ja samalla
   # hyväksyä yhteys. Data-tiedostoa lukemalla ja kirjoittamalla hoidetaan
   # tiedon vastaanotto ja lähetys.
   rdfd := sys->open(conn.dir + "/data", Sys->OREAD);  # data-tiedosto
   rfd := sys->open(conn.dir + "/remote", Sys->OREAD); # remote-tiedosto

   # remote-tiedostosta lukemalla saadaan vastapuolen osoite selville.
   n := sys->read(rfd, buf, len buf);
   remote:= string buf[:n-1];

   # Ilmoitetaan managerille uudesta asiakkaasta.
   commandch <- = ("new", "",  "", (remote, conn));

   output := "";
   error := "";

   buf = array [1] of byte; # Luetaan tavu kerrallaan.
   buf2 := array [3] of byte; # Unicode merkki 1-3 tavua.
   counter := 0; # Laskuri unicode tavuille.

   while( (n=sys->read(rdfd, buf, len buf ) ) > 0 )
   {
      buf2[counter] = buf[0]; # Vastaanotettu tavu unicode tavu puskuriin.

      (c, size, status) := sys->byte2char(buf2[:counter + 1], 0);
      if(status == 0 && size == 1) # Virheellinen unicode.
      {
         output[len output] = c; # Tallenna virheellisestä syötteestä ilmoittava unicode.

         # Jos vain yksi tavu luettu, niin takaisin alkuun.
         if(counter == 0)
            continue;

         # Siirretään virheellisen tavun jälkeinen tavu unicode puskurin alkuun.
         buf2[0] = buf2[counter];

         counter = 0;

         (c, size, status) = sys->byte2char(buf2[:counter + 1], 0);
      }

      if(status == 0 && size == 0) # Tavu OK, mutta unicode merkki ei valmis.
      {
         counter ++;

         if(counter <= 2)
            continue; # takaisin while-loopin alkuun.

      }

      if(status != 1)
         continue;


      # unicode merkki valmis ja OK.
      output[len output] = c; # Tallenna unicode merkki.

      counter = 0;

      if(len output > 1024) # Viesti hiukan liian pitkä?
      {
         output = ""; # Heitetään pois.
         continue;
      }

      # Tarkistetaan viestit ja lähetetään tarvittaessa komentokanavan kautta
      # managerille lisätoimia varten.
      if(len output >= 2)
      {
         if(output[len output - 2:] == "\r\n")
         {
            if(len output > 7 && output[:5] == "NICK ")
            {
               (count, nil) := sys->tokenize(output[5:], " ");
               if(count == 1)
               {
                  commandch <- = ("nick", output[5:], "", (remote, conn));
                  output = "";
               }
               else
                  error = "EXPECTED NICKNAME WITH NO SPACES ALLOWED\r\n";
            }
            else if(len output > 8 && output[:6] == "MSGTO ")
            {
               (count, tokens) := sys->tokenize(output[6:], " ");
               if(count >= 2)
               {
                  commandch <- = ("msgto", output[6 + len hd tokens + 1:], hd tokens, (remote, conn));
                  output = "";
               }
               else
                  error = "EXPECTED MESSAGE TARGET AND MESSAGE\r\n";
            }
            else if(len output > 6 && output[:4] == "MSG ")
            {
               commandch <- = ("broadcast", output[4:], "", (remote, conn));
               output = "";
            }
            if(output != "")
            {
               if(error == "")
                  error = "UNKNOWN COMMAND MESSAGE\r\n";

               commandch <- = ("unknown", error, "", (remote, conn));
               output = "";
            }

            error = "";
         }


      }

   }

   # Ilmoita managerille, että asiakas sulki yhteyden.
   commandch <- = ("bye", "", "", (remote, conn));

}



# Etsii asiakasta yhteyshakemiston perusteella.
search(client : Client, clients : array of Client) : (int, string)
{
   count := len clients;

   if(count < 1)
   {
      return (-1, "zero size");
   }

   for(i := 0; i < count; i++)
   {
      if (clients[i].connection.dir == client.connection.dir)
      {
         return (i, "");
      }
   }

   return (-1, "not found");
}



# Etsii asiakasta lempinimen perusteella
searchnick(id : string, clients : array of Client) : (int, string)
{
   count := len clients;

   if(count < 1)
   {
      return (-1, "zero size");
   }

   for(i := 0; i < count; i++)
   {
      if (clients[i].id == id)
      {
         return (i, "");
      }
   }

   return (-1, "not found");
}



# Hoitaa tapahtumien välityksen asiakkaille
# ja kaiken "kirjanpidon".
managerthread()
{
   clients : array of Client;

   while (1)
   {
      # Blokkaa, kunnes komentokanavaan tulee viesti.
      msg := <- commandch;

      # Käsitellään komentoviestit.
      case msg.command  {
         "new" =>
            message := "[" + msg.client.id + "]";
            tk->cmd(main, ".msg.l insert end '" + msg.client.id);
            message += ": ESTABLISHED CONNECTION";
            tk->cmd(main, ".msg.t insert end '" + message + "\n" );
            tk->cmd(main, ".msg.t see end;update");

            i := len clients;
            tempclients := array[i + 1] of Client;
            (tempclients[0:], tempclients[i], tempclients[i+1:])  =  (clients[0:i], msg.client, clients[i:]);
            clients = tempclients;

            if(len clients > 0)
            {
               message += "\r\n";

               for( i = 0; i < len clients; i++ )
               {
                  wdfd := sys->open(clients[i].connection.dir + "/data", Sys->OWRITE);
                  sys->write(wdfd, array of byte message, len array of byte message);
               }

               message = "USERS";

               for( i = 0; i < len clients; i++ )
               {
                  if(i == 0)
                     message += " " + clients[i].id;

                  else
                     message += ";" + clients[i].id;
               }

               message += "\r\n";

               for( i = 0; i < len clients; i++ )
               {
                  wdfd := sys->open(clients[i].connection.dir + "/data", Sys->OWRITE);
                  sys->write(wdfd, array of byte message, len array of byte message);
               }

            }

         "nick" =>
            (index, err) := searchnick(msg.msg[:len msg.msg - 2], clients);
            if (err != "")
            {
               (index, err) = search(msg.client, clients);
               if (err != "")
               {
                  sys->fprint(sys->fildes(2), "Server: manager error\n");
                  raise "fail:manager error";
               }

               message := "[" + clients[index].id + "]: SET NICKNAME TO " + msg.msg[:len msg.msg - 2];
               tk->cmd(main, ".msg.t insert end '" + message + "\n");
               tk->cmd(main, ".msg.t see end;update");

               clients[index].id = msg.msg[:len msg.msg - 2];

               tk->cmd(main, ".msg.l delete " + string index);
               tk->cmd(main, ".msg.l insert "+ string index + " '" + msg.msg[:len msg.msg - 2]);
               tk->cmd(main, ".msg.l see end;update");

               if(len clients > 0 )
               {
                  message += "\r\n";

                  for( i := 0; i < len clients; i++ )
                  {
                     wdfd := sys->open(clients[i].connection.dir + "/data", Sys->OWRITE);
                     sys->write(wdfd, array of byte message, len array of byte message);
                  }

                  message = "USERS";

                  for( i = 0; i < len clients; i++ )
                  {
                     if(i == 0)
                        message += " " + clients[i].id;

                     else
                        message += ";" + clients[i].id;
                  }

                  message += "\r\n";

                  for( i = 0; i < len clients; i++ )
                  {
                     wdfd := sys->open(clients[i].connection.dir + "/data", Sys->OWRITE);
                     sys->write(wdfd, array of byte message, len array of byte message);
                  }


               }
            }
            else
            {
               message := "NICKNAME " + msg.msg[:len msg.msg - 2] +" ALLREADY ON USE\r\n";
               wdfd := sys->open(msg.client.connection.dir + "/data", Sys->OWRITE);
               sys->write(wdfd, array of byte message, len array of byte message);
            }


         "bye" =>
            (index, err) := search(msg.client, clients);
            if (err != "")
            {
               sys->fprint(sys->fildes(2), "Server: manager error\n");
               raise "fail:manager error";
            }

            message := "[" + clients[index].id + "]: CLOSED CONNECTION";
            tk->cmd(main, ".msg.t insert end '" + message + "\n");
            tk->cmd(main, ".msg.t see end;update");

            clients[index:] = clients[index + 1:];
            clients = clients[0:len clients -1];

            tk->cmd(main, ".msg.l delete " + string index);
            tk->cmd(main, ".msg.l see end;update");

            if(len clients > 0 )
            {
               message += "\r\n";

               for( i := 0; i < len clients; i++ )
               {
                  wdfd := sys->open(clients[i].connection.dir + "/data", Sys->OWRITE);
                  sys->write(wdfd, array of byte message, len array of byte message);
               }

               message = "USERS";

               for( i = 0; i < len clients; i++ )
               {
                  if(i == 0)
                     message += " " + clients[i].id;

                  else
                     message += ";" + clients[i].id;

               }

               message += "\r\n";

               for( i = 0; i < len clients; i++ )
               {
                  wdfd := sys->open(clients[i].connection.dir + "/data", Sys->OWRITE);
                  sys->write(wdfd, array of byte message, len array of byte message);
               }


            }

         "broadcast" =>
            (index, err) := search(msg.client, clients);
            if (err != "")
            {
               sys->fprint(sys->fildes(2), "Server: manager error\n");
               raise "fail:manager error";
            }

            message := "[" + clients[index].id + "]: " + msg.msg;
            tk->cmd(main, ".msg2.t2 insert end '" + message[:len message - 2] + "\n");
            tk->cmd(main, ".msg2.t2 see end;update");

            if(len clients > 0)
            {

               for( i := 0; i < len clients; i++ )
               {
                  wdfd := sys->open(clients[i].connection.dir + "/data", Sys->OWRITE);
                  sys->write(wdfd, array of byte message, len array of byte message);
               }
            }

         "msgto" =>
            (index1, err1) := search(msg.client, clients);
            if (err1 != "")
            {
               sys->fprint(sys->fildes(2), "Server: manager error\n");
               raise "fail:manager error";
            }

            message := "[" + clients[index1].id + "]: * " + msg.msg;

            (index2, err2) := searchnick(msg.msgto, clients);
            if (err2 == "")
            {
               wdfd := sys->open(clients[index2].connection.dir + "/data", Sys->OWRITE);
               sys->write(wdfd, array of byte message, len array of byte message);
            }
            else
            {
               message = "CAN'T DELIVER MESSAGE TO " + msg.msgto + "\r\n";
               wdfd := sys->open(clients[index1].connection.dir + "/data", Sys->OWRITE);
               sys->write(wdfd, array of byte message, len array of byte message);
            }


         * =>
            (index, err) := search(msg.client, clients);
            if (err != "")
            {
               sys->fprint(sys->fildes(2), "Server: manager error\n");
               raise "fail:manager error";
            }

            wdfd := sys->open(clients[index].connection.dir + "/data", Sys->OWRITE);
            sys->write(wdfd, array of byte msg.msg, len array of byte msg.msg);

      }

   }

}
#
# Yksinkertainen Chat-asiakas.
#
implement SClient;

include "sys.m";
        sys: Sys;
        Connection : import Sys;

include "draw.m";
        draw: Draw;
        Screen, Display, Image, Context, Point, Rect: import draw;

include "tk.m";
        tk: Tk;
        Toplevel: import tk;

include "tkclient.m";
        tkclient: Tkclient;
        Resize, Hide, Help : import tkclient;


SClient : module
{
   init : fn(ctxt : ref Draw->Context, argv : list of string);
};





# Käyttöliittymän määritykset.
msg_cfg := array[] of {
   # kehykset
      "frame .msg",
      "frame .msg2",

   # tapahtuma ikkuna
      "text .msg.t -width 32h -height 15h -state disabled -yscrollcommand {.msg.scroll set} -bg white",
      "scrollbar .msg.scroll -command {.msg.t yview}",

   # syöte kenttä
      "entry .msg2.e -height 1h -state normal -bg white",
      "bind .msg2.e <Key-\n> {send cmd sendmsg}",

   # käyttäjälista
      "listbox .msg.l -width 15h -height 15h -state disabled -yscrollcommand {.msg.scroll2 set} -bg white",
      "scrollbar .msg.scroll2 -command {.msg.l yview}",

   # pakkaa ja näytä
      "pack .msg.l -side left -expand 1 -fill both",
      "pack .msg.scroll2 -side left -fill y",
      "pack .msg.t -side left -expand 1 -fill both",
      "pack .msg.scroll -side left -fill y",
      "pack .msg -expand 1 -fill both -padx 5 -pady 5",
      "pack .msg2.e -side left -expand 1 -fill x",
      "pack .msg2 -expand 1 -fill x -padx 20 -pady 20",
      "pack propagate . 0",
      "focus .msg2.e; update"
};



# Piirto konteksti ja toplevel widget.
ctxt: ref Draw->Context;
main : ref Tk->Toplevel;




init(xctxt : ref Draw->Context, argv : list of string)
{
   sys = load Sys Sys->PATH;

   # Tarkistetaan käynnistysparametrien määrä.
   if(len argv != 2) {
      sys->fprint(sys->fildes(2), "usage: %s address\n", hd argv);
       raise "fail:bad usage";
   }

   # Uusi prosessiryhmä, group id = kutsuvan prosessin pid.
   # Infernon prosessista voidaan käyttää myös säie nimitystä.
   sys->pctl(Sys->NEWPGRP, nil);

   draw = load Draw Draw->PATH;
   tk = load Tk Tk->PATH;
   tkclient = load Tkclient Tkclient->PATH;
   tkclient->init();

   # Piirto konteksti Tk-ikkunaa varten.
   if (xctxt == nil)
      xctxt = tkclient->makedrawcontext();

   ctxt = xctxt; # Talteen globaaliksi.

   # Hae käynnistysparametri (=osoite).
   param := tl argv;
   address := "tcp!" + hd param;


   # Yritetään muodostaa yhteys.
   (n, conn) := sys->dial(address, "");
   if (n < 0) {
      sys->fprint(sys->fildes(2), "Simple Chat Client: connection refused\n");
      raise "fail:connection refused";
   }


   # Ohjelman top-level ikkuna
   (t, wmctl) := tkclient->toplevel(ctxt, nil, "Simple Chat Client", Tkclient->Appl);
   if(t == nil)
   {
      sys->fprint(sys->fildes(2), "Simple Chat Client: creation of toplevel window failed\n");
      raise "fail:creation of toplevel window failed";
   }

   main = t; # Talteen globaaliksi, jotta voidaan käyttää muista säikeistä suoraan.

   # Kanava ikkunankäsittely viesteille
   cmd := chan of string;
   tk->namechan(main, cmd, "cmd");

   # Ylhäällä sijaitsevasta msg_cfg[] taulukosta luodaan käyttöliittymä.
   for (c:=0; c < len msg_cfg; c++)
      tk->cmd(main, msg_cfg[c]);

   # Tästä alaspäin toteuttaa oikeastaan minimi ikkunan viestien käsittelyn.
   tkclient->startinput(main, "ptr" :: "kbd" :: nil);
   tkclient->onscreen(main, nil);

   spawn receivethread(conn); # Säie vastaanottamaan dataa.

   for(;;) alt {
         s := <-main.ctxt.kbd =>
            tk->keyboard(main, s);
         s := <-t.ctxt.ptr =>
            tk->pointer(main, *s);
         s := <-main.ctxt.ctl or
         s = <-main.wreq or
         s = <-wmctl =>
            tkclient->wmctl(main, s);

         cmd := <- cmd =>
            case cmd {
               "sendmsg" =>
                     message := tk->cmd(t, ".msg2.e get");
                     if(message != "")
                     {
                        message += "\r\n";
                        bytes := array of byte message;
                        sys->write(conn.dfd, bytes, len bytes);
                        tk->cmd(main, ".msg2.e delete 0 end");
                        tk->cmd(main, "focus .msg2.e; update");
                     }

            }

      }

}




receivethread(conn : Connection)
{

   buf := array [1] of byte; # Luetaan tavu kerrallaan.
   buf2 := array [3] of byte; # Unicode merkki 1-3 tavua.

   output := "";
   counter := 0; # Laskuri unicode tavuille.

   while( (n := sys->read(conn.dfd, buf, len buf ) ) > 0 )
   {
      buf2[counter] = buf[0]; # Vastaanotettu tavu unicode tavu puskuriin.

      (c, size, status) := sys->byte2char(buf2[:counter + 1], 0);
      if(status == 0 && size == 1) # Virheellinen unicode.
      {
         output[len output] = c; # Tallenna virheellisestä syötteestä ilmoittava unicode.

         # Jos vain yksi tavu luettu, niin takaisin alkuun.
         if(counter == 0)
            continue;

         # Siirretään virheellisen tavun jälkeinen tavu unicode puskurin alkuun.
         buf2[0] = buf2[counter];

         counter = 0;

         (c, size, status) = sys->byte2char(buf2[:counter + 1], 0);
      }

      if(status == 0 && size == 0) # Tavu OK, mutta unicode merkki ei valmis.
      {
         counter ++;

         if(counter <= 2)
            continue; # takaisin while-loopin alkuun.

      }

      if(status != 1)
         continue;


      # unicode merkki valmis ja OK.
      output[len output] = c; # Tallenna unicode merkki.

      counter = 0;


      if(len output > 1024) # Viesti hiukan liian pitkä?
      {
         output = ""; # Heitetään pois.
         continue;
      }

      if(len output >= 2)
      {
         if(output[len output - 2:] == "\r\n")
         {
            if(len output > 8 && output[:6] == "USERS ")
            {
               tk->cmd(main, ".msg.l delete 0 end");

               (count, tokens) := sys->tokenize(output[6:len output - 2], ";");

               while(tokens != nil)
               {
                  tk->cmd(main, ".msg.l insert end '" + hd tokens);
                  tokens = tl tokens;
               }

                  tk->cmd(main, ".msg.l see end; update");

            }
            else
            {
               tk->cmd(main, ".msg.t insert end '" + output[:len output - 2] + "\n");
               tk->cmd(main, ".msg.t see end;update");
            }

            output = "";

         }


      }


   }

   tk->cmd(main, ".msg.t insert end '" + "SERVER CLOSED THE CONNECTION\n");
   tk->cmd(main, ".msg.t see end;update");

}

Gaxx [04.02.2010 09:51:43]

#

Alkukuvauksen ja koodin ulkoisen rakenteen perusteella vinkki vaikuttaa ihan hyvältä esimerkiltä säikeiden ja tcp-protokollan käytöstä libmolla, vaikka en kyseistä kieltä osaakaan.

En kuitenkaan voi käsittää, miksi vinkki ei tarvitsisi kommentteja. Eihän se sen toimintaan tietysti vaikuta, mutta siellä on käytetty paljon muuttujia, jotka on nimetty tyyliin "mvujrjhfklfj". Ei niistä ota selvää hyvällä mielikuvituksellakaan. Vaikka muuttujat olisikin nimetty "erinomaisen kuvaavasti", kommentit nopeuttavat koodin lukemista ja ymmärtämistä todella(!) paljon.

En usko, että tuosta on ilman kommentteja varsinaista apua sellaiselle ihmiselle, joka ei jo ennestään osaa käyttää säikeitä ja tcp-protokollaa.

jalski [04.02.2010 12:44:16]

#

Luulen kyllä, että joku tuosta esimerkistä saa jotain irti. Itse opettelin Limbon käytännössä lukemalla muiden tekemiä ohjelma listauksia, miettimällä mitä koodi tekee ja miksi se on tehty noin.

Ei kai nuo muuttujat nyt noin huonosti ole nimettyjä?

esimerkiksi:

rdfd = read data file descriptor
wdfd = write data file descriptor
rfd = remote file descriptor
cmd = command
ctxt = context
t = toplevel

Limbo-ohjelmoinnissa kannattaa aluksi aloittaa opiskelu tutustumalla Infernoon ja varsinkin siihen miten asiat on toteutettu.

Kuten useimpiin muihinkin asioihin, niin Inferno tarjoaa tiedostoliitynnän tcp/ip-pinoon. Eli suomeksi sanottuna sen käyttäminen on yhtä helppoa kuin tiedostoon kirjoittaminen ja lukeminen (oikeasti). Infernon komentoriviltä saa man-komennolla tietoa useimmista asioista.

Kysymyksiä saa toki esittää, jos pohdinnan jälkeen jotain esimerkin toiminnasta jää epäselväksi.

Metabolix [18.02.2012 15:30:37]

#

Käyttäisit enemmän funktioita (kunnollisilla nimillä vieläpä), niin koodista olisi helpompi etsiä olennaisia kohtia (koska ne olisivat omissa funktioissaan). Vanha Linux-viisaus on ihan pätevä: funktio on riittävän lyhyt, kun se mahtuu kerralla ruudulle (25x80). Tuossakin koodissa olisi hyvin sijaa kymmenille selventäville apufunktioille.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta