source: network-game/server/server.cpp@ 371ce29

Last change on this file since 371ce29 was 371ce29, checked in by dportnoy <dmp1488@…>, 12 years ago

The server now uses nonblocking sockets and sleep to free up cpu usage

  • Property mode set to 100644
File size: 7.3 KB
Line 
1#include "../common/compiler.h"
2
3#include <cstdlib>
4#include <cstdio>
5#include <unistd.h>
6#include <string>
7#include <iostream>
8#include <vector>
9#include <algorithm>
10
11#include <fcntl.h>
12#include <assert.h>
13
14#include <sys/socket.h>
15#include <netdb.h>
16#include <netinet/in.h>
17#include <arpa/inet.h>
18
19#include <openssl/bio.h>
20#include <openssl/ssl.h>
21#include <openssl/err.h>
22
23#include "Player.h"
24#include "DataAccess.h"
25#include "../common/message.h"
26
27/*
28 Protocol Design
29
30 Client sends a login message
31 Server replies with client's position in the world and positions of
32 oall other logged in players
33 Merver sends player ids along with locations
34 This means a newly logged in client will need to know which id is
35 assigned to it
36 So server needs to send an id message, wait for an ack, and then send
37 the location messages
38 When a client shuts down, it sends a message to indicate this so the
39 server can remove it from the list of connected clients
40 Eventually, there'll need to be a way to detect timeouts from clients
41 (if they crashed or otherwise failed to send the logout message)
42*/
43
44using namespace std;
45
46void processMessage(const NETWORK_MSG &clientMsg, const struct sockaddr_in &from, vector<Player> &vctPlayers, int &num, NETWORK_MSG &serverMsg);
47
48// this should probably go somewhere in the common folder
49void error(const char *msg)
50{
51 perror(msg);
52 exit(0);
53}
54
55Player *findPlayerByName(vector<Player> &vec, string name)
56{
57 vector<Player>::iterator it;
58
59 for (it = vec.begin(); it != vec.end(); it++)
60 {
61 if ( it->name.compare(name) == 0 )
62 return &(*it);
63 }
64
65 return NULL;
66}
67
68Player *findPlayerByAddr(vector<Player> &vec, const sockaddr_in &addr)
69{
70 vector<Player>::iterator it;
71
72 for (it = vec.begin(); it != vec.end(); it++)
73 {
74 if ( it->addr.sin_addr.s_addr == addr.sin_addr.s_addr &&
75 it->addr.sin_port == addr.sin_port )
76 return &(*it);
77 }
78
79 return NULL;
80}
81
82void set_nonblock(int sock)
83{
84 int flags;
85 flags = fcntl(sock, F_GETFL,0);
86 assert(flags != -1);
87 fcntl(sock, F_SETFL, flags | O_NONBLOCK);
88}
89
90int main(int argc, char *argv[])
91{
92 int sock, length, n;
93 struct sockaddr_in server;
94 struct sockaddr_in from; // holds the info on the connected client
95 NETWORK_MSG clientMsg, serverMsg;
96 vector<Player> vctPlayers;
97
98 srand(time(NULL));
99 int num = 500;
100 //int num = (rand() % 0) + 1;
101
102 cout << "num: " << num << endl;
103
104 SSL_load_error_strings();
105 ERR_load_BIO_strings();
106 OpenSSL_add_all_algorithms();
107
108 if (argc < 2) {
109 cerr << "ERROR, no port provided" << endl;
110 exit(1);
111 }
112
113 /*
114 DataAccess da;
115
116 da.printPlayers();
117
118 da.insertPlayer("playerName3", "playerPass");
119 cout << endl << "Inserted player" << endl << endl;
120
121 Player* p = da.getPlayer("playerName");
122 cout << "player name: " << p->name << endl;
123 delete(p);
124
125 p = da.getPlayer("playerName3");
126 cout << "player name: " << p->name << endl;
127 delete(p);
128
129 da.printPlayers();
130 cout << endl;
131 */
132
133 sock = socket(AF_INET, SOCK_DGRAM, 0);
134 if (sock < 0) error("Opening socket");
135 length = sizeof(server);
136 bzero(&server,length);
137 server.sin_family=AF_INET;
138 server.sin_port=htons(atoi(argv[1]));
139 server.sin_addr.s_addr=INADDR_ANY;
140 if ( bind(sock, (struct sockaddr *)&server, length) < 0 )
141 error("binding");
142
143 set_nonblock(sock);
144
145 while (true) {
146
147 usleep(5000);
148
149 n = receiveMessage(&clientMsg, sock, &from);
150
151 if (n >= 0) {
152 cout << "Got a message" << endl;
153
154 processMessage(clientMsg, from, vctPlayers, num, serverMsg);
155
156 cout << "msg: " << serverMsg.buffer << endl;
157
158 n = sendMessage(&serverMsg, sock, &from);
159 if (n < 0)
160 error("sendMessage");
161 }
162
163 }
164
165 return 0;
166}
167
168void processMessage(const NETWORK_MSG &clientMsg, const struct sockaddr_in &from, vector<Player> &vctPlayers, int &num, NETWORK_MSG &serverMsg)
169{
170 cout << "ip address: " << inet_ntoa(from.sin_addr) << endl;
171 cout << "port: " << from.sin_port << endl;
172 cout << "MSG: type: " << clientMsg.type << endl;
173 cout << "MSG contents: " << clientMsg.buffer << endl;
174
175 // Check that if an invalid message is sent, the client will correctly
176 // receive and display the response. Maybe make a special error msg type
177 switch(clientMsg.type)
178 {
179 case MSG_TYPE_REGISTER:
180 {
181 string username(clientMsg.buffer);
182 string password(strchr(clientMsg.buffer, '\0')+1);
183
184 cout << "username: " << username << endl;
185 cout << "password: " << password << endl;
186
187 strcpy(serverMsg.buffer, "Registration test");
188
189 serverMsg.type = MSG_TYPE_REGISTER;
190
191 break;
192 }
193 case MSG_TYPE_LOGIN:
194 {
195 string username(clientMsg.buffer);
196 cout << "Player logging in: " << username << endl;
197
198 Player *p = findPlayerByName(vctPlayers, username);
199
200 if (p == NULL)
201 {
202 Player newP(username, "");
203 newP.setAddr(from);
204
205 vctPlayers.push_back(newP);
206 strcpy(serverMsg.buffer, "I'm thinking of a number between 1 and 1000. Guess what it is.");
207 }
208 else
209 {
210 strcpy(serverMsg.buffer, "Player has already logged in.");
211 }
212
213 serverMsg.type = MSG_TYPE_LOGIN;
214
215 break;
216 }
217 case MSG_TYPE_LOGOUT:
218 {
219 string name(clientMsg.buffer);
220 cout << "Player logging out: " << name << endl;
221
222 Player *p = findPlayerByName(vctPlayers, name);
223
224 if (p == NULL)
225 {
226 strcpy(serverMsg.buffer, "That player is not logged in. This is either a bug, or you're trying to hack the server.");
227 }
228 else if( p->addr.sin_addr.s_addr != from.sin_addr.s_addr ||
229 p->addr.sin_port != from.sin_port )
230 {
231 strcpy(serverMsg.buffer, "That player is logged in using a differemt connection. This is either a bug, or you're trying to hack the server.");
232 }
233 else
234 {
235 vctPlayers.erase((vector<Player>::iterator)p);
236 strcpy(serverMsg.buffer, "You have successfully logged out. You may quit the game.");
237 }
238
239 break;
240 }
241 case MSG_TYPE_CHAT:
242 {
243 Player *p = findPlayerByAddr(vctPlayers, from);
244
245 if (p == NULL)
246 {
247 strcpy(serverMsg.buffer, "No player is logged in using this connection. This is either a bug, or you're trying to hack the server.");
248 }
249 else
250 {
251 int guess = atoi(clientMsg.buffer);
252
253 cout << "guess: " << guess << endl;
254
255 if (guess < 1 || guess > 1000) {
256 strcpy(serverMsg.buffer, "You must guess a number between 1 and 1000");
257 }else if(guess > num)
258 strcpy(serverMsg.buffer, "The number I'm thinking of is less than that.");
259 else if(guess < num)
260 strcpy(serverMsg.buffer, "The number I'm thinking of is greater than that.");
261 else if(guess == num) {
262 strcpy(serverMsg.buffer, "Congratulations! I will now think of a new number.");
263 num = (rand() % 1000) + 1;
264 }
265 }
266
267 serverMsg.type = MSG_TYPE_CHAT;
268
269 break;
270 }
271 default:
272 {
273 strcpy(serverMsg.buffer, "Server error occured. Report this please.");
274
275 serverMsg.type = MSG_TYPE_CHAT;
276
277 break;
278 }
279 }
280}
Note: See TracBrowser for help on using the repository browser.