Make sure to check errors for every call, networking can fail at any point.
Make sure to set socket options to reuse to enable effective debugging of the server. Why does linux do this for sockets?
Most server applications are interupted through a signal, but you shouldn’t do all of the cleanup in the signal handler because not every function is signal handler safe (think back to CS233). That means the often pattern we see is like below.
int is_running = 1;
int handler(){
is_running = 0;
}
int main(){
while(is_running){ /*...*/};
close(...);
}
Try to modularize your functions so that everything is not in the main method. This is ideally because we need to tell the system that we are done using shared resources like sockets, and we need to determine when a socket goes “out of scope” – we don’t have RAII like in C++ so we have to determine that ourselves.
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
while(1) {
printf("Listening on port %s\n", portname);
ssize_t bytes_recd=recvfrom(fd,buffer,sizeof(buffer),0,(struct sockaddr*)&source,&source_len);
if (bytes_recd==-1) quit("recvfrom");
if(bytes_recd == source_len)
printf("Datagram > buffer - message truncated\n");
// Print buffer contents
write(1, buffer, bytes_recd);
write(1, "\n",1);
// Encrypt the message
for(int i=0; i < bytes_recd; i++) {
if( buffer[i] >= 64) buffer[i] ^= 1;
}
int flags = 0;
size_t bytes_sent = sendto(fd, buffer, bytes_recd, flags, (struct sockaddr*) &source, source_len);
if(bytes_sent==-1) {
quit("sendto");
}
if(bytes_sent == bytes_recd ) printf("Replied\n");
else quit("write");
}
return 0;
}