| Register | FAQ | Calendar | Search | Today's Posts | Mark Forums Read |
|
#1
| |||
| |||
| I wrote a little UDP-based chat program. It's not very user-friendly, requiring every user to add every other user to a list. Anyway, I want to adapt it to TCP so you don't have to do that, as well as the ability to talk to people behind firewalls without reconfiguring them. The first problem I have is how to have the server application relay messages to all the clients. The other problem is how to format the output on the client application so that it can show what other people are saying as well as having a prompt at the bottom. Currently I have 2 seperate apps, one that listens for incoming messages, and one to send messages, but I don't think that'd work with TCP. The UDP app is available for DL here, if anyone is interested. http://sodasphere.us.to/pub/yipee |
|
#2
| |||
| |||
| phider1 said: > I wrote a little UDP-based chat program. It's not very user-friendly, > requiring every user to add every other user to a list. Anyway, I > want to adapt it to TCP so you don't have to do that, as well as the > ability to talk to people behind firewalls without reconfiguring them. > The first problem I have is how to have the server application relay > messages to all the clients. The other problem is how to format the > output on the client application so that it can show what other people > are saying as well as having a prompt at the bottom. Currently I have > 2 seperate apps, one that listens for incoming messages, and one to > send messages, but I don't think that'd work with TCP. No doubt you'll get lots of complicated answers, and no doubt they'll be good answers. Me? I'm simple, me. I'd do it like this: 1) the server In one way, this is simpler than the client, because you don't have to worry about a GUI if you don't want to. It is *not* my purpose here to teach you how to program TCP sockets in any kind of detail; rather, I'll just touch on what I consider a nice simple design that doesn't involve threads or anything scary like that. Ingredients (a) a listening socket (b) two circular lists, LOGINLIST and CHATLIST Method do all the initialisation work for the listening socket while not done select on the listening socket, with a delay of 0 - it'll come right back to you, telling you yay or nay if there's an incoming connection request create a new entry in the LOGINLIST, which will contain the new socket for the connection, any login data, and anything else you need to record for this user endif select on all the login sockets for up to L entries in the LOGINLIST that have messages waiting perform one step in your login protocol (e.g. get a user name, or get a password, or reject a password, or whatever it is) for this user, such that the /next/ step (if any) is up to the user if this step completes the login process remove this item from LOGINLIST add this item to CHATLIST endif rotate the list one place endfor select on all the chat sockets for up to C entries in the CHATLIST that have messages waiting receive the message from THISUSER if this is a chat message while the current user's *successor* is not THISUSER send out the message to the current user in the CHATLIST rotate the CHATLIST one place endwhile else deal with the message as appropriate (eg PRIVMSG, LOGOFF, or whatever) endif rotate the CHATLIST one place endfor endwhile This is about as simple as it gets, and doesn't require you to have an outlandish number of threads going - in fact, it's single-threaded and non-blocking. The "while not done" thing can be done using signal() to intercept Ctrl-C and set a volatile sig_atomic_t to 1: volatile sig_atomic_t done; void term(int sig) { done = 1; } Register that function for handling SIGINTs, using signal(). Then your main loop is simply: while(!done) { all that stuff I said } cleanup goes here - shutting down the listening socket, releasing dynamically allocated memory, etc. 2) the client The client is easier on the socket side, but harder on the interface side. On Win32, the fast-n-dirty solution would be to use the console functions (look up something like AllocConsole in MSDN, and then browse around that family of functions - you should soon work it out). On Linux, probably curses - or rather, ncurses. If you can find an ncurses for Windows, you're laughing. The point is that either of these techniques will allow you to address the screen at a point of your choosing. This allows you to have a chat log (typically a very large area) and a separate input zone (typically a single line at the bottom), and the output messages won't have to choose between waiting till you're done or corrupting your typing line. Some clients also have other stuff in other screen areas - e.g. user name lists, channel lists, etc. The comms design is easy enough: do the whole connection thing while not done select on the server socket if there's a message waiting on the server get the message if it's a chat message stick it in the chatlog zone else deal with it appropriately endif endif if the user just typed a line wrap it up as and if necessary, and send it to the server clear the input zone endif endwhile cleanup Note: I took a quick look at your source to find out which platforms you're interested in, and I saw this: scanf("%s",name); That is so 1988. Nowadays, we protect our buffers, by using a line-reading buffer-protecting routine such as fgets, and then parsing it as necessary. If you don't want to do that, at the very least you should tell scanf your maximum buffer size, e.g. scanf("%11s", name); for your 12-byte buffer. Note, too, that scanf will return the number of fields successfully converted. If it's fewer than you asked for, that's obviously a problem, and one that you can detect and correct for. Incidentally, you have a WSACleanup call in your Linux send code. :-) -- Richard Heathfield <http://www.cpax.org.uk> Email: -http://www. +rjh@ Google users: <http://www.cpax.org.uk/prg/writings/googly.php> "Usenet is a strange place" - dmr 29 July 1999 |
|
#3
| |||
| |||
| gah... That's really long and complex. What if I were to say that my server didn't require any sort of login? All it has to do is know who's connected at the time and relay messages. It also needs to be able to return a list of users (eg. when a user types /who or something like that). |
|
#4
| |||
| |||
| > Note: I took a quick look at your source to find out which platforms you're > interested in, and I saw this: > > * * * * scanf("%s",name); > > That is so 1988. Nowadays, we protect our buffers, by using a line-reading > buffer-protecting routine such as fgets, and then parsing it as necessary.. > If you don't want to do that, at the very least you should tell scanf your > maximum buffer size, e.g. scanf("%11s", name); for your 12-byte buffer. Thanks. I've seen mention of scanf's maximum buffer size, but didn't know the syntax. > Incidentally, you have a WSACleanup call in your Linux send code. :-) Oops... I'll go ahead and fix that now :P |
|
#5
| |||
| |||
| phider1 said: > gah... That's really long and complex. No, it isn't - really! > What if I were to say that my server didn't require any sort of login? Then I'd say you're asking for trouble (unless you're on a LAN with no Internet connectivity), and it would simply mean that the acceptance of new users would be a little smoother (at least, until the crackers found your server (unless you're on a LAN with no Internet connectivity)). > All it has to do is know > who's connected at the time Yes - and the process of connecting is normally called "logging in". > and relay messages. It also needs to be > able to return a list of users (eg. when a user types /who or > something like that). That's one of the many uses for the CHATLIST circular list - you can easily enquire it to find out who is logged... sorry! To find out who is connected. -- Richard Heathfield <http://www.cpax.org.uk> Email: -http://www. +rjh@ Google users: <http://www.cpax.org.uk/prg/writings/googly.php> "Usenet is a strange place" - dmr 29 July 1999 |
|
#6
| |||
| |||
| Really, my plan for this program right now is just to give it to friends and play around with it. Anyway, I'll try reading through it again and see if I can understand it all... |
|
#7
| |||
| |||
| In article <EbOdnbnRWPSEcijVnZ2dnUVZ8judnZ2d@bt.com>, Richard Heathfield <rjh@see.sig.invalid> wrote: >Note: I took a quick look at your source to find out which platforms you're >interested in, and I saw this: > > scanf("%s",name); > >That is so 1988. Nowadays, we protect our buffers, by using a line-reading >buffer-protecting routine such as fgets, and then parsing it as necessary. >If you don't want to do that, at the very least you should tell scanf your >maximum buffer size, e.g. scanf("%11s", name); for your 12-byte buffer. That should be ``scanf("%.11s", name);'' (note the dot) |
|
#8
| |||
| |||
| In article <1224434866.31958.0@proxy01.news.clara.net>, Ike Naar <ike@localhost.claranet.nl> wrote: >In article <EbOdnbnRWPSEcijVnZ2dnUVZ8judnZ2d@bt.com>, >Richard Heathfield <rjh@see.sig.invalid> wrote: >>Note: I took a quick look at your source to find out which platforms you're >>interested in, and I saw this: >> >> scanf("%s",name); >> >>That is so 1988. Nowadays, we protect our buffers, by using a line-reading >>buffer-protecting routine such as fgets, and then parsing it as necessary. >>If you don't want to do that, at the very least you should tell scanf your >>maximum buffer size, e.g. scanf("%11s", name); for your 12-byte buffer. > >That should be ``scanf("%.11s", name);'' (note the dot) Whoops! Actually, Richard was right. The dot should not be there. (I was confusing scanf format conversion specifiers with those for printf) Regards, Ike |
![]() |
| Thread Tools | |
| Display Modes | |
In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.