Skip to content

Commit 4446674

Browse files
committed
Add new tcp_stream functionality and example
1 parent 0bb8fde commit 4446674

5 files changed

Lines changed: 571 additions & 17 deletions

File tree

examples/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ add_example(udp_socket ip-sockets-cpp-lite)
1818
add_example(tcp_socket ip-sockets-cpp-lite)
1919
add_example(resolve_host ip-sockets-cpp-lite)
2020
add_example(http_server ip-sockets-cpp-lite)
21-
21+
add_example(tcp_stream ip-sockets-cpp-lite)

examples/tcp_stream.cpp

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
2+
#include "tcp_socket.h"
3+
4+
#include <thread>
5+
#include <chrono>
6+
#include <string>
7+
#include <sstream>
8+
#include <cstdio>
9+
10+
using namespace ipsockets;
11+
12+
#if true
13+
// server and client work on IPv4 mode
14+
static const ip_type_e cfg_ip_type = v4;
15+
static const addr4_t cfg_server = "127.0.0.1:3000";
16+
static const addr4_t cfg_client = "127.0.0.1:3000";
17+
#else
18+
// server and client work on IPv6 mode
19+
static const ip_type_e cfg_ip_type = v6;
20+
static const addr6_t cfg_server = "[::1]:3000";
21+
static const addr6_t cfg_client = "[::1]:3000";
22+
#endif
23+
24+
using tcp_server_t = tcp_socket_t<cfg_ip_type, socket_type_e::server>;
25+
using tcp_client_t = tcp_socket_t<cfg_ip_type, socket_type_e::client>;
26+
using tcp_stream_client_t = tcp_stream_t<cfg_ip_type, socket_type_e::client>;
27+
28+
log_e socket_log_level = log_e::error;
29+
30+
// ============================================================
31+
// Example 1: Basic << and >> operators
32+
// ============================================================
33+
// Server reads lines, client sends lines using iostream operators.
34+
bool shutdown_server { false };
35+
tcp_server_t server_sock { socket_log_level };
36+
37+
void example_1_server () {
38+
39+
if (server_sock.open (cfg_server) != no_error)
40+
return;
41+
42+
printf ("[server] listening on %s\n", cfg_server.to_str ().c_str ());
43+
44+
while (!shutdown_server) {
45+
46+
addr_t<cfg_ip_type> client_addr;
47+
tcp_client_t accepted = server_sock.accept (client_addr);
48+
49+
if (accepted.state != state_e::state_opened)
50+
continue;
51+
52+
printf ("[server] client connected: %s\n", client_addr.to_str ().c_str ());
53+
54+
// Wrap accepted socket into a stream
55+
tcp_stream_client_t stream (std::move (accepted));
56+
57+
{ // --- Example: read with >> operator ---
58+
std::string word;
59+
stream >> word;
60+
if (stream)
61+
printf ("[server] received word: '%s'\n", word.c_str ());
62+
}
63+
64+
{ // --- Example: read with std::getline ---
65+
std::string line;
66+
if (std::getline (stream, line))
67+
printf ("[server] received line: '%s'\n", line.c_str ());
68+
}
69+
70+
{ // --- Example: read number ---
71+
int number = 0;
72+
stream >> number;
73+
if (stream)
74+
printf ("[server] received number: %d\n", number);
75+
}
76+
77+
{ // consume trailing newline after number
78+
std::string rest;
79+
std::getline (stream, rest);
80+
}
81+
82+
{ // --- Example: read multi-word line ---
83+
std::string line;
84+
if (std::getline (stream, line))
85+
printf ("[server] received full line: '%s'\n", line.c_str ());
86+
}
87+
88+
// --- Example: send response using << ---
89+
stream << "RESPONSE OK" << std::endl;
90+
stream << "value=" << 42 << std::endl;
91+
stream << "pi=" << 3.14159 << std::endl;
92+
93+
printf ("[server] response sent, closing connection\n");
94+
// stream destructor flushes and the socket closes
95+
}
96+
97+
printf ("[server] shutdown\n");
98+
}
99+
100+
void example_1_client () {
101+
102+
// small delay to let server start
103+
std::this_thread::sleep_for (std::chrono::milliseconds (200));
104+
105+
tcp_client_t sock (socket_log_level);
106+
107+
if (sock.open (cfg_client) != no_error) {
108+
printf ("[client] failed to connect\n");
109+
return;
110+
}
111+
112+
// Wrap client socket into a stream
113+
tcp_stream_client_t stream (std::move (sock));
114+
115+
printf ("[client] connected to %s\n", stream.remote_address ().to_str ().c_str ());
116+
117+
// --- Send data using << operator ---
118+
stream << "Hello"; // Send a word (server reads with >>)
119+
stream << " world_from_stream!" << '\n'; // Send rest of line (server reads with getline — reads until \n)
120+
stream << 12345 << '\n'; // Send a number
121+
stream << "This is a multi-word message with spaces" << '\n'; // Send a full line with spaces
122+
stream << std::flush; // Flush to make sure everything is sent
123+
printf ("[client] all data sent\n");
124+
125+
{ // --- Read response using >> and getline ---
126+
std::string line;
127+
while (std::getline (stream, line))
128+
printf ("[client] received: '%s'\n", line.c_str ());
129+
}
130+
131+
printf ("[client] done\n");
132+
}
133+
134+
135+
136+
// ============================================================
137+
// Example 2: Structured data exchange
138+
// ============================================================
139+
// Shows sending/receiving structured data through streams.
140+
141+
void example_2_server_handler (tcp_client_t accepted) {
142+
tcp_stream_client_t stream (std::move (accepted));
143+
144+
// Read a command in format: "CMD arg1 arg2"
145+
std::string cmd;
146+
double a = 0, b = 0;
147+
stream >> cmd >> a >> b;
148+
149+
if (!stream) {
150+
printf ("[server2] failed to read command\n");
151+
return;
152+
}
153+
154+
printf ("[server2] command='%s' a=%.2f b=%.2f\n", cmd.c_str (), a, b);
155+
156+
// Compute and send result
157+
double result = 0;
158+
if (cmd == "ADD") result = a + b;
159+
else if (cmd == "MUL") result = a * b;
160+
else if (cmd == "SUB") result = a - b;
161+
else {
162+
stream << "ERROR unknown command" << std::endl;
163+
return;
164+
}
165+
166+
stream << "RESULT " << result << std::endl;
167+
printf ("[server2] sent result: %.2f\n", result);
168+
}
169+
170+
void example_2_server () {
171+
172+
tcp_server_t server (socket_log_level);
173+
if (server.open ({cfg_client.ip, 3001}) != no_error)
174+
return;
175+
176+
printf ("[server2] listening for calculator requests\n");
177+
178+
for (int i = 0; i < 3; i++) {
179+
addr_t<cfg_ip_type> client_addr;
180+
tcp_client_t accepted = server.accept (client_addr);
181+
if (accepted.state == state_e::state_opened)
182+
example_2_server_handler (std::move (accepted));
183+
}
184+
185+
printf ("[server2] shutdown\n");
186+
}
187+
188+
void example_2_client () {
189+
190+
std::this_thread::sleep_for (std::chrono::milliseconds (200));
191+
192+
// Send three calculator requests
193+
struct { const char* cmd; double a; double b; } requests[] = {
194+
{ "ADD", 10.5, 20.3 },
195+
{ "MUL", 3.0, 7.0 },
196+
{ "SUB", 100.0, 42.0 }
197+
};
198+
199+
for (int i = 0; i < 3; i++) {
200+
tcp_client_t sock (socket_log_level);
201+
if (sock.open ({cfg_client.ip, 3001}) != no_error) {
202+
printf ("[client2] failed to connect\n");
203+
continue;
204+
}
205+
206+
tcp_stream_client_t stream (std::move (sock));
207+
208+
// Send: "CMD a b\n"
209+
stream << requests[i].cmd << ' ' << requests[i].a << ' ' << requests[i].b << std::endl;
210+
211+
// Read response line
212+
std::string line;
213+
if (std::getline (stream, line))
214+
printf ("[client2] %s %.1f %.1f -> %s\n", requests[i].cmd, requests[i].a, requests[i].b, line.c_str ());
215+
}
216+
217+
printf ("[client2] done\n");
218+
}
219+
220+
221+
222+
223+
// ============================================================
224+
// Example 3: Line-by-line chat
225+
// ============================================================
226+
// Server echoes every line back in uppercase.
227+
228+
void example_3_server_handler (tcp_client_t accepted) {
229+
tcp_stream_client_t stream (std::move (accepted));
230+
231+
std::string line;
232+
while (std::getline (stream, line)) {
233+
printf ("[echo-server] received: '%s'\n", line.c_str ());
234+
235+
if (line == "QUIT")
236+
break;
237+
238+
// Convert to uppercase and echo back
239+
for (char& c : line)
240+
if ('a' <= c && c <= 'z')
241+
c = c - 'a' + 'A';
242+
243+
stream << line << '\n';
244+
}
245+
246+
stream << std::flush;
247+
printf ("[echo-server] client disconnected\n");
248+
}
249+
250+
void example_3_server () {
251+
252+
tcp_server_t server (socket_log_level);
253+
if (server.open ({cfg_client.ip, 3002}) != no_error)
254+
return;
255+
256+
printf ("[echo-server] listening\n");
257+
258+
addr_t<cfg_ip_type> client_addr;
259+
tcp_client_t accepted = server.accept (client_addr);
260+
if (accepted.state == state_e::state_opened)
261+
example_3_server_handler (std::move (accepted));
262+
263+
printf ("[echo-server] shutdown\n");
264+
}
265+
266+
void example_3_client () {
267+
268+
std::this_thread::sleep_for (std::chrono::milliseconds (200));
269+
270+
tcp_client_t sock (socket_log_level);
271+
if (sock.open ({cfg_client.ip, 3002}) != no_error)
272+
return;
273+
274+
tcp_stream_client_t stream (std::move (sock));
275+
276+
// Send several lines and read echoed responses
277+
const char* messages[] = { "hello world", "ip-sockets-cpp-lite is great", "testing 123" };
278+
279+
for (const char* msg : messages) {
280+
stream << msg << '\n';
281+
stream << std::flush; // ensure data is sent before reading response
282+
283+
std::string response;
284+
if (std::getline (stream, response))
285+
printf ("[echo-client] sent='%s' received='%s'\n", msg, response.c_str ());
286+
}
287+
288+
// Send quit command
289+
stream << "QUIT" << std::endl;
290+
291+
printf ("[echo-client] done\n");
292+
}
293+
294+
295+
// ============================================================
296+
// Main — run all examples sequentially
297+
// ============================================================
298+
299+
int main () {
300+
301+
printf ("========================================\n");
302+
printf (" Example 1: Basic iostream operators\n");
303+
printf ("========================================\n\n");
304+
305+
{
306+
std::thread server (example_1_server);
307+
std::thread client (example_1_client);
308+
309+
client.join ();
310+
311+
shutdown_server = true;
312+
server_sock.close ();
313+
server.join ();
314+
}
315+
316+
std::this_thread::sleep_for (std::chrono::milliseconds (500));
317+
318+
printf ("\n========================================\n");
319+
printf (" Example 2: Calculator (structured)\n");
320+
printf ("========================================\n\n");
321+
322+
{
323+
std::thread server (example_2_server);
324+
std::thread client (example_2_client);
325+
326+
client.join ();
327+
server.join ();
328+
}
329+
330+
std::this_thread::sleep_for (std::chrono::milliseconds (500));
331+
332+
printf ("\n========================================\n");
333+
printf (" Example 3: Echo server (line-by-line)\n");
334+
printf ("========================================\n\n");
335+
336+
{
337+
std::thread server (example_3_server);
338+
std::thread client (example_3_client);
339+
340+
client.join ();
341+
server.join ();
342+
}
343+
344+
printf ("\n========================================\n");
345+
printf (" All examples completed!\n");
346+
printf ("========================================\n");
347+
348+
return 0;
349+
}

0 commit comments

Comments
 (0)