Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Kalrish Bäakjen committed Jan 13, 2018
0 parents commit 5d9ace3
Show file tree
Hide file tree
Showing 18 changed files with 1,621 additions and 0 deletions.
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Lappenchat server for Windows

Lappenchat server targeting Windows, implemented as a command and a service and using only native Windows APIs.


## Was zum Teufel?
Für das Semesterprojekt des vom Professor Siegfried Rump erteilten Lehrfaches Prozedurale Programmierung, das im ersten Semester stattfindet, haben wir uns für eine Chat-Implementierung entschieden. Das Projekt besteht aus einem Server, der Nachrichten von den Clients bekommt und weitergibt, und einigen Clients, die Kontakt mit dem Server aufnehmen und die dem Benutzer dazu dienen, mit anderen zu kommunizieren. Dies ist nun der Windows Server. Der Name begreift das etwas beleidigende Wort »Lappen« ein, das einem der Mitglieder des Projektteams an einem unseligen Tage von sehr witzigen Kommilitonen beschert wurde.

## Installation

### Dependencies
The programs don't require any libraries beyond those included in a standard Windows installation. They both have to be linked against `ws2_32.dll`. Additionally, the service requires `advapi32.dll`.

### Configuration
The build scripts accompanying this program and used in its development include defaults which should work in all cases. Thus, for a quick check of this project, you could possibly do without setting any build parameter.

Should you want to customize the build parameters or to adjust them to suit your environment, you can do so in a `tup.config` file.

