This repository has been archived by the owner on Jan 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.cpp
167 lines (145 loc) · 3.48 KB
/
main.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
/**
* @file main.cpp
* @author Onegen Something <[email protected]>
* @brief Main logic of IPKCP Client
* @date 2023-02-28
*
*/
#include <atomic>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include "client.hpp"
/**
* @brief SIGINT Flag
*
* A flag indicating whether SIGINT was received, used to gracefully
* terminate the client.
*/
std::atomic<bool> quit(false);
/**
* @brief Signal handling function
*
* Sets the quit flag to true, which is used to gracefully terminate the
* client.
*
* @param signal - Signal number
*/
void signal_handler(int signal) {
(void)signal;
quit.store(true);
}
/**
* @brief Main function
*
* Parses command line arguments, creates a client and connects to the
* server. Then it reads input from stdin and sends it to the server.
*
* @return int - Exit code
*/
int main(int argc, char* argv[]) {
using std::cerr;
using std::cout;
using std::endl;
/* Set up signal handler */
struct sigaction sig_act {};
memset(&sig_act, 0, sizeof(sig_act));
sig_act.sa_handler = signal_handler;
sigfillset(&sig_act.sa_mask);
sigaction(SIGINT, &sig_act, NULL);
/* Parse command line options */
int opt;
int port = 0;
std::string hostname;
std::string protocol;
while ((opt = getopt(argc, argv, "h:p:m:")) != -1) {
switch (opt) {
case 'h':
hostname = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'm':
protocol = optarg;
break;
default:
cerr << " Usage: ./ipkcpc -h <host> -p <port> -m <mode>"
<< endl;
return EXIT_FAILURE;
}
}
/* Validate options */
if (hostname.empty()) {
cerr << "!ERR! Host not specified!" << endl
<< " Usage: ./ipkcpc -h <host> -p <port> -m <mode>" << endl;
return EXIT_FAILURE;
}
if (port == 0) {
cerr << "!ERR! Port not specified!" << endl
<< " Usage: ./ipkcpc -h <host> -p <port> -m <mode>" << endl;
return EXIT_FAILURE;
}
if (protocol.empty()) {
cerr << "!ERR! Mode not specified!" << endl
<< " Usage: ./ipkcpc -h <host> -p <port> -m <mode>" << endl;
return EXIT_FAILURE;
}
if (protocol != "tcp" && protocol != "udp") {
cerr << "!ERR! Invalid mode, expected tcp or udp!" << endl
<< " Usage: ./ipkcpc -h <host> -p <port> -m <mode>" << endl;
return EXIT_FAILURE;
}
/* Create client */
IPKCPClient client(port, hostname,
protocol == "tcp" ? SOCK_STREAM : SOCK_DGRAM);
if (client.get_state() == IPKCPCState::ERRORED) {
cerr << "!ERR! " << client.error_msg << endl;
return EXIT_FAILURE;
}
/* Connect to server */
if (!client.connect() || client.get_state() == IPKCPCState::ERRORED) {
cerr << "!ERR! " << client.error_msg << endl;
return EXIT_FAILURE;
}
/* Communicate */
while (client.get_state() == IPKCPCState::UP) {
std::string input;
if (std::cin.eof()) {
/* Reached EOF */
quit.store(true);
} else {
/* Read input */
std::getline(std::cin, input);
}
/* Quit on SIGINT or EOF */
if (quit.load()) {
/* Disconnect from server */
cout << client.disconnect();
break;
}
/* Skip empty input */
if (input.empty()) {
continue;
}
/* Send input to server */
if (client.send(input) < 0) {
break;
}
/* Receive response from server */
std::string response = client.recv();
if (response.empty()) {
break;
}
/* Print response */
cout << response;
}
/* Print error message */
if (client.get_state() != IPKCPCState::DOWN) {
cerr << "!ERR! " << client.error_msg << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}