Refactored project structure, added multithreaded echo server and prime checking server, updated tests.
This commit is contained in:
8
.idea/cprotohackers.iml
generated
8
.idea/cprotohackers.iml
generated
@@ -1,2 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Python" name="Python facet">
|
||||
<configuration sdkName="Python 3.13" />
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.13" />
|
||||
</component>
|
||||
<component name="CMakePythonSetting">
|
||||
<option name="pythonIntegrationState" value="YES" />
|
||||
</component>
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
project(cprotohackers C)
|
||||
project(server00 C)
|
||||
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
|
||||
add_executable(cprotohackers main.c
|
||||
main.h
|
||||
|
||||
set(COMMON_SOURCES
|
||||
data.c
|
||||
data.h)
|
||||
target_link_libraries(cprotohackers wsock32 ws2_32)
|
||||
|
||||
|
||||
add_executable(server00 server00.c
|
||||
server00.h
|
||||
${COMMON_SOURCES})
|
||||
target_link_libraries(server00 wsock32 ws2_32)
|
||||
|
||||
find_package(json-c CONFIG REQUIRED)
|
||||
|
||||
|
||||
add_executable(server01 server01.c
|
||||
server01.h
|
||||
${COMMON_SOURCES})
|
||||
|
||||
target_link_libraries(server01 PRIVATE wsock32 ws2_32 json-c::json-c)
|
||||
58
data.c
58
data.c
@@ -6,6 +6,25 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define PORT 40000
|
||||
SOCKET get_listen_socket() {
|
||||
WSADATA WSAData;
|
||||
|
||||
SOCKET client;
|
||||
|
||||
SOCKADDR_IN serverAddr, clientAddr;
|
||||
|
||||
WSAStartup(MAKEWORD(2,0), &WSAData);
|
||||
SOCKET server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
serverAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(PORT);
|
||||
|
||||
bind(server, (SOCKADDR *)&serverAddr, sizeof(serverAddr));
|
||||
listen(server, 0);
|
||||
return server;
|
||||
}
|
||||
|
||||
char_array_t *char_array_create(int capacity) {
|
||||
char_array_t *array = malloc(sizeof(char_array_t));
|
||||
@@ -22,10 +41,11 @@ void char_array_append(char_array_t *array, char *value, size_t length) {
|
||||
if (array->data == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
printf("old size: %llu\n", array->size);
|
||||
printf("length: %llu\n", length);
|
||||
// char *temp = calloc(length+1, sizeof(char));
|
||||
// memcpy(temp, value, length);
|
||||
// printf("Appending '%s' bytes\n", temp);
|
||||
// free(temp);
|
||||
size_t new_size = array->size + length;
|
||||
printf("New size: %llu\n", new_size);
|
||||
if (new_size > array->capacity) {
|
||||
array->capacity = new_size;
|
||||
char *new_array = realloc(array->data, array->capacity);
|
||||
@@ -47,3 +67,35 @@ void char_array_wipe(char_array_t *array) {
|
||||
array->size = 0;
|
||||
memset(array->data, 0, array->capacity);
|
||||
};
|
||||
|
||||
bool char_array_has_char(char_array_t *array, char c) {
|
||||
const char *ret = memchr(array->data, c, array->size);
|
||||
if (ret == NULL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
char *char_array_get_until_char(char_array_t *array, char c) {
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i < array->size; i++) {
|
||||
if (array->data[i] == c) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx == 0) {
|
||||
return NULL;
|
||||
}
|
||||
char *ret = malloc(idx+1);
|
||||
memcpy(ret, array->data, idx);
|
||||
ret[idx] = '\0';
|
||||
char *temp = calloc(array->capacity, sizeof(char));
|
||||
size_t new_i = 0;
|
||||
for (size_t i = idx+1; i < array->size; i++) {
|
||||
temp[new_i++] = array->data[i];
|
||||
}
|
||||
free(array->data);
|
||||
array->data = temp;
|
||||
array->size = new_i;
|
||||
return ret;
|
||||
};
|
||||
8
data.h
8
data.h
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
|
||||
#include <winsock2.h>
|
||||
typedef struct CharArray {
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
@@ -11,4 +11,8 @@ char_array_t *char_array_create(int size);
|
||||
void char_array_destroy(char_array_t *array);
|
||||
void char_array_append(char_array_t *array, char *value, size_t length);
|
||||
void char_array_print(const char_array_t *array);
|
||||
void char_array_wipe(char_array_t *array);
|
||||
void char_array_wipe(char_array_t *array);
|
||||
bool char_array_has_char(char_array_t *array, char c);
|
||||
char *char_array_get_until_char(char_array_t *array, char c);
|
||||
|
||||
SOCKET get_listen_socket();
|
||||
55
echo_server_test.py
Normal file
55
echo_server_test.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import socket
|
||||
import time
|
||||
|
||||
def test_echo_server(host='localhost', port=40000, message="Hello, Echo Server!"):
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
try:
|
||||
s.connect((host, port))
|
||||
print(f"Connected to {host}:{port}")
|
||||
|
||||
# Send the message
|
||||
print(f"Sending message of length: {len(message)}")
|
||||
s.sendall(message.encode())
|
||||
s.shutdown(socket.SHUT_WR)
|
||||
|
||||
|
||||
# Receive the response in chunks
|
||||
chunks = []
|
||||
bytes_received = 0
|
||||
while bytes_received < len(message):
|
||||
chunk = s.recv(1024)
|
||||
if not chunk:
|
||||
break
|
||||
chunks.append(chunk)
|
||||
bytes_received += len(chunk)
|
||||
time.sleep(0.1) # Match the server's Sleep(100)
|
||||
|
||||
response = b''.join(chunks).decode()
|
||||
print(f"Received {len(response)} bytes")
|
||||
|
||||
# Check if it's a true echo
|
||||
if response == message:
|
||||
print("SUCCESS: Received message matches sent message!")
|
||||
else:
|
||||
print("FAILURE: Received message differs from sent message!")
|
||||
if len(response) != len(message):
|
||||
print(f"Length mismatch: sent {len(message)}, received {len(response)}")
|
||||
|
||||
except ConnectionRefusedError:
|
||||
print("Connection failed! Make sure the server is running.")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test with a simple message
|
||||
test_echo_server()
|
||||
|
||||
# Test with a longer message
|
||||
test_echo_server(message="This is a longer message to test the echo server's ability to handle larger strings!")
|
||||
|
||||
# Test with special characters
|
||||
test_echo_server(message="Special chars: !@#$%^&*()")
|
||||
|
||||
# Test with a large message
|
||||
large_message = "X" * 10000 # 10KB of data
|
||||
test_echo_server(message=large_message)
|
||||
79
main.c
79
main.c
@@ -1,79 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <winsock2.h>
|
||||
#include <pthread.h>
|
||||
#include "main.h"
|
||||
#include "data.h"
|
||||
|
||||
#define PORT 40000
|
||||
#define BUFFER_SIZE 1024
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
// load_file();
|
||||
|
||||
WSADATA WSAData;
|
||||
|
||||
SOCKET client;
|
||||
|
||||
SOCKADDR_IN serverAddr, clientAddr;
|
||||
|
||||
WSAStartup(MAKEWORD(2,0), &WSAData);
|
||||
SOCKET server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
serverAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(PORT);
|
||||
|
||||
bind(server, (SOCKADDR *)&serverAddr, sizeof(serverAddr));
|
||||
listen(server, 0);
|
||||
|
||||
printf("Listening for incoming connections...\n");
|
||||
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
int clientAddrSize = sizeof(clientAddr);
|
||||
int recieved;
|
||||
char_array_t *reply = char_array_create(1024);
|
||||
while((client = accept(server, (SOCKADDR *)&clientAddr, &clientAddrSize)) != INVALID_SOCKET)
|
||||
{
|
||||
printf(" Client connected!\n");
|
||||
while ((recieved = recv(client, buffer, sizeof(buffer), 0)) > 0) {
|
||||
printf(" Client says: |%.1024s|\n",buffer);
|
||||
char_array_append(reply, buffer, recieved);
|
||||
if (buffer[recieved] == '\0') {
|
||||
break;
|
||||
}
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
printf(" Sending back...\n");
|
||||
|
||||
printf(" Sending back: |%d| \n", reply->size);
|
||||
send(client, reply->data,reply->size,0);
|
||||
char_array_wipe(reply);
|
||||
|
||||
closesocket(client);
|
||||
printf("Client disconnected.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void echo(SOCKET client) {
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
char_array_t *reply = char_array_create(BUFFER_SIZE);
|
||||
int bytesReceived;
|
||||
while ((bytesReceived = recv(client, buffer, sizeof(buffer), 0)) > 0) {
|
||||
printf(" Client says: |%.1024s|\n",buffer);
|
||||
char_array_append(reply, buffer, bytesReceived);
|
||||
if (buffer[bytesReceived] == '\0') {
|
||||
break;
|
||||
}
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
Sleep(10);
|
||||
}
|
||||
printf(" Sending back...\n");
|
||||
|
||||
printf(" Sending back: |%llu| \n", reply->size);
|
||||
send(client, reply->data,reply->size,0);
|
||||
closesocket(client);
|
||||
}
|
||||
21
main.h
21
main.h
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// Created by Ajurna on 27/07/2025.
|
||||
//
|
||||
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#endif //MAIN_H
|
||||
|
||||
|
||||
typedef struct ReplyArray {
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
char *data;
|
||||
} array_t;
|
||||
|
||||
array_t *array_create(int size);
|
||||
void array_destroy(array_t *array);
|
||||
void array_append(array_t *array, char *value, size_t length);
|
||||
void array_print(const array_t *array);
|
||||
void array_wipe(array_t *array);
|
||||
84
parallel_echo_client_test.py
Normal file
84
parallel_echo_client_test.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import random
|
||||
import string
|
||||
|
||||
def random_string(length):
|
||||
"""Generate a random string of fixed length"""
|
||||
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
|
||||
|
||||
def echo_client(client_id, message):
|
||||
"""Individual client function that connects and sends/receives data"""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
try:
|
||||
s.connect(('localhost', 40000))
|
||||
print(f"Client {client_id}: Connected")
|
||||
|
||||
# Send the message
|
||||
print(f"Client {client_id}: Sending message of length {len(message)}")
|
||||
s.sendall(message.encode())
|
||||
|
||||
# Receive the response in chunks
|
||||
chunks = []
|
||||
bytes_received = 0
|
||||
while bytes_received < len(message):
|
||||
chunk = s.recv(1024)
|
||||
if not chunk:
|
||||
break
|
||||
chunks.append(chunk)
|
||||
bytes_received += len(chunk)
|
||||
time.sleep(0.1)
|
||||
|
||||
response = b''.join(chunks).decode()
|
||||
|
||||
# Verify response
|
||||
if response == message:
|
||||
print(f"Client {client_id}: SUCCESS - Message verified ({len(response)} bytes)")
|
||||
else:
|
||||
print(f"Client {client_id}: FAILURE - Message mismatch!")
|
||||
print(f"Client {client_id}: Expected length: {len(message)}, Got: {len(response)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Client {client_id}: Error - {str(e)}")
|
||||
|
||||
def run_parallel_test(num_clients=5, message_size_range=(100, 5000)):
|
||||
"""Run multiple clients in parallel"""
|
||||
threads = []
|
||||
|
||||
print(f"Starting parallel test with {num_clients} clients...")
|
||||
|
||||
# Create and start threads
|
||||
for i in range(num_clients):
|
||||
# Generate random message size between min and max range
|
||||
msg_size = random.randint(*message_size_range)
|
||||
message = random_string(msg_size)
|
||||
|
||||
thread = threading.Thread(target=echo_client, args=(i, message))
|
||||
threads.append(thread)
|
||||
thread.start()
|
||||
# Small delay between starting threads
|
||||
time.sleep(0.1)
|
||||
|
||||
# Wait for all threads to complete
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
print("All clients finished")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test 1: Few clients with small messages
|
||||
print("\nTest 1: 3 parallel clients with small messages")
|
||||
run_parallel_test(num_clients=3, message_size_range=(100, 500))
|
||||
|
||||
time.sleep(1) # Wait between tests
|
||||
|
||||
# Test 2: More clients with varying message sizes
|
||||
print("\nTest 2: 5 parallel clients with varying message sizes")
|
||||
run_parallel_test(num_clients=5, message_size_range=(1000, 5000))
|
||||
|
||||
time.sleep(1) # Wait between tests
|
||||
|
||||
# Test 3: Stress test with many clients
|
||||
print("\nTest 3: Stress test with 10 parallel clients")
|
||||
run_parallel_test(num_clients=10, message_size_range=(500, 10000))
|
||||
163
prime_time_server_tests.py
Normal file
163
prime_time_server_tests.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import socket
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
import random
|
||||
|
||||
def send_request(sock, request):
|
||||
"""Send a request and get response"""
|
||||
request_str = json.dumps(request) + '\n'
|
||||
sock.sendall(request_str.encode())
|
||||
|
||||
response = sock.recv(1024).decode()
|
||||
try:
|
||||
return json.loads(response.strip())
|
||||
except json.JSONDecodeError:
|
||||
return response.strip()
|
||||
|
||||
def batch_test_client():
|
||||
"""Run a test with 50 problems in quick succession"""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
try:
|
||||
sock.connect(('localhost', 40000))
|
||||
print("Batch Test: Connected")
|
||||
|
||||
# Generate 50 test cases with known prime and non-prime numbers
|
||||
test_numbers = (
|
||||
# Known primes
|
||||
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] +
|
||||
# Non-primes
|
||||
[4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25] +
|
||||
# Edge cases
|
||||
[0, 1, -1, 1.5, 2.0] +
|
||||
# Larger numbers
|
||||
[97, 100, 121, 169, 289, 361, 529, 841, 961, 1001] +
|
||||
# Random numbers to complete 50
|
||||
[random.randint(1000, 10000) for _ in range(5)]
|
||||
)
|
||||
|
||||
success_count = 0
|
||||
start_time = time.time()
|
||||
|
||||
for i, number in enumerate(test_numbers):
|
||||
request = {"method": "isPrime", "number": number}
|
||||
|
||||
try:
|
||||
response = send_request(sock, request)
|
||||
expected_prime = (
|
||||
isinstance(number, int) and
|
||||
number > 1 and
|
||||
all(number % i != 0 for i in range(2, int(number ** 0.5) + 1))
|
||||
)
|
||||
expected = {"method": "isPrime", "prime": expected_prime}
|
||||
|
||||
if response == expected:
|
||||
success_count += 1
|
||||
print(f"✓ Test {i+1}/50: {number} -> {response['prime']}")
|
||||
else:
|
||||
print(f"✗ Test {i+1}/50: {number} Expected: {expected}, Got: {response}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error on test {i+1}/50 with number {number}: {e}")
|
||||
break
|
||||
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
|
||||
print(f"\nBatch Test Results:")
|
||||
print(f"Total tests: 50")
|
||||
print(f"Successful: {success_count}")
|
||||
print(f"Failed: {50 - success_count}")
|
||||
print(f"Time taken: {duration:.2f} seconds")
|
||||
print(f"Average time per request: {(duration/50)*1000:.2f} ms")
|
||||
|
||||
except ConnectionRefusedError:
|
||||
print("Batch Test: Connection failed! Is the server running?")
|
||||
|
||||
def test_client(test_cases, client_id):
|
||||
"""Run a series of test cases"""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
try:
|
||||
sock.connect(('localhost', 40000))
|
||||
print(f"Client {client_id}: Connected")
|
||||
|
||||
for i, test in enumerate(test_cases):
|
||||
request = test['request']
|
||||
expected = test['expected']
|
||||
desc = test.get('desc', 'No description')
|
||||
|
||||
print(f"\nClient {client_id}: Test {i + 1} - {desc}")
|
||||
print(f"Request: {request}")
|
||||
|
||||
try:
|
||||
response = send_request(sock, request)
|
||||
print(f"Response: {response}")
|
||||
|
||||
if test.get('should_disconnect', False):
|
||||
try:
|
||||
send_request(sock, {"method": "isPrime", "number": 2})
|
||||
print("ERROR: Server didn't disconnect when it should have")
|
||||
except:
|
||||
print("SUCCESS: Server correctly disconnected")
|
||||
break
|
||||
|
||||
if response == expected:
|
||||
print("SUCCESS: Response matches expected")
|
||||
else:
|
||||
print(f"FAILURE: Expected {expected}, got {response}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during test: {e}")
|
||||
if test.get('should_disconnect', False):
|
||||
print("SUCCESS: Expected disconnect occurred")
|
||||
else:
|
||||
print("FAILURE: Unexpected error")
|
||||
break
|
||||
|
||||
except ConnectionRefusedError:
|
||||
print(f"Client {client_id}: Connection failed! Is the server running?")
|
||||
|
||||
def run_all_tests():
|
||||
print("Starting Prime Time Server tests...")
|
||||
|
||||
# Test 1: Basic functionality
|
||||
print("\nTest 1: Single client with valid requests")
|
||||
test_client([{
|
||||
'desc': "Basic prime test",
|
||||
'request': {"method": "isPrime", "number": 7},
|
||||
'expected': {"method": "isPrime", "prime": True}
|
||||
}], 0)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Test 2: Malformed request
|
||||
print("\nTest 2: Single client with malformed request")
|
||||
test_client([{
|
||||
'desc': "Malformed request test",
|
||||
'request': {"method": "wrong", "number": 7},
|
||||
'expected': "malformed",
|
||||
'should_disconnect': True
|
||||
}], 0)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Test 3: Batch test with 50 problems
|
||||
print("\nTest 3: Batch test with 50 problems")
|
||||
batch_test_client()
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Test 4: Multiple parallel clients
|
||||
print("\nTest 4: Multiple parallel clients")
|
||||
threads = []
|
||||
for i in range(5):
|
||||
thread = threading.Thread(target=batch_test_client)
|
||||
threads.append(thread)
|
||||
thread.start()
|
||||
time.sleep(0.1)
|
||||
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_all_tests()
|
||||
68
server00.c
Normal file
68
server00.c
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <stdio.h>
|
||||
#include <winsock2.h>
|
||||
#include <pthread.h>
|
||||
#include "server00.h"
|
||||
#include "data.h"
|
||||
|
||||
#define PORT 40000
|
||||
#define BUFFER_SIZE 1024
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
// load_file();
|
||||
|
||||
WSADATA WSAData;
|
||||
|
||||
SOCKET client;
|
||||
|
||||
SOCKADDR_IN serverAddr, clientAddr;
|
||||
|
||||
WSAStartup(MAKEWORD(2,0), &WSAData);
|
||||
SOCKET server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
serverAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(PORT);
|
||||
|
||||
bind(server, (SOCKADDR *)&serverAddr, sizeof(serverAddr));
|
||||
listen(server, 0);
|
||||
|
||||
printf("Listening for incoming connections...\n");
|
||||
|
||||
int clientAddrSize = sizeof(clientAddr);
|
||||
int connection_number = 0;
|
||||
char_array_t *reply = char_array_create(1024);
|
||||
while((client = accept(server, (SOCKADDR *)&clientAddr, &clientAddrSize)) != INVALID_SOCKET)
|
||||
{
|
||||
struct EchoArgs *args = malloc(sizeof(struct EchoArgs));
|
||||
args->client = client;
|
||||
args->connection = connection_number++;
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, nullptr, echo,(void *) args);
|
||||
}
|
||||
}
|
||||
|
||||
void *echo(void *args) {
|
||||
const auto echoArgs = (struct EchoArgs *) args;
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
char_array_t *reply = char_array_create(BUFFER_SIZE);
|
||||
int bytesReceived;
|
||||
int bytesTotal = 0;
|
||||
while ((bytesReceived = recv(echoArgs->client, buffer, sizeof(buffer), 0)) > 0) {
|
||||
// printf(" Client {%d} says: |%.1024s|\n", echoArgs->connection ,buffer);
|
||||
printf("Client sent {%d}: |%d| \n", echoArgs->connection, bytesReceived);
|
||||
|
||||
char_array_append(reply, buffer, bytesReceived);
|
||||
bytesTotal += bytesReceived;
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
Sleep(20);
|
||||
}
|
||||
printf("Total bytes received {%d}: %d\n", echoArgs->connection, bytesTotal);
|
||||
printf("Sending back {%d}: |%llu| \n", echoArgs->connection, reply->size);
|
||||
send(echoArgs->client, reply->data,reply->size,0);
|
||||
closesocket(echoArgs->client);
|
||||
free(echoArgs);
|
||||
return NULL;
|
||||
}
|
||||
17
server00.h
Normal file
17
server00.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by Ajurna on 27/07/2025.
|
||||
//
|
||||
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#endif //MAIN_H
|
||||
#include <winsock2.h>
|
||||
#pragma once
|
||||
|
||||
struct EchoArgs {
|
||||
int connection;
|
||||
SOCKET client;
|
||||
};
|
||||
|
||||
void *echo(void *args);
|
||||
124
server01.c
Normal file
124
server01.c
Normal file
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// Created by PeterDwyer on 28/07/2025.
|
||||
//
|
||||
|
||||
#include "server01.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <json-c/json_tokener.h>
|
||||
#include <json-c/json_object.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <winsock2.h>
|
||||
#include "data.h"
|
||||
|
||||
#define IS_PRIME "{\"method\":\"isPrime\",\"prime\":true}\n"
|
||||
#define IS_NOT_PRIME "{\"method\":\"isPrime\",\"prime\":false}\n"
|
||||
#define IS_PRIME_ERROR "{\"method\":\"isPrime\",\"error\":true}\n"
|
||||
int main() {
|
||||
SOCKET server = get_listen_socket();
|
||||
SOCKADDR_IN serverAddr, clientAddr;
|
||||
SOCKET client;
|
||||
int clientAddrSize = sizeof(clientAddr);
|
||||
int connection_number = 1;
|
||||
while((client = accept(server, (SOCKADDR *)&clientAddr, &clientAddrSize)) != INVALID_SOCKET)
|
||||
{
|
||||
handle_args_t *args = malloc(sizeof(handle_args_t));
|
||||
args->client = client;
|
||||
args->connection = connection_number++;
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, nullptr, handle_connection, args);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *handle_connection(void *args) {
|
||||
handle_args_t *handleArgs = args;
|
||||
char buffer[1024] = {0};
|
||||
int bytesReceived;
|
||||
char_array_t *data = char_array_create(1024);
|
||||
while ((bytesReceived = recv(handleArgs->client, buffer, sizeof(buffer), 0)) > 0) {
|
||||
printf("Client sent {%d}: |%d| \n", handleArgs->connection, bytesReceived);
|
||||
char_array_append(data, buffer, bytesReceived);
|
||||
char *request;
|
||||
while ((request = char_array_get_until_char(data, '\n')) != NULL) {
|
||||
parse_request(handleArgs, request, data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void parse_request(handle_args_t *handleArgs, const char *request, char_array_t *data) {
|
||||
if (request != NULL) {
|
||||
const json_object *obj = json_tokener_parse(request);
|
||||
if (obj == NULL) {
|
||||
printf("{%d} Failed to parse json\n", handleArgs->connection);
|
||||
send(handleArgs->client, IS_PRIME_ERROR, strlen(IS_PRIME_ERROR), 0);
|
||||
char_array_destroy(data);
|
||||
closesocket(handleArgs->client);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
json_object *method_obj = json_object_object_get(obj, "method");
|
||||
json_object *number_obj = json_object_object_get(obj, "number");
|
||||
if (method_obj == NULL || number_obj == NULL) {
|
||||
printf("{%d} Missing Fields\n", handleArgs->connection);
|
||||
send(handleArgs->client, IS_PRIME_ERROR, strlen(IS_PRIME_ERROR), 0);
|
||||
char_array_destroy(data);
|
||||
closesocket(handleArgs->client);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
const int number_type = json_object_get_type(number_obj);
|
||||
if (!(json_object_is_type(method_obj, json_type_string) && (number_type== json_type_double || number_type == json_type_int))) {
|
||||
printf("{%d} Fields Wrong Type\n", handleArgs->connection);
|
||||
send(handleArgs->client, IS_PRIME_ERROR, strlen(IS_PRIME_ERROR), 0);
|
||||
char_array_destroy(data);
|
||||
closesocket(handleArgs->client);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
const char *method = json_object_get_string(method_obj);
|
||||
if (strcmp(method, "isPrime") != 0) {
|
||||
printf("{%d} Invalid method\n", handleArgs->connection);
|
||||
send(handleArgs->client, IS_PRIME_ERROR, strlen(IS_PRIME_ERROR), 0);
|
||||
char_array_destroy(data);
|
||||
closesocket(handleArgs->client);
|
||||
}
|
||||
if (number_type == json_type_double) {
|
||||
printf("{%d} Floats cannot be prime\n", handleArgs->connection);
|
||||
send(handleArgs->client, IS_NOT_PRIME, strlen(IS_NOT_PRIME), 0);
|
||||
}
|
||||
const int number = json_object_get_int(number_obj);
|
||||
bool number_is_prime;
|
||||
if (number == INT32_MAX) {
|
||||
number_is_prime = false;
|
||||
} else {
|
||||
number_is_prime = is_prime(number);
|
||||
}
|
||||
|
||||
printf("{%d} '%d' is prime: %d\n", handleArgs->connection, number, number_is_prime);
|
||||
char *response = number_is_prime ? IS_PRIME : IS_NOT_PRIME;
|
||||
send(handleArgs->client, response, strlen(response), 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_prime(const int number) {
|
||||
if (number == 2) {
|
||||
return true;
|
||||
}
|
||||
if (number < 2 || number % 2 == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int limit = (int)sqrt(number);
|
||||
for (int i = 3; i <= limit; i+=2) {
|
||||
if (number % i == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
21
server01.h
Normal file
21
server01.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by PeterDwyer on 28/07/2025.
|
||||
//
|
||||
|
||||
#ifndef SERVER01_H
|
||||
#define SERVER01_H
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "data.h"
|
||||
#pragma once
|
||||
|
||||
typedef struct HandleArgs {
|
||||
int connection;
|
||||
SOCKET client;
|
||||
} handle_args_t;
|
||||
|
||||
#endif //SERVER01_H
|
||||
|
||||
bool is_prime(const int number);
|
||||
void *handle_connection(void *args);
|
||||
void parse_request(handle_args_t *handleArgs, const char *request, char_array_t *data);
|
||||
Reference in New Issue
Block a user