-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.cpp
More file actions
204 lines (177 loc) · 7.33 KB
/
server.cpp
File metadata and controls
204 lines (177 loc) · 7.33 KB
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
#include <iostream>
#include <fstream> // stream of files
#include <string> // for strings
#include <cstring>
#include <vector> // for vectors
#include <filesystem> //to estract easily the name of the file
#include <signal.h> //to mantain the signal during the download of the file
#ifdef _WIN32
/*-------FOR WINDOWS-------*/
#include <winsock2.h> // library for windows
#include <ws2tcpip.h>
#include <windows.h> //for sleep()
#pragma comment(lib, "ws2_32.lib") //instruction for the linker on windows
#define CLOSE_SOCKET closesocket
typedef int socklen_t; // Windows uses int for address length in accept()
typedef SOCKET socket_t; // <--socket type for windows
#define IS_VALID_SOCKET(s) ((s)!=INVALID_SOCKET) // CONTROL FOR WINDOWS
#else
/*-------FOR LINUX-------*/
#include <sys/socket.h> //library for Linux socketset linux
#include <netinet/in.h> // structures for Internet addresses (IP/Ports)
#include <unistd.h> // for system functions like close()
#define CLOSE_SOCKET close
typedef int socket_t; // <--socket type for linux
#define IS_VALID_SOCKET(s) ((s)>=0) // CONTROL FOR LINUX
#endif
int main(int argc, char* argv[]){
#ifndef _WIN32// if NOT defined WINDOWS
signal(SIGPIPE, SIG_IGN); // ignore the error if the iPhone abruptly closes the connection
#endif
//ARGUMENT CHECKING
//argc is the number of arguments. agrv[0] is the name of the program, arg[1] is the name of the file
if(argc<2){
std::cerr<<"error: you must specify a file.\n";
std::cerr<<"exemple: "<<argv[0]<<" image.jpg"<<std::endl;
return 1;
}
//variables for the file name and path
std::string complete_path = argv[1];
//extract only the name(example: "image.jpg") from the full path
std::string destination_file_name=std::filesystem::path(complete_path).filename().string();
#ifdef _WIN32
// WINSOCK INITIALIZATION (Strictly required for Windows)
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed." << std::endl;
return 1;
}
#endif
//VARIABLE DECLARATION FOR THE SOCKET
socket_t server_fd; //server socket file descriptor
socket_t new_socket; //accepted connection file descriptor
struct sockaddr_in address; //structure for the IP address and port
int opt=1; //option for reusing the port
int port=80; //used port
//SOCKET CREATION
//AF_INET: IPv4 | SOCK_STREAM: TCP
server_fd=socket(AF_INET, SOCK_STREAM, 0);
if(!IS_VALID_SOCKET(server_fd)){
perror("socket opening failed");
#ifdef _WIN32
WSACleanup();
#endif
return 1;
}
//PORT CONFIGURATION (avoids the "address already in use" error)
#ifdef _WIN32
// Windows expects const char* and SO_REUSEPORT is not commonly used/standard here
if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt))){
#else
if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))){
#endif
perror("setsockopt error");
CLOSE_SOCKET(server_fd);
#ifdef _WIN32
WSACleanup();
#endif
return 1;
}
// initialize structure memory (prevents crashes on Linux)
memset(&address, 0, sizeof(address));
address.sin_family=AF_INET; // accepts connection from any interface (Wi-fi/Eth)
address.sin_addr.s_addr=INADDR_ANY; // accepts connection from any local IP
address.sin_port = htons(port); // set listening on the port
//BIND E LISTEN
if(bind(server_fd, (struct sockaddr*)&address, sizeof(address))<0){
perror("Bind failed");
CLOSE_SOCKET(server_fd);
#ifdef _WIN32
WSACleanup();
#endif
return 1;
}
if (listen(server_fd, 10) < 0){ // listen control
perror("Listen failed");
CLOSE_SOCKET(server_fd);
#ifdef _WIN32
WSACleanup();
#endif
return 1;
}
//GRAFICAL INTERFACE (server side)
std::cout<<"=========================================================================="<<std::endl;
std::cout<<" SERVER STARTED"<<std::endl;
std::cout<<" file being shared: "<<destination_file_name<<std::endl;
std::cout<<" connect with your phone at http://192.168.1.106:"<<port<<std::endl;
std::cout<<" (CAUTION: use HTTP, not HTTPS!)"<<std::endl;
std::cout<<"=========================================================================="<<std::endl;
//CONNECTION ACCEPT LOOP
while(true){
// VARIABLES FOR THE CLIENT (PHONE)
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
//CORRECT CALL TO ACCEPT
// client_addr & client_len, not the structure of the server!
new_socket = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); //new socket
if(!IS_VALID_SOCKET(new_socket)){
perror("error accepting connection");
#ifdef _WIN32 // if there is a critical error it's better to stop for a moment
Sleep(1000);
#else
sleep(1);
#endif
continue;
}
//ADDITION --> put the server in listen mode before sending the whole file
//this addition is necessary because otherwise iPhone will interrupt the file downloas
char client_request[4096];
int byte_received=recv(new_socket, client_request, sizeof(client_request)-1, 0);
if(byte_received>0){
client_request[byte_received]='\0';
//to see what the phone says, uncomment the line below
// std::cout<<"client request: \n"<<client_request<<std::endl;
}
//open file in binary mode
std::ifstream file(complete_path, std::ios::binary | std::ios::ate);
if(!file.is_open()){
std::cerr<<"error: unable to open the file: "<<complete_path<<std::endl;
std::string msg404="HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
send(new_socket, msg404.c_str(), msg404.size(), 0);
CLOSE_SOCKET(new_socket);
continue;
}
//calculate size
std::streamsize size_file=file.tellg();
file.seekg(0, std::ios::beg);
//HTTP header construction (using original file name)
std::string header=
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/octet-stream\r\n"
"Content-Disposition: attachment; filename=\"" + destination_file_name + "\"\r\n"
"Content-Length: " + std::to_string(size_file) + "\r\n"
"Connection: close\r\n\r\n";
//send header
send(new_socket, header.c_str(), (int)header.size(), 0);
//send content in blocks
std::vector<char> buffer(16384); //16KB buffer
while(file.good()){
file.read(buffer.data(), buffer.size());
std::streamsize bytes_readed=file.gcount();
if(bytes_readed>0){
if(send(new_socket, buffer.data(), (int)bytes_readed, 0)<0){
std::cerr<<"connection interrupted by PHONE"<<std::endl;
break;
}
}
}
std::cout<<"File '" << destination_file_name << "' sent successfully!"<<std::endl;
file.close();
CLOSE_SOCKET(new_socket);
}
CLOSE_SOCKET(server_fd);
#ifdef _WIN32
WSACleanup();
#endif
return 0;
}