i want make chat. server made in console app , client made in winforms.
in client write nickname , connect server. server receives name client. add clients connect server in dictionary list (string)name , (tcpclient)socket. after, want send every client client list.
when debug on server, sockets appear dualmode,enablebroadcast error. in client when have receive list stops , doesn't anything.
server
namespace myserver { class myserver { public dictionary<string, tcpclient> clientlist = new dictionary<string, tcpclient>(); tcplistener server = null; networkstream stream = null; streamreader streamreader = null; streamwriter streamwriter = null; tcpclient clientsocket; string messagereceived; int number_clients = 0; public myserver(tcpclient clientsocket_connect) { stream = clientsocket_connect.getstream(); streamreader = new streamreader(stream); streamwriter = new streamwriter(stream); receivemessage(clientsocket_connect); // receive messages } public myserver() { thread thread = new thread(new threadstart(run)); thread.start(); } public void receivemessage(tcpclient client_socket) { messagereceived = streamreader.readline(); if (messagereceived.substring(messagereceived.length - 4) == "user") { string name = messagereceived.substring(0, messagereceived.length - 4); bool found = false; foreach (var namefound in clientlist.keys) { if (namefound == name) { found = true; streamwriter.writeline("the user exists"); streamwriter.flush(); } } if (!found) { //show who's connected console.writeline(name + " online"); number_clients++; clientlist.add(name, client_socket); //send client clientlist string send = null; foreach (var key in clientlist.keys) { send += key + "."; } foreach (var value in clientlist.values) { tcpclient trimitereclientsocket = value; if (trimitereclientsocket != null) { networkstream networkstream = trimitereclientsocket.getstream(); streamwriter networkwriter = new streamwriter(networkstream); networkwriter.writeline(send + "connected"); networkwriter.flush(); } } } } } void run() { ipaddress ipaddress = ipaddress.parse("127.0.0.1"); server = new tcplistener(ipaddress, 8000); server.start(); console.writeline("server started!"); while (true) { clientsocket = server.accepttcpclient(); new myserver(clientsocket); } } } static void main(string[] args) { myserver server = new myserver(); } }
client
namespace myclient { class myclient { list<string> clientlist = new list<string>(); tcpclient client = null; networkstream stream = nul l; streamreader streamreader = null; streamwriter streamwriter = null; bool connected; string received_message; public myclient() { client = new tcpclient("127.0.0.1", 8000); stream = client.getstream(); streamreader = new streamreader(stream); streamwriter = new streamwriter(stream); } public void sendclientname(string name) { streamwriter.writeline(convert.tostring(name)); streamwriter.flush(); } public list<clientname> receiveclientlist() { list<clientname> val = new list<clientname>(); string name = convert.tostring(streamreader.readline()); if (name.substring(0, name.length - 9) == "connected") { clientname client = new clientname(); client.nume = name; val.add(client); } return val; } } }
client form
public partial class form1 : form { myclient client = new myclient(); public form1() { initializecomponent(); thread receiveclients = new thread(new threadstart(getmessages)); } private void btnconnect_click(object sender, eventargs e) { client.sendclientname(txtnickname.text + "user"); } public void getmessages() { while (true) { lbclientsconnected.items.add(client.receiveclientlist()); } } }
i unable reproduce error when running code. don't know mean "the sockets appear dualmode,enablebroadcast error". said, there number of fixable problems code, including pertain directly concern "when have receive list stops , doesn't anything."
probably biggest issue code never start client's receiving thread. need call start()
method on thread
object after it's been created:
public form1() { initializecomponent(); thread receiveclients = new thread(new threadstart(getmessages)); // receiving thread needs started receiveclients.start(); }
now, fixed, have few other problems. next big issue parsing received text incorrectly. in code, should looking text "connected"
@ end of string, instead extract other part of text (with list of client names).
your receiveclientlist()
method should instead this:
private const string _kconnected = "connected"; public list<string> receiveclientlist() { list<string> val = new list<string>(); string name = convert.tostring(streamreader.readline()); // need check *end* of string "connected" text, // not beginning. if (name.endswith(_kconnected)) { name = name.substring(0, name.length - _kconnected.length); val.add(name); } return val; }
(you didn't share clientname
class in question, , example doesn't need it; simple string
value suffices purpose of exercise. also, i've introduced const string
named _kconnected
, ensure string literal used correctly in each place it's needed, simplify usage.)
but 2 issues fixed, you've still got couple in form
code handle return value of receive method. first, passing list<t>
object returned receive method listbox.items.add()
method, result in listbox
displaying type name object, rather elements.
second, because code executing in thread other ui thread owns listbox
object, must wrap call in call control.invoke()
. otherwise, you'll cross-thread operation exception.
fixing 2 issues, this:
public void getmessages() { while (true) { // need receive data, , call invoke() add // data listbox. also, if adding list<t>, need call // addrange(), not add(). string[] receivedclientlist = client.receiveclientlist().toarray(); invoke((methodinvoker)(() => listbox1.items.addrange(receivedclientlist))); }
with changes, code process message sent client, , return list of clients. should further along. said, still have number of other problems, including fundamental ones:
the biggest issue when accept connection in server, create whole new server object handle connection. there number of reasons isn't idea, main 1 rest of code seems conceptually assume single server object tracking of clients, each connection result in own collection of client objects, each collection having 1 member (i.e. client).
note once you've fixed issue, have multiple threads accessing single dictionary data structure. need learn how uselock
statement ensure safe shared use of dictionary across multiple threads.another significant problem instead of using
streamwriter
created when first accepted connection, create whole newstreamwriter
object (referenced in local variable namednetworkwriter
) write socket. in simple example, works fine, between buffering , lack of thread safety, incorrectly-designed code have serious data corruption problems.less problematic, worth fixing, server code fails take advantage of fact you're storing clients in dictionary, .net has useful helper functions doing things joining bunch of strings together. write server's
receivemessage()
method more this:
private const string _kuser = "user"; public void receivemessage(tcpclient client_socket) { messagereceived = streamreader.readline(); if (messagereceived.endswith(_kuser)) { string name = messagereceived.substring(0, messagereceived.length - _kuser.length); if (clientlist.containskey(name)) { streamwriter.writeline("the user exists"); streamwriter.flush(); return; } //show who's connected console.writeline(name + " online"); number_clients++; clientlist.add(name, client_socket); string send = string.join(".", clientlist.keys); foreach (var value in clientlist.values.where(v => v != null)) { // note: didn't change problem noted in #2 above, instead // left code way had it, mostly. of course, in // corrected version of code, dictionary contain not // `tcpclient` objects, client-specific object specific // server implementation, in `tcpclient` object // found, along `streamreader` , `streamwriter` objects // you've created connection (and other per-client // data need track). write already- // existing `streamwriter` object instead of creating new 1 each // time here. networkstream networkstream = value.getstream(); streamwriter networkwriter = new streamwriter(networkstream); networkwriter.writeline(send + "connected"); networkwriter.flush(); } } }
the above not exhaustive means. frankly, should spend more time looking @ existing examples of network-aware code, e.g. on msdn , stack overflow, on tutorials on web sites, blogs, or in books. when write server in one-thread-per-connection way seem trying here, there lots of little details need correct, , haven't far.
but hope above enough past current hurdle, , on next big problem(s). :)
Comments
Post a Comment