Simple game server doubt
Hi, this will be a very noob question, so I'm sorry for that. But I'm having doubts about a simple game server on Linux (which, by the way, I'm new to).
Consider a small (less than a thousand clients) game server for a simple round-based strategical game, where clients can connect to a main room, create a game room and then start the game when all the partecipats are ready.
The server has a list of connected clients with some details, such as name, ID, status, etc. The name for the client is asked when the client connects, so that I'll be able to implement a login/register mechanism in the future, but for now I'm not even checking for duplicate names and there is no password whatsoever.
My doubt is whether to use processes or threads. I mean, for a game I'm pretty sure that a separate process fits best, since it'll be a completely separate thing from the main server. But how to handle the main room? (you can think of it as a main chat room, where instead of text messages, clients can only send few commands)
Thanks for any help or advice or links.
I'm writing a chat program right now, where the "client side" is web based. However, I'm only interested in one room, so a single server process with say half a dozen clients.
For the server, I don't think you need threads and you probably don't need much in the way of multiple processes either. Processing messages happens extremely quickly -- the "bottleneck" is the transmission time, and not within the server activity itself. So if you just use one active server per room, it can easily iterate thru all incoming connections and deal with them one a time with no delay. Threading the connections will only create a totally unnecessary complication IMO. Perhaps this is especially true of a turn based game.
You can use a loop with select() to deal with all the connected clients plus, if desired, an additional socket to accept() further clients. It is also easy to accept(), read/write, then disconnect all calls at every iteration without taking up any time. In other words, you do not even need to keep the connections if for some reason you find this suits your purposes better.
Vis, the main room vs. the various game rooms, I would just write two completely seperate and different servers for this, with the main room server starting a game room server and passing off the connection as appropriate. So perhaps the main room server loop would not have persistent clients (as described in the last paragraph) whereas the game rooms would.
Hi again! I'm using the same thread though the question is different, yet related to the topic.
The question this time involves a double connection between client and server. After implementing the basic game server I found the adding also the chat functionality was giving me some troubles, so I've decided to separate the channels (or connections, as you prefer), and thinking about that I've come up with three possibilities:
- giving both sides two channels where one is for writing and the other one is for reading (indipendently from what they read or write),
- giving both sides two channels where one is for exchanging "system messages" (such as commands and replies to them), and the other one is dedicated to the chat,
- using only one channel with formatted messages (for instance chat messages could be formatted "chat <message>", ora a system one could be "syscmd <cmd> *<params>")
Which one you think is better?
Right now I've implemented an hybrid solution mixing the 1st and the 2nd point of the list (and the reason is that I hadn't the problem clear enough in the beginning), which works fine by the way, but isn't the best choice in terms of organization, clarity, and flexibility for future development.
EDIT: one more thing, right now I'm reading one char at a time from the chat socket using a loop and checking for the return char '\n'. I'm doing this because when reading the whole message and then printing it using 'printf', it didn't stop at the termination char '\0' but continued printing also characthers after it (if the previous message was longer than the actual one). Since I'm using a fixed buffer each time, I know that the buffer would overwrite the old message with the new one, but shouldn't the 'printf' function print the string up to the first termination char (which I'm sure it is sent and read correctly)??? Why it is printing the whole content of the buffer instead?
Thx in advance!
ps: thx MK27 for your reply. I've proceeded using threads in the end. The main server process listens for and establishes the connections, and then fires up a thread for each client. For clients I'm using a select() on the chat socket (which is read only) and stdin.
Hey. Actually I've gone with threads too, albeit in a different way, because I want to maintain a continuous ping from one particular client to server at all times. (The one particular client controls the server remotely).
Vis multiple channels, it's a fine idea, but here are some thoughts about what you can do with one: At the server end, since I'm only using one channel per client, the ping can get mixed in with the other content (since the transmissions from that one client are asychronous/threaded, and automated), but the "ping" is a unique sequence ("*THIS!PING*") and easy to remove from a string -- note the socket layer CANNOT keep the messages discrete (if the client transmits "hello" at the same time as it's pinging thread pings, the server recieves "hello*THIS!PING*"). But removing a substr works fine here.
The other technique that I've been using is to use the first byte as a signifier. In other words, after the pings are discarded, I use a switch/case on message. 'M' might indicate this is a normal text message; the M is appended prior to transmission. 'L' might indicate login data, 'S' might be some kind of command, 'D' might be non text data. This is also fine and easy, so your "formatted messages" idea should be as well.
However, since my chat is web based, there is a central location to store the text of the chat in for all normal clients, that is probably not the case if you are using an independent interface and simply routing the messages. Still: I do not think using a separate send and receive channel is a necessary complication, so think hard before you resort to that.
Your problem with '\0' is probably because the send() and recv() functions are not string functions, they do not append a null terminator. You need to check and make sure you are doing so (probably replace the '\n' before transmission; I know you say you are sure that is the case but obviously, a printf %s is a C string and does end at '\0', so there is no 0 -- it cannot overshoot mysteriously). Failing that, you could expand the first byte signifier this way: if M, the next four bytes are an integer (or, an integer as a string; in the first case you just cast/assign the bytes to an int, in the second you sscanf("%d")) representing the length of the message. I've used that with data, where there are no possible terminating bytes since 0-254 are used, this is a common practice AFAIK (eg, http packets) and works great, since it can also help to confirm that all the data was received.
Yep! I've been thinking about that right after posting, and I've come up with the decision to leave things as they are, since they're working fine and I don't have too much time. I should start to write the test application.
Originally Posted by MK27
As for the code organization I can do it anyway, there's no need to get things complicated, although after this exams session I'll surely reorganize and clean the structure of the whole thing.
Thx again for your replies!