Skip to content

Instantly share code, notes, and snippets.

@vlaleli
Created March 8, 2026 20:30
Show Gist options
  • Select an option

  • Save vlaleli/b575efc56da73d89446afadf15d2d8e5 to your computer and use it in GitHub Desktop.

Select an option

Save vlaleli/b575efc56da73d89446afadf15d2d8e5 to your computer and use it in GitHub Desktop.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
class CurrencyClient {
string host;
int port;
int sockFd = -1;
public:
CurrencyClient(string h, int p) : host(std::move(h)), port(p) {}
bool connectToServer() {
sockFd = socket(AF_INET, SOCK_STREAM, 0);
if (sockFd < 0) {
cerr << "socket() failed\n";
return false;
}
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons((uint16_t)port);
if (inet_pton(AF_INET, host.c_str(), &addr.sin_addr) != 1) {
cerr << "inet_pton() failed\n";
close(sockFd);
sockFd = -1;
return false;
}
if (connect(sockFd, (sockaddr*)&addr, sizeof(addr)) < 0) {
cerr << "connect() failed\n";
close(sockFd);
sockFd = -1;
return false;
}
return true;
}
void run() {
cout << "Connected to server " << host << ":" << port << "\n";
cout << "Enter pair like: USD EUR\n";
cout << "Type EXIT to quit\n\n";
while (true) {
cout << "> ";
string input;
getline(cin, input);
if (!cin) break;
if (!sendLine(input)) {
cout << "Connection lost.\n";
break;
}
string resp;
if (!recvLine(resp)) {
cout << "Connection lost.\n";
break;
}
trimLine(resp);
string up = input;
transform(up.begin(), up.end(), up.begin(),
[](unsigned char c){ return (char)toupper(c); });
if (up == "EXIT") {
cout << "Server: " << resp << "\n";
break;
}
if (resp == "ERR") cout << "Server: ERR\n";
else cout << "Server: " << resp << "\n";
}
if (sockFd >= 0) close(sockFd);
}
private:
static void trimLine(string& s) {
while (!s.empty() && (s.back() == '\n' || s.back() == '\r')) s.pop_back();
while (!s.empty() && isspace((unsigned char)s.front())) s.erase(s.begin());
while (!s.empty() && isspace((unsigned char)s.back())) s.pop_back();
}
bool sendLine(const string& msg) {
string data = msg + "\n";
const char* p = data.c_str();
size_t left = data.size();
while (left > 0) {
ssize_t n = send(sockFd, p, left, 0);
if (n <= 0) return false;
p += (size_t)n;
left -= (size_t)n;
}
return true;
}
bool recvLine(string& out) {
out.clear();
char ch;
while (true) {
ssize_t n = recv(sockFd, &ch, 1, 0);
if (n <= 0) return false;
out.push_back(ch);
if (ch == '\n') return true;
if (out.size() > 4096) return false;
}
}
};
int main() {
CurrencyClient client("127.0.0.1", 54000);
if (!client.connectToServer()) return 1;
client.run();
return 0;
}
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
class TimeUtil {
public:
static string now() {
time_t t = time(nullptr);
tm tmv{};
localtime_r(&t, &tmv);
char buf[64];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tmv);
return string(buf);
}
};
class Logger {
string filename;
public:
explicit Logger(string file) : filename(std::move(file)) {}
void writeSession(const string& ip, int port,
const string& connectedAt,
const vector<string>& queries,
const string& disconnectedAt) {
ofstream log(filename, ios::app);
if (!log) return;
log << "========================================\n";
log << "Client: " << ip << ":" << port << "\n";
log << "Connected: " << connectedAt << "\n";
log << "Queries:\n";
if (queries.empty()) log << " (none)\n";
else for (const auto& q : queries) log << " " << q << "\n";
log << "Disconnected: " << disconnectedAt << "\n";
log << "========================================\n\n";
}
};
class RateProvider {
map<string, double> usdTo;
public:
RateProvider() {
usdTo["USD"] = 1.0;
usdTo["EUR"] = 0.86;
usdTo["UAH"] = 38.50;
usdTo["PLN"] = 3.95;
usdTo["GBP"] = 0.78;
}
static string norm(string s) {
string out;
for (unsigned char c : s) {
if (!isspace(c)) out.push_back((char)toupper(c));
}
return out;
}
bool getRate(const string& from, const string& to, double& outRate) const {
auto itFrom = usdTo.find(from);
auto itTo = usdTo.find(to);
if (itFrom == usdTo.end() || itTo == usdTo.end()) return false;
outRate = itTo->second / itFrom->second;
return true;
}
static string format(double x) {
ostringstream oss;
oss.setf(ios::fixed);
oss.precision(4);
oss << x;
string s = oss.str();
while (!s.empty() && s.back() == '0') s.pop_back();
if (!s.empty() && s.back() == '.') s.pop_back();
if (s.empty()) s = "0";
return s;
}
};
class CurrencyServer {
int port;
int serverFd = -1;
Logger logger;
RateProvider rates;
public:
explicit CurrencyServer(int p)
: port(p), logger("server_log.txt"), rates() {}
bool start() {
serverFd = ::socket(AF_INET, SOCK_STREAM, 0);
if (serverFd < 0) {
cerr << "socket() failed\n";
return false;
}
int opt = 1;
::setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons((uint16_t)port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (::bind(serverFd, (sockaddr*)&addr, sizeof(addr)) < 0) {
cerr << "bind() failed\n";
::close(serverFd);
serverFd = -1;
return false;
}
if (::listen(serverFd, 5) < 0) {
cerr << "listen() failed\n";
::close(serverFd);
serverFd = -1;
return false;
}
cout << "Currency Server started on port " << port << "\n";
cout << "Waiting for clients...\n";
return true;
}
void run() {
if (serverFd < 0) return;
while (true) {
sockaddr_in caddr{};
socklen_t clen = sizeof(caddr);
int clientFd = ::accept(serverFd, (sockaddr*)&caddr, &clen);
if (clientFd < 0) continue;
handleClient(clientFd, caddr);
}
}
private:
static void trimLine(string& s) {
while (!s.empty() && (s.back() == '\n' || s.back() == '\r')) s.pop_back();
while (!s.empty() && isspace((unsigned char)s.front())) s.erase(s.begin());
while (!s.empty() && isspace((unsigned char)s.back())) s.pop_back();
}
bool recvLine(int fd, string& out) {
out.clear();
char ch;
while (true) {
ssize_t n = ::recv(fd, &ch, 1, 0);
if (n <= 0) return false;
out.push_back(ch);
if (ch == '\n') return true;
if (out.size() > 4096) return false;
}
}
bool sendLine(int fd, const string& msg) {
string data = msg + "\n";
const char* p = data.c_str();
size_t left = data.size();
while (left > 0) {
ssize_t n = ::send(fd, p, left, 0);
if (n <= 0) return false;
p += (size_t)n;
left -= (size_t)n;
}
return true;
}
void handleClient(int clientFd, const sockaddr_in& caddr) {
string ip = inet_ntoa(caddr.sin_addr);
int cport = ntohs(caddr.sin_port);
string connectedAt = TimeUtil::now();
vector<string> queries;
cout << "Client connected: " << ip << ":" << cport << "\n";
string line;
while (recvLine(clientFd, line)) {
trimLine(line);
if (line.empty()) continue;
string upper = line;
transform(upper.begin(), upper.end(), upper.begin(),
[](unsigned char c){ return (char)toupper(c); });
if (upper == "EXIT") {
sendLine(clientFd, "BYE");
break;
}
istringstream iss(line);
string a, b;
iss >> a >> b;
a = RateProvider::norm(a);
b = RateProvider::norm(b);
if (a.empty() || b.empty()) {
sendLine(clientFd, "ERR");
continue;
}
queries.push_back(a + " " + b);
double rate = 0.0;
if (!rates.getRate(a, b, rate)) {
sendLine(clientFd, "ERR");
continue;
}
sendLine(clientFd, RateProvider::format(rate));
}
string disconnectedAt = TimeUtil::now();
logger.writeSession(ip, cport, connectedAt, queries, disconnectedAt);
cout << "Client disconnected: " << ip << ":" << cport << "\n";
::close(clientFd);
}
};
int main() {
CurrencyServer server(54000);
if (!server.start()) return 1;
server.run();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment