• Welcome to Valhalla Legends Archive.
 

[C/Linux] Server Model, need help

Started by rabbit, June 14, 2007, 12:45 PM

Previous topic - Next topic

iago

He obviously knows more than I do. You can use my code there for reference (especially regarding how to use select()), but learn from him.

Quote from: rabbit on June 16, 2007, 07:56 AM
An iago, could you please allow traffic through port 9933?  That'd be awesome.
Done.
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


rabbit

Okay, I put up my little echoer on the vm, but it won't run if I close my SSH.  How can I get around that?
Grif: Yeah, and the people in the red states are mad because the people in the blue states are mean to them and want them to pay money for roads and schools instead of cool things like NASCAR and shotguns.  Also, there's something about ketchup in there.

iago

Learn how to use "screen".

When you log in, run "screen -r" (resume). If there's no screens to resume, run "screen". That'll open one. You can run stuff inside there and close your ssh client. Stuff will keep running, and you can use "screen -r" to get it back.

You can also have multiple "windows" within the screen. You should find a tutorial if you want to learn more. I typically use screen when coding, I have the code on one and I compile/test it on the other.
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Banana fanna fo fanna

You mean your server quits when you exit ssh? Try "man nohup"

You code looks good (I'm not going to compile and test it, but I'll trust you if you say it works!).

At this point, I'd say your next step is to break it down into several smaller functions (or put it all in a class). If you go the class route, replace initalize() with a constructor. If you go the functions route (plain old C), you are going to ned to pass a struct to each of these functions containing all of the variables you defined at the top of main() in your pastebin. You're going to want to have functions that have names such as:

initialize(localhost, localport) - bind the socket and listen
tick() - perform one iteration of the I/O loop. takes an argument whether or not select() should block
run() - continuously call tick() with blocking set to TRUE
on_ready_accept(socket) - called from tick() when you are ready to accept a connection. Inside here you should actually call accept() and add it to your fd_set. Do all error checking in here as well
on_ready_read(socket) - called when a socket is ready to read. Again, do any actual reading in here
on_ready_write(socket) - called when a socket is ready to write. You'll need to modify your code to support this.
on_socket_except(socket) - called when a socket has an exception. You'll need to modify your code to support this, too.
terminate() - stop the run() loop and clean up the socket

This module or class should be called something that gets across the message "I'm the only thing that does I/O in this whole program"

rabbit

#19
Okay, mostly done: http://pastebin.com/930882

I'm not sure what you were expecting for on_socket_exception(), so I basically left exceptions to perror().  I'm also not sure what to do with BOOL block in tick()...
Grif: Yeah, and the people in the red states are mad because the people in the blue states are mean to them and want them to pay money for roads and schools instead of cool things like NASCAR and shotguns.  Also, there's something about ketchup in there.

Banana fanna fo fanna

Some thoughts:
- I would make memzero a function, not a macro. It'll be easier to debug and is otherwise just not natural.
- I would call _settings something else. Perhaps APP, or SERVER. Conceptually, the struct stores things like the socket reference, which aren't actually settings. This is a nitpick, though...it's not a big deal.

Your code is quite well written though.

In your on_socket_exception(), you should remove the socket from the FD_SET (make sure it's properly cleaned up), and be prepared to deal with conditions such as removing it from the chatroom (when you get to this stage). I like treating READ_SOCKET_CLOSE like a socket exception; copy the code from READ_SOCKET_CLOSE to on_socket_exception(), and call on_socket_exception() in READ_SOCKET_CLOSE.

As far as your block argument goes, it's easy. Look at the man page for select(). If block is true, you want to sit in tick() until an I/O event occurs. You want this in a single-threaded environment where your server is only acting upon I/O operations and not something else (scheduled tasks etc). Often, however, you want to instantly return if there is no work to be done. This occurs in systems where you cannot afford to block the thread. The man page says "if timeout is a nil pointer, the select blocks indefinitely." Pass NULL when block is true. The man page also says "to effect a poll, the timeout argument should be non-nil, pointing to a zero-valued timeval structure." Checking if an I/O event has occurred, but not waiting for it is called polling. Do this when block is false.

The next step is to prepare the event system. You are tasked with the following:
- Create a structure which represents an event. You are going to have three types of events: reads, writes, and errors. You're going to run into a problem: conceptually, these are three different events, and should go into three different structures. With C++, we would use inheritance, but in C, we're going to use something called a void pointer. These are pointers that can point to whatever type of data you want, but be careful: they are VERY prone to bugs if you aren't careful. You are also going to be using something called an enumeration (enum), which is essentially an easy way to bind names to serial numbers (i.e. A to 1, B to 2, etc). So, your event structure will have two fields: type, which is an enum of (read, write, exception), and a void pointer (void*) to the actual event structure (you'll need three structures, one for each event type. They should contain all of the data pertaining to each event (socket handles, data buffers if needed etc). For the data buffer, document the fact that the consumer of the event must clean up the memory for the data buffer.
- Create two functions, queue_get(queue, *event) and queue_put(queue, *event). These are FIFO operations (first in, first out). You'll also need to create the datastructure for the queue. It doesn't matter if this implementation is good or not. We're in a phase of development called prototyping. Eventually, we're going to use a multithreaded queue provided to us by Linux, but for now, we are using a placeholder. For simplicity's sake, I'd suggest a struct containing a constant size preallocated array of pointers to event objects, and 2 integers specifying the current indices of the start of the queue and the end of the queue. Also, stick in the documentation of queue_get that the event should probably be allocated on the heap, and in the documentation of queue_put, that the caller should probably be responsible for freeing the memory of the event. This is complicated stuff, feel free to ask me a question about it. The queue implementation, though a good exercise, is something we're just going to throw out anyway, so if it takes you too long, ask me and I'll write a quick implementation (good brush up for me too).

Also, feel free to start reading up on pthreads.

Good luck.