I apologize if this question seems trivial, but I'm a beginner at both C and programming with sockets. Also, english is not my native language.
I recently failed an uni assignment that asked me to implement a TCP server-client in C that accepts both ipv4 and ipv6, in which the client would send messages to add, remove, list and query pairs of ints from a linked list stored in the server. So, I made the server and the client with the codes below (not included are the code for the linked lists and for the function strsplit):
server (servidor.c)
void func(int sockfd)
{
struct Node* posts = NULL;
char client_message[MAX];
char server_message[MAX];
char* str;
char **p;
int X, Y, i, j, k;
int n = 0;
char sX[5];
char sY[5];
for (;;)
{
recv(sockfd, client_message, sizeof(client_message), 0);
printf("> %s", client_message);
strcpy(str, client_message);
p = strsplit(str, " ");
memset(server_message, 0, MAX);
if(strncmp("kill", p[0], 4) == 0)
{
break;
}
else if(strncmp("add", p[0], 3) == 0)
{
if(n == 50)
{
strncat(server_message, "limit exceeded", 14);
}
else
{
strncat(server_message, p[1], 4);
strncat(server_message, " ", 1);
X = atoi(p[1]);
strtok(p[2], "\n");
strncat(server_message, p[2], 4);
strncat(server_message, " ", 1);
Y = atoi(p[2]);
i = push(&posts, X, Y);
if(i == 0)
{
strncat(server_message, "added", 5);
n++;
}
else
{
strncat(server_message, "already exists", 14);
}
free(p);
}
}
else if(strncmp("rm", p[0], 2) == 0)
{
strncat(server_message, p[1], 4);
strncat(server_message, " ", 1);
X = atoi(p[1]);
strtok(p[2], "\n");
strncat(server_message, p[2], 4);
strncat(server_message, " ", 1);
Y = atoi(p[2]);
i = deleteNode(&posts, X, Y);
if(i == 0)
{
strncat(server_message, "removed", 7);
n--;
}
else
{
strncat(server_message, "does not exist", 14);
}
free(p);
}
else if(strncmp("list", p[0], 4) == 0)
{
if(posts == NULL)
{
strncat(server_message, "none", 4);
}
else
{
struct Node *temp = posts;
while(temp != NULL)
{
bzero(sX, 4);
bzero(sY, 4);
snprintf(sX, sizeof(sX), "%d",temp->X);
snprintf(sY, sizeof(sY), "%d",temp->Y);
strncat(server_message, sX, 4);
strncat(server_message, " ", 1);
strncat(server_message, sY, 4);
strncat(server_message, " ", 1);
temp = temp->next;
}
free(temp);
}
}
else if(strncmp("query", p[0], 5) == 0)
{
if(posts == NULL)
{
strncat(server_message, "none", 4);
}
else
{
struct Node *temp1 = posts;
k = 19998;
X = atoi(p[1]);
strtok(p[2], "\n");
Y = atoi(p[2]);
while(temp1 != NULL)
{
i = abs(temp1->X - X);
j = abs(temp1->Y - Y);
i = i + j;
if(i <= k)
{
snprintf(sX, sizeof(sX), "%d",temp1->X);
snprintf(sY, sizeof(sY), "%d",temp1->Y);
k = i;
}
temp1 = temp1->next;
}
strncat(server_message, sX, 4);
strncat(server_message, " ", 1);
strncat(server_message, sY, 4);
strncat(server_message, " ", 1);
free(temp1);
}
}
strncat(server_message, "\n", 1);
printf("%s", server_message);
send(sockfd, server_message, sizeof(server_message), 0);
}
}
int main(int argc, char *argv[])
{
if(strncmp("v4", argv[1], 2) == 0)
{
int sockfd, connfd, len;
struct sockaddr_in servaddr, cli;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(atoi(argv[2]));
if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0)
{
exit(0);
}
if ((listen(sockfd, 5)) != 0)
{
exit(0);
}
len = sizeof(cli);
connfd = accept(sockfd, (SA*)&cli, &len);
if (connfd < 0)
{
exit(0);
}
func(connfd);
close(sockfd);
}
else if(strncmp("v6", argv[1], 2) == 0)
{
int sockfd, connfd, len;
struct sockaddr_in6 servaddr, cli;
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
if(sockfd == -1)
{
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin6_family = AF_INET6;
servaddr.sin6_addr = in6addr_any;
servaddr.sin6_port = htons(atoi(argv[2]));
if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0)
{
exit(0);
}
if ((listen(sockfd, 5)) != 0)
{
exit(0);
}
len = sizeof(cli);
connfd = accept(sockfd, (SA*)&cli, &len);
if (connfd < 0)
{
exit(0);
}
func(connfd);
close(sockfd);
}
return 0;
}
client (cliente.c)
void func(int sockfd)
{
char buff[MAX];
int n;
for (;;)
{
bzero(buff, sizeof(buff));
n = 0;
while ((buff[n++] = getchar()) != '\n')
;
send(sockfd, buff, sizeof(buff), 0);
if ((strncmp(buff, "kill", 4)) == 0)
{
break;
}
bzero(buff, sizeof(buff));
recv(sockfd, buff, sizeof(buff), 0);
printf("> %s", buff);
}
}
int main(int argc, char *argv[])
{
if(strchr(argv[1], ':') != NULL)
{
int sockfd, connfd;
struct sockaddr_in6 servaddr, cli;
struct in6_addr result;
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
if (sockfd == -1)
{
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin6_family = AF_INET6;
inet_pton(AF_INET6, argv[1], &result);
servaddr.sin6_addr = result;
servaddr.sin6_port = htons(atoi(argv[2]));
if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0)
{
exit(0);
}
func(sockfd);
close(sockfd);
}
else
{
int sockfd, connfd;
struct sockaddr_in servaddr, cli;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
servaddr.sin_port = htons(atoi(argv[2]));
if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0)
{
exit(0);
}
func(sockfd);
close(sockfd);
}
return 0;
}
Testing on my own, by opening the server and the client in separate terminals and sending messages one by one, the server seemed to be working as intended. However, my professor says otherwise. After failing the assignment, he provided me with the python code of the software used to grade my server, which is as follows:
run_tests.py
def run_basic_tests(exec_path, version, port, msg_type):
address = '127.0.0.1' if version == 'v4' else '::1'
list_dir = os.listdir('tests/in')
list_dir = sorted(list_dir)
for filename in list_dir:
filename = filename.split('.')[0]
_, result_file_path = tempfile.mkstemp()
server = sp.Popen(
[f'exec {exec_path} {version} {port}'], shell=True, stdout=sp.DEVNULL, stderr=sp.DEVNULL)
ret = os.system(
f'python3 client.py {address} {port} {msg_type} < tests/in/{filename}.in > {result_file_path}')
ret = os.WEXITSTATUS(ret)
if ret == 0:
if filecmp.cmp(f'tests/out/{filename}.out', result_file_path, shallow=False):
print(f'{filename}\t[OK]')
else:
print(f'{filename}\t[FAILED] (diff output)')
else:
print(f'{filename}\t[FAILED] (client exited with non-zero code {ret})')
os.remove(result_file_path)
server.kill()
if __name__ == '__main__':
if len(sys.argv) != 3:
print(f'usage: python3 {sys.argv[0]} <server> <port>')
sys.exit(0)
exec_path = sys.argv[1]
port = int(sys.argv[2])
if not exec_path.startswith('/'):
print('provide the full path to the executable')
sys.exit(0)
for address_family in ['v4', 'v6']:
for msg_type in ['single_msg_single_pkg', 'single_msg_multiple_pkg', 'multiple_msg_single_pkg']:
print('Testing IP' + address_family, msg_type)
run_basic_tests(exec_path, address_family, port, msg_type)
client.py
BUFFER = ''
def get_address_family(address):
try:
socket.inet_pton(socket.AF_INET, address)
return socket.AF_INET
except:
pass
try:
socket.inet_pton(socket.AF_INET6, address)
return socket.AF_INET6
except:
pass
sys.exit(1)
def create_socket(address, port):
address_family = get_address_family(address)
attempts = 0
client_socket = None
while attempts < 5:
try:
client_socket = socket.socket(address_family, socket.SOCK_STREAM)
client_socket.connect((address, port))
break
except socket.error:
client_socket = None
time.sleep(0.05)
attempts += 1
if not client_socket:
sys.exit(2)
return client_socket
# send one message
def send(client_socket, msg):
msg = msg.encode('ascii')
total_sent = 0
while total_sent != len(msg):
sent = client_socket.send(msg[total_sent:])
if sent == 0:
sys.exit(3)
total_sent += sent
# receive one complete message
def receive(client_socket):
global BUFFER
while True:
if '\n' in BUFFER:
msg = BUFFER[:BUFFER.index('\n')]
BUFFER = BUFFER[BUFFER.index('\n') + 1:]
return msg
try:
data = client_socket.recv(500 - len(BUFFER)).decode()
except socket.timeout:
sys.exit(7)
if not data:
sys.exit(4)
BUFFER += data
if len(BUFFER) >= 500:
sys.exit(5)
def run_multiple_msg_single_pkg(client_socket):
msg_buffer = []
for msg in sys.stdin:
msg_buffer.append(msg)
for i in range(0, len(msg_buffer), 2):
if i == len(msg_buffer) - 1:
send(client_socket, msg_buffer[i])
if msg_buffer[i] == 'kill\n':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)
else:
msg = msg_buffer[i] + msg_buffer[i + 1]
send(client_socket, msg)
if msg_buffer[i] == 'kill\n':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)
if msg_buffer[i + 1] == 'kill\n':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)
def run_single_msg_multiple_pkg(client_socket):
for msg in sys.stdin:
send(client_socket, msg[:3])
time.sleep(0.1)
send(client_socket, msg[3:])
if msg == 'kill\n':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)
def run_single_msg_single_pkg(client_socket):
for msg in sys.stdin:
send(client_socket, msg)
if msg == 'kill\n':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)
if __name__ == '__main__':
if len(sys.argv) != 4:
print(
f'Usage: python3 {sys.argv[0]} <address> <port> [single_msg_single_pkg | single_msg_multiple_pkg | multiple_msg_single_pkg]')
sys.exit(6)
client_socket = create_socket(sys.argv[1], int(sys.argv[2]))
client_socket.settimeout(5)
if sys.argv[3] == 'single_msg_single_pkg':
run_single_msg_single_pkg(client_socket)
elif sys.argv[3] == 'single_msg_multiple_pkg':
run_single_msg_multiple_pkg(client_socket)
elif sys.argv[3] == 'multiple_msg_single_pkg':
run_multiple_msg_single_pkg(client_socket)
client_socket.close()
sys.exit(0)
Running my code with this software, none of the tests work. Basically, after receiving and sending the first message, my server halts communication with the client, failing every single test. This has probably something to do with how I'm treating the strings on my code, but honestly I'm at a loss here. Can someone at least give me a nudge in the right direction?