c# - Async chat server buffer issue -


can please me out this... i've been struggling day.

so i'm trying learn async sockets that's been giving me trouble.

the issue way i'm updating listbox people have joined chat room's names:

chat window

basically i'm doing having each client send "!!addlist [nickname]" when join server.

it's not ideal doesn't check duplicates etc. want know why won't work. whenever adds name haven't seen before, send "!!addlist [nick]"

in way, every time joins, lists should updated everyone. issue seems clients start communicating @ same time , interferes buffer.

i've tried using separate buffer every client that's not issue. i've tried using lock() doesn't seem working either.

essentially happens buffers seem truncate; there data 2 different people in same buffer.

please tell me i'm doing wrong buffers or on client side:

note async socket using send instead of beginsend. i've tried both methods , run same issue... it's client side?

public partial class login : form {     private chatwindow cw;     private socket serversocket;     private list<socket> socketlist;     private byte[] buffer;     private bool ishost;     private bool isclosing;      public void startlistening()     {         try         {             this.ishost = true;                                                         //we're hosting server             cw.callingform = this;                                                      //give chatform login form (this) [that acts server]             cw.show();                                                                  //show chatform             cw.ishost = true;                                                           //tell chatform host (for display purposes)             this.hide();                                                                //and hide login form             serversocket.bind(new ipendpoint(ipaddress.any, int.parse(portbox.text)));  //bind our local address             serversocket.listen(1);                                                     //and start listening             serversocket.beginaccept(new asynccallback(acceptcallback), null);          //when connects, begin async callback             cw.connectto("127.0.0.1", int.parse(portbox.text), nicknamebox.text);       //and have chatform connect server         }         catch (exception) { /*messagebox.show("error:\n\n" + e.tostring());*/ }           //let know if ran errors     }      public void acceptcallback(iasyncresult ar)     {         try         {             socket s = serversocket.endaccept(ar);                                                              //when connects, accept new socket             socketlist.add(s);                                                                                  //add our list of clients             s.beginreceive(buffer, 0, buffer.length, socketflags.none, new asynccallback(receivecallback), s);  //begin async receive method using our buffer             serversocket.beginaccept(new asynccallback(acceptcallback), null);                                  //and start accepting new connections         }         catch (exception) {}     }      public void receivecallback(iasyncresult ar)                //when message client received     {         try         {             if (isclosing)                 return;              socket s = (socket)ar.asyncstate;                   //get socket our iasyncresult              int received = s.endreceive(ar);                    //read number of bytes received (*need add locking code here*)             byte[] dbuf = new byte[received];                   //create temporary buffer store received don't have data              array.copy(buffer, dbuf, received);                 //copy received data our buffer our temporary buffer              foreach (socket client in socketlist)               //for each client connected             {                 try                 {                     if (client != (socket)ar.asyncstate)        //if isn't same client sent message (*client handles displaying these*)                         client.send(dbuf);                      //send message client                 }                 catch (exception) {  }             }                                                  //start receiving new data again             s.beginreceive(buffer, 0, buffer.length, socketflags.none, new asynccallback(receivecallback), s);         }         catch (exception) { /*cw.output("\n\nerror:\n\n" + e.tostring());*/ }     }      public void sendcallback(iasyncresult ar)     {         try         {             socket s = (socket)ar.asyncstate;             s.endsend(ar);         }         catch (exception) { /*cw.output("\n\nerror:\n\n" + e.tostring());*/ }     } 

here client side:

    public void getdata()     {         try         {             byte[] buf = new byte[1024];             string message = "";             while(isconnected)             {                 array.clear(buf, 0, buf.length);                 message = "";                 clientsocket.receive(buf, buf.length, socketflags.none);                 message = encoding.ascii.getstring(buf);                 if (message.startswith("!!addlist"))                 {                     message = message.replace("!!addlist", "");                     string usernick = message.trim();                     if (!namesbox.items.contains(usernick))                     {                         addnick(usernick.trim());                     }                     continue;                 }                 else if (message.startswith("!!removelist"))                 {                     message = message.replace("!!removelist", "");                     string usernick = message.trim();                     removenick(usernick);                     output("someone left room: " + usernick);                     continue;                 }                 else if (!namesbox.items.contains(message.substring(0, message.indexof(":"))))                 {                     addnick(message.substring(0, message.indexof(":")).trim()); //so @ least added when send message                 }                 output(message);             }         }         catch (exception)         {             output("\n\nconnection server lost.");             isconnected = false;         }     } 

here retarded addnick function seems fix things?

    public void addnick(string n)     {         if (n.contains(" ")) //no spaces... such headache             return;         if (n.contains(":"))             return;         bool shouldadd = true;         n = n.trim();         (int x = namesbox.items.count - 1; x >= 0; --x)             if (namesbox.items[x].tostring().contains(n))                 shouldadd = false;         if (shouldadd)         {             namesbox.items.add(n);             output("someone new joined room: " + n);             sendraw("!!addlist " + nickname);         }     } 

i think issue of packets being skipped?

maybe there's code in client after receive before gets called again?

should create separate thread each message receive runs constantly? (dumb)

should have client use async receives , sends well?

i have feeling answer ^

with of checks do, managed clean duplicate name issue... regularly receive messages spaces , partial messages other clients seems.

not receiving names

okay so, after messing long time, have relatively stable.

for starters, added following state object:

public class stateobject {     public socket worksocket = null;     public const int buffersize = 1024;     public byte[] buffer = new byte[buffersize];     public stringbuilder sb = new stringbuilder();     public bool newconnection = true; } 

this makes easy keep track of each connection , gives each connection own buffer.

the second thing did new line in each message. wasn't looking in original code , believe root of issues.

i gave responsibility of dealing username management server; should have done start obviously.

here current server code:

this code in no way perfect , i'm continuously finding new errors more try break it. i'm going keep messing awhile @ moment, seems work decently.

public partial class login : form {     private chatwindow cw;     private socket serversocket;     private list<socket> socketlist;     private byte[] buffer;     private bool ishost;     private bool isclosing;     private listbox usernames;      public login()     {         initializecomponent();     }      private void login_load(object sender, eventargs e)     {         iplabel.text = getlocalip();         cw = new chatwindow();         serversocket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);         socketlist = new list<socket>();         buffer = new byte[1024];         isclosing = false;         usernames = new listbox();     }      public string getlocalip()     {         return dns.gethostentry(dns.gethostname()).addresslist.firstordefault(ip => ip.addressfamily == addressfamily.internetwork).tostring();     }      private void joinbutton_click(object sender, eventargs e)     {         try         {             int tryport = 0;             this.ishost = false;             cw.callingform = this;             if (ipbox.text == "" || portbox.text == "" || nicknamebox.text == "" || !int.tryparse(portbox.text.tostring(), out tryport))             {                 messagebox.show("you must enter ip address, port, , nickname connect server.", "missing info");                 return;             }             this.hide();             cw.show();             cw.connectto(ipbox.text, int.parse(portbox.text), nicknamebox.text);         }         catch(exception otheree) {             messagebox.show("error:\n\n" + otheree.tostring(),"error connecting...");             cw.hide();             this.show();         }     }      private void hostbutton_click(object sender, eventargs e)     {         int tryport = 0;         if (portbox.text == "" || nicknamebox.text == "" || !int.tryparse(portbox.text.tostring(), out tryport)) {             messagebox.show("you must enter port , nickname host server.", "missing info");             return;         }         startlistening();     }      public void startlistening()     {         try         {             this.ishost = true;                                                         //we're hosting server             cw.callingform = this;                                                      //give chatform login form (this) [that acts server]             cw.show();                                                                  //show chatform             cw.ishost = true;                                                           //tell chatform host (for display purposes)             this.hide();                                                                //and hide login form             serversocket.bind(new ipendpoint(ipaddress.any, int.parse(portbox.text)));  //bind our local address             serversocket.listen(1);                                                     //and start listening             serversocket.beginaccept(new asynccallback(acceptcallback), null);          //when connects, begin async callback             cw.connectto("127.0.0.1", int.parse(portbox.text), nicknamebox.text);       //and have chatform connect server         }         catch (exception) {}     }      public void acceptcallback(iasyncresult ar)     {         try         {             stateobject state = new stateobject();             state.worksocket = serversocket.endaccept(ar);             socketlist.add(state.worksocket);             state.worksocket.beginreceive(state.buffer, 0, stateobject.buffersize, socketflags.none, new asynccallback(receivecallback), state);             serversocket.beginaccept(new asynccallback(acceptcallback), null);         }         catch (exception) {}     }      public void receivecallback(iasyncresult ar)     {         try         {             if (isclosing)                 return;              stateobject state = (stateobject)ar.asyncstate;             socket s = state.worksocket;             string content = "";             int received = s.endreceive(ar);              if(received > 0)                 state.sb.append(encoding.ascii.getstring(state.buffer, 0, received));              content = state.sb.tostring();              if (content.indexof(environment.newline) > -1) //if we've received end of message             {                  if (content.startswith("!!addlist") && state.newconnection)                 {                     state.newconnection = false;                     content = content.replace("!!addlist", "");                     string usernick = content.trim();                     if (ishost && usernick.startswith("!"))                         usernick = usernick.replace("!", "");                     usernick = usernick.trim();                     if (usernick.startswith("!") || usernick == string.empty || usernames.items.indexof(usernick) > -1)                     {                         //invalid username :c dropped                         s.send(encoding.ascii.getbytes("invalid username/in use - sorry :("));                         s.shutdown(socketshutdown.both);                         s.disconnect(false);                         s.close();                         socketlist.remove(s);                         return;                     }                     usernames.items.add(usernick);                     foreach (string name in usernames.items)                     {                         if (name.indexof(usernick) < 0)                         {                             s.send(encoding.ascii.getbytes("!!addlist " + name + "\r\n"));                             thread.sleep(10); //such hack... ugh annoys me works                         }                     }                     foreach (socket client in socketlist)                     {                         try                         {                             if (client != s)                                 client.send(encoding.ascii.getbytes("!!addlist " + usernick + "\r\n"));                         }                         catch (exception) { }                     }                 }                 else if (content.startswith("!!removelist") && !state.newconnection)                 {                     content = content.replace("!!removelist", "");                     string usernick = content.trim();                     usernames.items.remove(usernick);                     foreach (socket client in socketlist)                     {                         try                         {                             if (client != s)                                 client.send(encoding.ascii.getbytes("!!removelist " + usernick + "\r\n"));                         }                         catch (exception) { }                     }                 }                 else if (state.newconnection) //if don't give name , try send data, drop.                 {                     s.shutdown(socketshutdown.both);                     s.disconnect(false);                     s.close();                     socketlist.remove(s);                     return;                 }                 else                 {                     foreach (socket client in socketlist)                     {                         try                         {                             if (client != s)                                 client.send(system.text.encoding.ascii.getbytes(content));                         }                         catch (exception) { }                     }                 }             }             array.clear(state.buffer, 0, stateobject.buffersize);             state.sb.clear();             s.beginreceive(state.buffer, 0, stateobject.buffersize, 0, new asynccallback(receivecallback), state);         }         catch (exception) {             socketlist.remove(((stateobject)ar.asyncstate).worksocket);         }     }     public void sendcallback(iasyncresult ar)     {         try         {             stateobject state = (stateobject)ar.asyncstate;             state.worksocket.endsend(ar);         }         catch (exception) {}     }     private void login_formclosed(object sender, formclosedeventargs e)     {         try         {             this.isclosing = true;             if (this.ishost)             {                 foreach (socket c in socketlist)                 {                     if (c.connected)                     {                         c.close();                     }                 }                 serversocket.shutdown(socketshutdown.both);                 serversocket.close();                 serversocket = null;                 serversocket.dispose();             }             socketlist.clear();         }         catch (exception) { }                 {             application.exit();         }     } } public class stateobject {     public socket worksocket = null;     public const int buffersize = 1024;     public byte[] buffer = new byte[buffersize];     public stringbuilder sb = new stringbuilder();     public bool newconnection = true; } 

the client code (work in progress):

public partial class chatwindow : form {     private socket clientsocket;     private thread chatthread;     private string ipaddress;     private int port;     private bool isconnected;     private string nickname;     public bool ishost;     public login callingform;      private static object conlock = new object();      public chatwindow()     {         initializecomponent();         isconnected = false;         ishost = false;     }      public string getip() {         return dns.gethostentry(dns.gethostname()).addresslist.firstordefault(ip => ip.addressfamily == addressfamily.internetwork).tostring();     }      public void displayerror(string err)     {         output(environment.newline + environment.newline + err + environment.newline);     }      public void op(string s)     {         try         {             lock (conlock)             {                 chatbox.text += s;             }         }         catch (exception) { }     }      public void connectto(string ip, int p, string n) {         try         {             this.text = "trying connect " + ip + ":" + p + "...";             this.ipaddress = ip;             this.port = p;             this.nickname = n;              clientsocket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);              if (!ishost)             {                 op("connecting " + ipaddress + ":" + port + "...");             }             else             {                 output("listening on " + getip() + ":" + port + "...");             }              clientsocket.connect(ipaddress, port);              isconnected = true;              if (!ishost)             {                 this.text = "connected " + ipaddress + ":" + port + " - nickname: " + nickname;                 output("connected!");             }             else             {                 this.text = "hosting on " + getip() + ":" + port + " - nickname: " + nickname;             }              chatthread = new thread(new threadstart(getdata));             chatthread.start();              nickname = nickname.replace(" ", "");             nickname = nickname.replace(":", "");             if(nickname.startswith("!"))                 nickname = nickname.replace("!", "");             namesbox.items.add(nickname);              sendraw("!!addlist " + nickname);         }         catch (threadabortexception)         {             //do nothing; closing chat window         }         catch (exception e)         {             if (!isconnected)             {                 this.hide();                 callingform.show();                 cleartext();                 messagebox.show("error:\n\n" + e.tostring(), "error connecting remote host");             }         }     }      public void removenick(string n)     {         if (namesbox.items.count <= 0)             return;         (int x = namesbox.items.count - 1; x >= 0; --x)             if (namesbox.items[x].tostring().contains(n))                 namesbox.items.removeat(x);     }      public void cleartext()     {         try         {             lock (conlock)             {                 chatbox.text = "";             }         }         catch (exception) { }     }      public void addnick(string n)     {         if (n.contains(" ")) //no spaces... such headache             return;         if (n.contains(":"))             return;         bool shouldadd = true;         n = n.trim();         (int x = namesbox.items.count - 1; x >= 0; --x)             if (namesbox.items[x].tostring().contains(n))                 shouldadd = false;         if (shouldadd)         {             namesbox.items.add(n);             output("someone new joined room: " + n);             //sendraw("!!addlist " + nickname);         }     }      public void addnicknomessage(string n)     {         if (n.contains(" ")) //no spaces... such headache             return;         if (n.contains(":"))             return;         bool shouldadd = true;         n = n.trim();         (int x = namesbox.items.count - 1; x >= 0; --x)             if (namesbox.items[x].tostring().contains(n))                 shouldadd = false;         if (shouldadd)         {             namesbox.items.add(n);             //sendraw("!!addlist " + nickname);         }     }      public void getdata()     {         try         {             byte[] buf = new byte[1024];             string message = "";             while(isconnected)             {                 array.clear(buf, 0, buf.length);                 message = "";                 int gotdata = clientsocket.receive(buf, buf.length, socketflags.none);                 if (gotdata == 0)                     throw new exception("i swear, working before isn't anymore...");                 message = encoding.ascii.getstring(buf);                 if (message.startswith("!!addlist"))                 {                     message = message.replace("!!addlist", "");                     string usernick = message.trim();                     if(!namesbox.items.contains(usernick))                     {                         addnick(usernick);                     }                     continue;                 }                 else if (message.startswith("!!removelist"))                 {                     message = message.replace("!!removelist", "");                     string usernick = message.trim();                     removenick(usernick);                     output("someone left room: " + usernick);                     continue;                 }                 output(message);             }         }         catch (exception)         {             isconnected = false;             output(environment.newline + "connection server lost.");         }     }      public void output(string s)     {         try         {             lock (conlock)             {                 chatbox.text += s + environment.newline;             }         }         catch (exception) { }     }      private void chatwindow_formclosed(object sender, formclosedeventargs e)     {         try         {             if(isconnected)                 sendraw("!!removelist " + nickname);             isconnected = false;             clientsocket.shutdown(socketshutdown.receive);             if (chatthread.isalive)                 chatthread.abort();             callingform.close();         }         catch (exception) { }     }      private void sendbutton_click(object sender, eventargs e)     {         if(isconnected)             send(sendbox.text);     }      private void sendbox_keyup(object sender, keyeventargs e)     {         if (e.keycode == keys.enter)         {             if (isconnected)             {                 if (sendbox.text != "")                 {                     send(sendbox.text);                     sendbox.selectall();                     e.suppresskeypress = true;                     e.handled = true;                 }             }         }     }      private void send(string t) {         try         {             byte[] data = system.text.encoding.ascii.getbytes(nickname + ": " + t + "\r\n");             clientsocket.send(data);             output(nickname + ": " + t);         }         catch (exception e)         {             displayerror(e.tostring());         }     }      private void sendraw(string t)     {         try         {             byte[] data = system.text.encoding.ascii.getbytes(t + "\r\n");             clientsocket.send(data);         }         catch (exception e)         {             displayerror(e.tostring());         }     }      private void chatbox_textchanged(object sender, eventargs e)     {         chatbox.selectionstart = chatbox.text.length;         chatbox.scrolltocaret();     }      private void sendbox_keydown(object sender, keyeventargs e)     {         if (e.keycode == keys.enter)             e.suppresskeypress = true;     } } 

to do:

add invokes, more delegates, more qa , find out breaks it. also, believe there's still possibility of packet loss due client addlist functions being in read loop. believe why "crappy hack" using thread.sleep(10) in server callback name population issue.

i think might better either pass command off thread while continuing read or have client tell server it's ready name.

otherwise, there might data loss during name updates.

the other thing that, said in comments above, delegates should used when updating ui objects (chatbox , listbox). wrote code these removed because there no noticeable change , wanted keep simple.

i still use object lock when outputting text chatbox, there's no noticeable difference there.

the code should added not using delegates potentially problematic, literally caught chat box in infinite loop of updates without issue.

i tried breaking telnet , successful added newconnection property stateobject ensure each client can send "!!addlist" once.

there are, of course, other ways abuse server creating client joins , leaves repeatedly, end passing !!removelist handling server instead of leaving client.


Comments

Popular posts from this blog

python - Subclassed QStyledItemDelegate ignores Stylesheet -

java - HttpClient 3.1 Connection pooling vs HttpClient 4.3.2 -

SQL: Divide the sum of values in one table with the count of rows in another -