multithreading - C# Issues with Multi-Threading and Socket -
first of all, want let know not new programming, should easier me :)
i having issues multi-threaded chat making in c# socket.
i have 3 threads :
- void listensocketconnection : check socket connect. connected socket added list<>
- void checkifclientstillconnectedthread : check if socket disconnected. disconnected socket removed list<>
- void receivedatalistener : check if socket received data
- here issue. if first or second thread remove socket list<>, 'foreach (clientmanager cmanager in clientslist)' raise exception.
- here second issue. if socket disconnect during foreach, 'foreach clientmanager cmanager in clientslist)' raise exception : disposedexception
do have tips on how fix this?
here code :
using system; using system.collections.generic; using system.linq; using system.text; using system.threading.tasks; using system.net; using system.net.sockets; using system.componentmodel; using system.threading; namespace jachat.library { class socketserver { private socket socketserver; private backgroundworker bwsocketconnectlistener; private backgroundworker bwcheckifconnected; private backgroundworker bwreceivedatalistener; private list<clientmanager> clientslist; #region constructor public socketserver(int port) { clientslist = new list<clientmanager>(); socketserver = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); socketserver.bind(new ipendpoint(ipaddress.any, port)); socketserver.listen(100); bwsocketconnectlistener = new backgroundworker(); bwsocketconnectlistener.dowork += new doworkeventhandler(listensocketconnection); bwsocketconnectlistener.runworkerasync(); bwcheckifconnected = new backgroundworker(); bwcheckifconnected.dowork += checkifclientstillconnectedthread; bwcheckifconnected.runworkerasync(); bwreceivedatalistener = new backgroundworker(); bwreceivedatalistener.dowork += receivedatalistener; bwreceivedatalistener.runworkerasync(); } #endregion #region getter public list<clientmanager> connectedclients { { return clientslist; } } #endregion #region public methods /// <summary> /// parse , send command object targets /// </summary> public void sendcommand(command cmd) { backgroundworker test = new backgroundworker(); test.dowork += delegate { foreach(clientmanager cmanager in clientslist){ cmanager.sendcommand(cmd); } }; test.runworkerasync(); } /// <summary> /// disconnect , close socket /// </summary> public void disconnect() { socketserver.disconnect(false); socketserver.close(); socketserver = null; //stop background worker } #endregion #region private methods private void listensocketconnection(object sender, doworkeventargs e) { while (socketserver != null) { //get , wait new connection clientmanager newclientmanager = new clientmanager(socketserver.accept()); clientslist.add(newclientmanager); onclientconnect.invoke(newclientmanager); } } private void checkifclientstillconnectedthread(object sender, doworkeventargs e){ while(socketserver != null){ for(int i=0;i<clientslist.count;i++){ if(clientslist[i].socket.poll(10,selectmode.selectread) && clientslist[i].socket.available==0){ clientslist[i].socket.close(); onclientdisconnect.invoke(clientslist[i]); clientslist.remove(clientslist[i]); i--; } } thread.sleep(5); } } private void receivedatalistener(object unused1, doworkeventargs unused2){ while (socketserver != null){ foreach (clientmanager cmanager in clientslist) { try { if (cmanager.socket.available > 0) { console.writeline("receive data listener 0"); //read command's type. byte[] buffer = new byte[4]; int readbytes = cmanager.socket.receive(buffer, 0, 4, socketflags.none); console.writeline("receive data listener 1"); if (readbytes == 0) break; console.writeline("receive data listener 2"); commandtype cmdtype = (commandtype)(bitconverter.toint32(buffer, 0)); console.writeline("receive data listener 3"); //read sender ip size. buffer = new byte[4]; readbytes = cmanager.socket.receive(buffer, 0, 4, socketflags.none); if (readbytes == 0) break; int senderipsize = bitconverter.toint32(buffer, 0); //read sender ip. buffer = new byte[senderipsize]; readbytes = cmanager.socket.receive(buffer, 0, senderipsize, socketflags.none); if (readbytes == 0) break; ipaddress cmdsenderip = ipaddress.parse(system.text.encoding.ascii.getstring(buffer)); //read sender name size. buffer = new byte[4]; readbytes = cmanager.socket.receive(buffer, 0, 4, socketflags.none); if (readbytes == 0) break; int sendernamesize = bitconverter.toint32(buffer, 0); //read sender name. buffer = new byte[sendernamesize]; readbytes = cmanager.socket.receive(buffer, 0, sendernamesize, socketflags.none); if (readbytes == 0) break; string cmdsendername = system.text.encoding.unicode.getstring(buffer); //read target ip size. string cmdtarget = ""; buffer = new byte[4]; readbytes = cmanager.socket.receive(buffer, 0, 4, socketflags.none); if (readbytes == 0) break; int targetipsize = bitconverter.toint32(buffer, 0); //read command's target. buffer = new byte[targetipsize]; readbytes = cmanager.socket.receive(buffer, 0, targetipsize, socketflags.none); if (readbytes == 0) break; cmdtarget = system.text.encoding.ascii.getstring(buffer); //read command's metadata size. string cmdmetadata = ""; buffer = new byte[4]; readbytes = cmanager.socket.receive(buffer, 0, 4, socketflags.none); if (readbytes == 0) break; int metadatasize = bitconverter.toint32(buffer, 0); //read command's meta data. buffer = new byte[metadatasize]; readbytes = cmanager.socket.receive(buffer, 0, metadatasize, socketflags.none); if (readbytes == 0) break; cmdmetadata = system.text.encoding.unicode.getstring(buffer); //create command object command cmd = new command(cmdtype, cmdsenderip, cmdsendername, ipaddress.parse(cmdtarget), cmdmetadata); this.oncommandreceived(cmd); } } catch (objectdisposedexception) {/*le socket s'est déconnectée pendant le each. ignore l'érreur et retourne dans le while*/ } catch (invalidoperationexception) { /* clientslist été modifié pendant le foreach et délanche une exception. retour while*/} } } console.writeline("receive data listener closed"); } #endregion #region events public delegate void onclientconnecteventhandler(clientmanager client); /// <summary> /// events invoked when client connect server /// </summary> public event onclientconnecteventhandler onclientconnect = delegate { }; public delegate void onclientdisconnecteventhandler(clientmanager client); /// <summary> /// events invoked when client disconnect server /// </summary> public event onclientdisconnecteventhandler onclientdisconnect = delegate { }; public delegate void oncommandreceivedeventhandler(command cmd); /// <summary> /// events invoked when command has been sent server /// </summary> public event oncommandreceivedeventhandler oncommandreceived = delegate { }; #endregion } }
- there multiple threads don't see synchronization. that's incorrect. use lock protect mutable shared state.
- instead of having central management of sockets, polling , checking of
dataavailable, why don't use either 1 thread per socket or async io? way ever manage 1 socket @ time. no polling necessary. callread(or async version) , wait data arrive. better paradigm deal sockets. basically, if socket code containsdataavailableor polling, going against best-practices (and have bug somewhere). think how solve without using two. possible, , better. - in
receivedatalistenerassume, if data available entire message available. that's wrong because tcp stream-oriented. can receive sent data in arbitrarily small chunks. going point (2) fixes this.
to elaborate on (2): actor model. 1 actor per socket. whether implement actor using thread, using async/await or using legacy async io not matter.
hope helps. feel free ask follow-up questions in comments below.
Comments
Post a Comment