-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.cpp
214 lines (162 loc) · 7.37 KB
/
server.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/**************************************************
** File: server.cpp
** Author: Justin Siddon
** Description: This file provides the server interface to a socket communication system.
**************************************************/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h> // Required for sockets to work.
#include <sys/wait.h>
#include <netinet/in.h> // Required for internet domain access.
#include "error.c"
#include "message.c"
// Prototypes
int receiveMess(int, char*);
int sendMess(int, char*, char*);
/**************************************************
** Function: Main
** Description: Server style socket configurations based off example at: http://www.linuxhowtos.org/C_C++/socket.htm
** and http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html
** Parameters: command line argument port number to listen on.
** Returns: 1
**************************************************/
int main(int argc, char *argv[]) {
int sockfd; // Socket file descriptor.
int newsockfd; // Socket file descriptor for use once socket it bound to a port.
int portno; // Port we are accepting connections on.
int pid; // PID of child processes.
socklen_t clilen; // Size of clients address, required by 'accept()'.
char buffer[1000]; // Data buffer to read incoming messages into.
char username[] = "localhost";
struct sockaddr_in serv_addr; // Address of the server (here).
struct sockaddr_in cli_addr; // Address of the client (who connects).
int n; // Integer to hold number of characters read or write returns.
int status; // Status variable to communicate child process status.
int portCounter = 0; // Integer to increment port number by as clients connect.
// Begin socket setup.
// Check user passed in a port to set socket up on.
if(argc < 2) {
fprintf(stderr, "ERROR, no port provided\n");
fflush(stderr);
exit(1);
}
// Create new socket.
sockfd = socket(AF_INET, SOCK_STREAM, 0); // Socket uses unix domain, stream type socket, with TCP protocol.
if(sockfd < 0) // -1 means socket created errored out.
error("ERROR opening socket", 1);
bzero((char *) &serv_addr, sizeof(serv_addr)); // Set serv_addr struct to all 0's.
portno = atoi(argv[1]); // Convert user entered port number to integer.
// Set up server address struct.
serv_addr.sin_family = AF_INET; // Set to unix domain.
serv_addr.sin_port = htons(portno); // Set port number to network byte order.
serv_addr.sin_addr.s_addr = INADDR_ANY; // Set server address to it's own IP address. (INADDR_ANY returns current machines IP).
// Bind socket to server address.
if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR binding", 1);
listen(sockfd, 5); // Listen on socket for connections.
int quit = 0;
while(1) { // Loop forever waiting for connections.
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); // Accept connection, make new socket for connection.
portCounter = portCounter + 1; // Increment port increase variable, so they can communicate on new port #.
if(newsockfd < 0) // If newsockfd is less than 0, the accept call failed.
error("ERROR on accept", 1);
// Connection was successful so fork a new process.
pid = fork();
if(pid < 0)
error("ERROR forking process", 1);
if(pid == 0) { // CHILD
close(sockfd); // Close the old socket.
char childPort[50]; // Character array to hold port number child should talk on.
int childsockfd; // New socket.
int newchildsockfd; // New communication between child process and client. Sock bound to new port.
childsockfd = socket(AF_INET, SOCK_STREAM, 0); // Socket uses unix domain, stream type socket, with TCP protocol.
sprintf(childPort, "%d", (portno + portCounter)); // Convert port integer to string and store it in childPort.
// Set up new port to talk on, port is 5 characters.
write(newsockfd, childPort, 5);
// Copy old serv address to new one.
struct sockaddr_in child_serv_addr; // Address of the server (here).
// Copy over master server address information to child.
memcpy(&child_serv_addr, &serv_addr, sizeof(serv_addr));
// Set port number of child.
child_serv_addr.sin_port = htons((portno + portCounter)); // Set new port number.
// Bind socket to child server address.
if(bind(childsockfd, (struct sockaddr *) &child_serv_addr, sizeof(child_serv_addr)) < 0)
error("ERROR binding", 1);
listen(childsockfd, 5); // Listen on socket for connections.
newchildsockfd = accept(childsockfd, (struct sockaddr *) &cli_addr, &clilen); // Accept connection, make new socket for connection.
if(newchildsockfd < 0) {
error("ERROR accepting connection from client", 1);
}
int messLen = 0;
int contConn = 1; // Be default we want to continue receiving message.
while(1) {
// Receive message.
contConn = receiveMess(newchildsockfd, buffer);
if(contConn == 0) { // Client decided to close connection.
break;
}
// Send message.
contConn = sendMess(newchildsockfd, buffer, username);
if(quit == 1) { // \quit was entered.
close(newchildsockfd);
close(newsockfd);
kill(pid, SIGKILL);
return 0;
}
}
exit(0);
} else { // PARENT
waitpid(0, &status, WNOHANG);
close(newsockfd);
}
} /* End of while loop */
close(sockfd);
return 0;
}
/**************************************************
** Function: receiveMess
** Description: Receives a message from connected clients.
** Parameters: int socket - socket identifier, char buff - buffer to store message from client.
** Returns: 1 if client is still active, 0 if client is no longer active.
**************************************************/
int receiveMess(int socket, char* buff) {
int n = recv(socket, buff, 1000, 0); // Get message from client.
if(n < 0) {
error("ERROR: error reading from server", 1);
} else if(n == 0) {
printf("Client Connection Closed.\n");
return 0;
}
printf("%s\n", buff);
return 1;
}
/**************************************************
** Function: sendMess
** Description: Sends a message to connected clients.
** Parameters:
** int socket - socket identifier,
** char buff - buffer to store message from client
** char usr - client user name.
** Returns: 1 if server user still wants to chat, 0 if they entered '\quit'.
**************************************************/
int sendMess(int socket, char* buff, char* usr) {
// Write back.
bzero(buff, 1000);
printf("%s> ", usr);
fgets(buff, 1000, stdin);
int quit = checkMessage(buff);
// \quit was entered.
if(quit == 1) {
return 0;
}
packageMess(usr, buff); // Package message to send.
int n = send(socket, buff, 1000, 0);
if(n < 0) {
error("ERROR: error sending back to client.", 1);
}
return 1;
}