Created
March 8, 2026 20:30
-
-
Save vlaleli/b575efc56da73d89446afadf15d2d8e5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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