### Build
The [tup build system](http://gittup.org/tup/) manages the build process.

### Installation
The server is implemented both as a command and as a Windows service.

The command is mainly meant for a quick check. Should it be desired to install it, however, its installation involves nothing more than copying it to the desired location.

As for the service, its installation comprises the usual setup of a new service, which can be carried out with the SC.EXE command as follows:

$ sc config lappenchat-server DisplayName= "Lappenchat server" depend= tcpip binPath= "exePath"

The placeholder _exePath_ is the absolute path of the Lappenchat server executable.


## Usage
The command can be managed from a command prompt. To start it:

$ lappenchat-server-command [OPTIONS]

To stop it, hit CTRL-C.

The service can be managed as any other Windows service.

In a command prompt:

$ sc start lappenchat-server [OPTIONS]

Then, to stop it:

$ sc stop lappenchat-server

In both cases, _OPTIONS_ is a placeholder for any options you might want to pass to the server.

### Options
Options are specified as service start parameters, in the following format: the option code is preceded by a single dash and any required arguments are provided as separate parameters following it. An example:

$ sc start lappenchat-server -l logFilePath -p Port -t nThreads

A list of the options currently supported follows:

| Code | Availability | Meaning
|-------|-----------------|----------------------
| l | service | **Path to the log file**. If this option is not specified, logs will not be saved anywhere.
| p | command+service | **Port number to listen on**. The default is 3144.
| t | command+service | **Number of threads** to spawn and use in handling connection requests and server traffic.

Some options are only supported by the service.
6 changes: 6 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- [ ] Mark suitable pointers with the `restrict` keyword
- [ ] Extract the client disconnect logic into a separate function
- [ ] Implement a proper memory strategy that doesn't impose a limit on the number of connected users and, if possible, doesn't require that much mutually-exclusive access from different threads (i.e. one that's more multithread-friendly)
- [ ] Use a memory pool for the message buffers
- [ ] Implement another strategy using AcceptEx instead of accept
- [ ] Send messages asynchronously (i.e. use WSASend instead of send)
12 changes: 12 additions & 0 deletions Tupfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
include_rules


: foreach common.c server.c error.c logmsg.c |> !cc |> {objs}

: command.c |> !cc |> {command_obj}
LIBS=$(LIBS_COMMAND)
: {command_obj} {objs} |> !ld |> lappenchat-server-command.exe

: service.c |> !cc |> {service_obj}
LIBS=$(LIBS_SERVICE)
: {service_obj} {objs} |> !ld |> lappenchat-server-service.exe
Empty file added Tupfile.ini
Empty file.
17 changes: 17 additions & 0 deletions Tuprules.tup
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.gitignore


ifdef TOOLCHAIN
TOOLCHAIN=@(TOOLCHAIN)
else
ifeq (@(TUP_PLATFORM),win32)
# Use Microsoft's native compiler if building on Windows
TOOLCHAIN=msvc
else
# Otherwise, go for MinGW
TOOLCHAIN=gnu
endif
endif

# Include the appropriate toolchain definition file
include toolchains.tup/$(TOOLCHAIN).tup
85 changes: 85 additions & 0 deletions command.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <winsock2.h>
#include <windows.h>
#include "logmsg.h"
#include "error.h"
#include "common.h"
#include "server.h"


static HANDLE stop_event;

BOOL WINAPI ConsoleCtrlHandler
(
DWORD signal
)
{
switch ( signal )
{
case CTRL_C_EVENT:
return SetEvent(stop_event);
default:
return 0;
}
}

int main
(
int argc,
char * * argv
)
{
int rv = 0;
char parameter = 0;
struct lappenchat_server_options lcso = { 0 };

logout = stderr;

for ( char * * arg_cur = argv, * * const argv_end = argv+argc ; arg_cur != argv_end ; ++arg_cur )
{
char * const arg = *arg_cur;
if ( parameter )
{
switch ( parameter )
{
/* FIXME: strtol parses a sign ('+' or '-') as well.
* This should actually be prohibited here, as a "negative
* port" or a "negative number of threads" don't make
* sense. */
case 'p':
/* Here, we should also check that the number
* provided doesn't exceed the maximum port #. */
lcso.port = (u_short)strtol(arg, NULL, 10);
break;
case 't':
lcso.threads = strtol(arg, NULL, 10);
break;
}
parameter = 0;
}
else
if ( *arg == '-' )
parameter = arg[1];
}

if ( stop_event = CreateEvent(NULL, TRUE, FALSE, NULL) )
{
if ( SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) )
{
if ( !lcso.port )
lcso.port = 3144;

if ( !lcso.threads )
lcso.threads = get_proc_n();

rv = start_server(lcso, stop_event);
}
else
winapi_perror("couldn't set console control handler");

CloseHandle(stop_event);
}
else
winapi_perror("couldn't create stop event object");

return !rv;
}
44 changes: 44 additions & 0 deletions common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "common.h"

#include <winsock2.h>
#include <windows.h>
#include "logmsg.h"
#include "error.h"
#include "server.h"


int start_server
(
struct lappenchat_server_options lcso,
HANDLE stop_event
)
{
int rv;
if ( WSAStartup(MAKEWORD(2,2), &lcso.wsa_data) == 0 )
{
logmsgf("successfully initialized Winsock\nWinsock version offered: %u.%u\nWinsock implementation description: %s\n", LOBYTE(lcso.wsa_data.wVersion), HIBYTE(lcso.wsa_data.wVersion), lcso.wsa_data.szDescription);

rv = lappenchat_server(lcso, stop_event);

if ( WSACleanup() == 0 )
logmsg("successfully terminated use of Winsock");
else
wsa_perror("couldn't terminate use of Winsock");
}
else
{
logmsg("couldn't initialize Winsock");
rv = 0;
}

return rv;
}

size_t
get_proc_n
( void )
{
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
return system_info.dwNumberOfProcessors;
}
14 changes: 14 additions & 0 deletions common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <stddef.h> // size_t
#include <winsock2.h>
#include <windows.h> // HANDLE
#include "server.h" // struct lappenchat_server_options


int start_server
(
struct lappenchat_server_options,
HANDLE
);

size_t get_proc_n
(void);
46 changes: 46 additions & 0 deletions error.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "error.h"

#include <winsock2.h>
#include "logmsg.h"


void win_perror
(
const char * const msg,
int error_code
)
{
LPTSTR formatted_error_code;

if ( FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
(LPTSTR)&formatted_error_code,
0,
NULL
) == 0 )
logmsgf("%s (%i)\n", msg, error_code);
else
{
logmsgf("%s: %i: %s", msg, error_code, formatted_error_code);
LocalFree(formatted_error_code);
}
}

void wsa_perror
(
const char * const msg
)
{
win_perror(msg, WSAGetLastError());
}

void winapi_perror
(
const char * const msg
)
{
win_perror(msg, GetLastError());
}
15 changes: 15 additions & 0 deletions error.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
void win_perror
(
const char * const msg,
int error_code
);

void wsa_perror
(
const char * const msg
);

void winapi_perror
(
const char * const msg
);
30 changes: 30 additions & 0 deletions logmsg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "logmsg.h"

#include <stdarg.h>
#include <stdio.h>


FILE * logout;

void logmsg
(
const char * const msg
)
{
fputs(msg, logout);
fputc('\n', logout);
fflush(logout);
}

void logmsgf
(
const char * const fmt,
...
)
{
va_list arguments;
va_start(arguments, fmt);
vfprintf(logout, fmt, arguments);
fflush(logout);
va_end(arguments);
}
14 changes: 14 additions & 0 deletions logmsg.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <stdio.h>


extern FILE * logout;

void logmsg
(
const char * const msg
);
void logmsgf
(
const char * const fmt,
...
);
Loading

0 comments on commit 5d9ace3

Please sign in to comment.