- use InterlockedExchange for setting shutdown flag
[reactos.git] / reactos / services / tcpsvcs / skelserver.c
1 /*
2 * ReactOS Services
3 * Copyright (C) 2005 ReactOS Team
4 *
5 * LICENCE: GPL - See COPYING in the top level directory
6 * PROJECT: ReactOS simple TCP/IP services
7 * FILE: apps/utils/net/tcpsvcs/skelserver.c
8 * PURPOSE: Provide CharGen, Daytime, Discard, Echo, and Qotd services
9 * PROGRAMMERS: Ged Murphy (gedmurphy@gmail.com)
10 * REVISIONS:
11 * GM 04/10/05 Created
12 *
13 */
14
15 #include "tcpsvcs.h"
16
17 extern BOOL bShutDown;
18 extern BOOL bPause;
19
20 DWORD WINAPI StartServer(LPVOID lpParam)
21 {
22 SOCKET ListeningSocket;
23 PSERVICES pServices;
24 const TCHAR* HostIP = "127.0.0.1";
25 TCHAR buf[256];
26
27 pServices = (PSERVICES)lpParam;
28
29 //DebugBreak();
30 ListeningSocket = SetUpListener(HostIP, htons(pServices->Port));
31 if (ListeningSocket == INVALID_SOCKET)
32 {
33 LogEvent("Socket error when setting up listener\n", 0, TRUE);
34 return 3;
35 }
36
37 _stprintf(buf, _T("%s is waiting for connections on port %d...\n"),
38 pServices->Name, pServices->Port);
39 LogEvent(buf, 0, FALSE);
40
41 if (! bShutDown)
42 AcceptConnections(ListeningSocket, pServices->Service, pServices->Name);
43
44 ExitThread(0);
45 }
46
47
48 SOCKET SetUpListener(const char* ServAddr, int Port)
49 {
50 SOCKET Sock;
51 SOCKADDR_IN Server;
52
53 Sock = socket(AF_INET, SOCK_STREAM, 0);
54 if (Sock != INVALID_SOCKET)
55 {
56 Server.sin_family = AF_INET;
57 Server.sin_addr.s_addr = htonl(INADDR_ANY);
58 Server.sin_port = Port;
59 if (bind(Sock, (SOCKADDR*)&Server, sizeof(SOCKADDR_IN)) != SOCKET_ERROR)
60 {
61 listen(Sock, SOMAXCONN);
62 return Sock;
63 }
64 else
65 LogEvent(_T("bind() failed\n"), 0, TRUE);
66
67 }
68 return INVALID_SOCKET;
69 }
70
71 /* note: consider allowing a maximum number of connections
72 * A number of threads can be allocated and worker threads will
73 * only be spawned if a free space is available
74
75 typedef struct _WORKER_THREAD {
76 DWORD num;
77 BOOL available;
78 HANDLE hThread;
79 } WORKER_THREAD;
80
81 */
82
83 VOID AcceptConnections(SOCKET ListeningSocket,
84 LPTHREAD_START_ROUTINE Service, TCHAR *Name)
85 {
86 SOCKADDR_IN Client;
87 SOCKET Sock;
88 HANDLE hThread;
89 TIMEVAL TimeVal;
90 FD_SET ReadFDS;
91 INT nAddrSize = sizeof(Client);
92 DWORD ThreadID;
93 TCHAR buf[256];
94 INT TimeOut = 2000; // 2 seconds
95
96 //DebugBreak();
97
98 /* set timeout values */
99 TimeVal.tv_sec = TimeOut / 1000;
100 TimeVal.tv_usec = TimeOut % 1000;
101
102 while (! bShutDown) // (i<MAX_CLIENTS && !bShutDown)
103 {
104 INT SelRet = 0;
105
106 FD_ZERO(&ReadFDS);
107 FD_SET(ListeningSocket, &ReadFDS);
108
109 SelRet = select(0, &ReadFDS, NULL, NULL, &TimeVal);
110 if (SelRet == SOCKET_ERROR)
111 {
112 LogEvent(_T("select failed\n"), 0, TRUE);
113 return;
114 }
115 else if (SelRet > 0)
116 {
117 /* don't call FD_ISSET if bShutDown flag is set */
118 if ((! bShutDown) || (FD_ISSET(ListeningSocket, &ReadFDS)))
119 {
120 Sock = accept(ListeningSocket, (SOCKADDR*)&Client, &nAddrSize);
121 if (Sock != INVALID_SOCKET)
122 {
123 _stprintf(buf, _T("Accepted connection to %s server from %s:%d\n"),
124 Name, inet_ntoa(Client.sin_addr), ntohs(Client.sin_port));
125 LogEvent(buf, 0, FALSE);
126 _stprintf(buf, _T("Creating new thread for %s\n"), Name);
127 LogEvent(buf, 0, FALSE);
128
129 hThread = CreateThread(0, 0, Service, (void*)Sock, 0, &ThreadID);
130
131 /* Check the return value for success. */
132 if (hThread == NULL)
133 {
134 _stprintf(buf, _T("Failed to start worker thread for "
135 "the %s server....\n"), Name);
136 LogEvent(buf, 0, TRUE);
137 }
138
139 WaitForSingleObject(hThread, INFINITE);
140
141 CloseHandle(hThread);
142 }
143 else
144 {
145 LogEvent(_T("accept failed\n"), 0, TRUE);
146 return;
147 }
148 }
149 }
150 }
151 }
152
153 BOOL ShutdownConnection(SOCKET Sock, BOOL bRec)
154 {
155 TCHAR buf[256];
156
157 /* Disallow any further data sends. This will tell the other side
158 that we want to go away now. If we skip this step, we don't
159 shut the connection down nicely. */
160 if (shutdown(Sock, SD_SEND) == SOCKET_ERROR)
161 {
162 LogEvent(_T("Error in shutdown()\n"), 0, TRUE);
163 return FALSE;
164 }
165
166 /* Receive any extra data still sitting on the socket. After all
167 data is received, this call will block until the remote host
168 acknowledges the TCP control packet sent by the shutdown above.
169 Then we'll get a 0 back from recv, signalling that the remote
170 host has closed its side of the connection. */
171 if (bRec)
172 {
173 char ReadBuffer[BUF];
174 int NewBytes = recv(Sock, ReadBuffer, BUF, 0);
175 if (NewBytes == SOCKET_ERROR)
176 return FALSE;
177 else if (NewBytes != 0)
178 {
179 _stprintf(buf, _T("FYI, received %d unexpected bytes during shutdown\n"), NewBytes);
180 LogEvent(buf, 0, FALSE);
181 }
182 }
183
184 /* Close the socket. */
185 if (closesocket(Sock) == SOCKET_ERROR)
186 return FALSE;
187
188 return TRUE;
189 }