60 {
62
63
64
65
66
67
68
69
70
71
72
73
75
76 std::cout << "DICOM servers use these key parameters:\n";
77 std::cout << " - AE Title: Application Entity identifier (like hostname)\n";
78 std::cout << " - Port: TCP listening port\n";
79 std::cout << " - Max PDU: Maximum data unit size per transfer\n";
80 std::cout << " - Timeouts: Connection and idle timeouts\n\n";
81
82
83 uint16_t port = 11112;
84 if (argc > 1) {
85 try {
86 int port_arg = std::stoi(argv[1]);
87 if (port_arg > 0 && port_arg < 65536) {
88 port = static_cast<uint16_t>(port_arg);
89 }
90 } catch (const std::exception&) {
91
92 }
93 }
94
95
102
103
104
107
110 {
"Port", std::to_string(config.
port)},
112 {
"Idle Timeout", std::to_string(config.
idle_timeout.count()) +
" seconds"},
113 {
"Max PDU Size", std::to_string(config.
max_pdu_size) +
" bytes"}
114 });
115
117
118
119
120
121
122
123
124
125
126
127
128
129
130
132
133 std::cout << "DICOM Service Class Provider (SCP) types:\n";
134 std::cout << " - Verification SCP: C-ECHO (connectivity test)\n";
135 std::cout << " - Storage SCP: C-STORE (receive images)\n";
136 std::cout << " - Query SCP: C-FIND (search data)\n";
137 std::cout << " - Retrieve SCP: C-GET/C-MOVE (retrieve data)\n\n";
138
139
140 auto server = std::make_unique<net::dicom_server>(config);
141
142
143
144
145 auto verification_service = std::make_shared<svc::verification_scp>();
146 server->register_service(verification_service);
147
148 std::cout << "Registered services:\n";
149 for (const auto& sop_class : server->supported_sop_classes()) {
150 if (sop_class == "1.2.840.10008.1.1") {
151 std::cout << " - Verification SCP (1.2.840.10008.1.1)\n";
152 } else {
153 std::cout << " - " << sop_class << "\n";
154 }
155 }
156
158
159
160
161
162
163
164
165
166
167
168
169
170
172
173 std::cout << "DICOM Association lifecycle:\n";
174 std::cout << " 1. A-ASSOCIATE-RQ -> Client requests connection\n";
175 std::cout << " 2. A-ASSOCIATE-AC -> Server accepts\n";
176 std::cout << " 3. DIMSE exchange -> C-ECHO, C-STORE, etc.\n";
177 std::cout << " 4. A-RELEASE -> Clean disconnection\n\n";
178
179
180 std::atomic<int> connection_count{0};
181 std::atomic<int> total_connections{0};
182
183
184
185 server->on_association_established(
187 connection_count++;
188 total_connections++;
189 std::cout << "[" << current_timestamp() << "] "
193 << " (active: " << connection_count << ")\n";
194 }
195 );
196
197
198
199 server->on_association_released(
201 connection_count--;
202 std::cout << "[" << current_timestamp() << "] "
206 << " (active: " << connection_count << ")\n";
207 }
208 );
209
210
211
212 server->on_error(
213 [](const std::string& error_msg) {
214 std::cerr << "[" << current_timestamp() << "] "
217 << error_msg << "\n";
218 }
219 );
220
222
223
224
225
226
227
228
229
231
232
233
236 << "Shutdown signal received..."
238 server->stop();
239 });
240
241
242 auto start_result = server->start();
243 if (start_result.is_err()) {
245 start_result.error().message);
246 return 1;
247 }
248
249
251 "Echo Server Running",
252 "",
253 "Test with DCMTK:",
254 " echoscu -aec ECHO_SCP localhost " + std::to_string(config.
port),
255 "",
256 "Test multiple connections:",
257 " for i in {1..5}; do echoscu -aec ECHO_SCP localhost " +
258 std::to_string(config.
port) +
"; done",
259 "",
260 "Press Ctrl+C to stop"
261 });
262
263
264
266
267
268
269
270
271
272
274
275 auto stats = server->get_statistics();
276
278 {"Total Associations", std::to_string(stats.total_associations)},
279 {"Rejected Associations", std::to_string(stats.rejected_associations)},
280 {"Messages Processed", std::to_string(stats.messages_processed)},
281 {"Bytes Received", std::to_string(stats.bytes_received)},
282 {"Bytes Sent", std::to_string(stats.bytes_sent)},
283 {"Uptime", std::to_string(stats.uptime().count()) + " seconds"}
284 });
285
286
288 "Congratulations! You have learned:",
289 "",
290 "1. Server Configuration - AE Title, port, timeouts",
291 "2. Service Registration - SCP services for DIMSE handling",
292 "3. Association Lifecycle - Connect, exchange, release",
293 "4. Event Callbacks - Monitoring and logging",
294 "5. Graceful Shutdown - Clean resource cleanup",
295 "",
296 "Next step: Level 3 - Storage Server (C-STORE SCP)"
297 });
298
300
301 return 0;
302}
DICOM Association management class.
std::string_view calling_ae() const noexcept
Get calling AE title.
std::string_view called_ae() const noexcept
Get called AE title.
void wait()
Block until shutdown signal is received.
constexpr const char * reset
Reset all formatting.
constexpr const char * red
constexpr const char * yellow
constexpr const char * green
constexpr const char * cyan
void print_error(std::string_view message)
Print an error message.
void print_table(std::string_view title, const std::vector< std::pair< std::string, std::string > > &rows)
Print a key-value table.
void print_header(std::string_view title)
Print a header/title with decorative border.
void print_success(std::string_view message)
Print a success message.
void print_section(std::string_view title)
Print a section header.
void print_box(const std::vector< std::string > &lines)
Print text in a decorative box.
Configuration for DICOM server.
size_t max_associations
Maximum concurrent associations (0 = unlimited)
std::chrono::seconds idle_timeout
Idle timeout for associations (0 = no timeout)
uint32_t max_pdu_size
Maximum PDU size for data transfer.
std::string implementation_version_name
Implementation Version Name.
uint16_t port
Port to listen on (default: 11112, standard alternate DICOM port)
std::string ae_title
Application Entity Title for this server (16 chars max)
std::string implementation_class_uid
Implementation Class UID.