//
//An inline C sockets lecture (part 2).
//Niall McMahon, 2022.
//
//Client, host and port: 127.0.0.1:xxxx
// - The port number is randomly assigned by the OS. xxxx represents a 4-digit number.
// - If we choose, we can specify a port
// - Host address: 127.0.0.1
// - Socket address: 127.0.0.1:xxxx
//
//Server, host and port: 127.0.0.1:1234
// - Server process host and port must be known.
// - Host address: 127.0.0.1
// - Socket address: 127.0.0.1:1234
//

//This is code for a server, i.e. a process running on a central host (a server) that accepts connections from a client.
//The client and server can be processes running on the same machine.

//SERVER PROGRAM

//Standard input and output, stdio.h.
#include <stdio.h>
//String function library, string.h.
#include <string.h>
//The standard library, stdlib.h, which defines basic variables and functions.
#include <stdlib.h>
//unistd.h, miscellaneous symbolic constants and types.
#include <unistd.h>
//arpa/inet.h, defines structures and types for internet operations including in_port_t and in_addr_t
//See The Single UNIX Specification, Version 2.
#include <arpa/inet.h>
#include <sys/socket.h>
//There's some overlap between definitions provided by netinet/in.h and sys/socket.h and arpa.inet.h. For now assume we need to include it.
#include <netinet/in.h>

//Expecting an integer return when main is complete.
int main(){
//Define the server socket.

//SERVER SOCKET.
//create a new socket descriptor.
//Sockets are of type integer, corresponding to the file descriptor id.
//You define a socket like this:
// int socket(int af, int type, int protocol)
//Parameters for socket():
// 1. af(Address Family) AF_INET and AF_INET6
// - The AF_INET address family enables interprocess communication between processes on the same computer (in the transport layer) or processes running on different computers, across the internet.
// - For an AF_INET process that uses IP Version 4, the AF_INET family uses the sockaddr_in address structure. This is loaded from sys/socket.h.
// - See lecture notes about struct types.
// - See IBM's article about AF_INET.
// 2. type: SOCK_STREAM and SOCK_DGRAM
// - See lecture notes about streams and datagrams.
// 3. protocol: IPPROTO_TCP and IPPTOTO_UDP
// - Either TCP or UDP. Stream defaults to TCP and datagram defaults to UDP.
// - In this example, we've explicitly defined the protocol.
// - See lecture notes about TCP and UDP.
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


//SERVER SOCKET DEFINITION - further detail to define the socket.
//We collect together information about the server socket in a sockaddr_in struct.
//Again, IBM's article about AF_INET.
//See lecture notes about struct types.
//See lecture notes about the sockaddr_in struct.
//Create a new struct of type sockaddr_in called serv_addr.
struct sockaddr_in serv_addr;
//Fill the memory allocated to the serv_addr struct with zeroes. Pass in the address of serv_addr as the first parameter.
memset(&serv_addr, 0, sizeof(serv_addr));
//This socket uses the AF_INET address family - see note above.
serv_addr.sin_family = AF_INET;
//Set the IP address. Remember that sin_addr is of type union. s_addr specifies that the IP address will be stored as a 4 byte integer.
//See lecture notes about the sockaddr_in struct.
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//Set the port number used by the server process.
//See lecture notes about htons.
serv_addr.sin_port = htons(1234);

//In this case, different to the client, we (attempt to) associate, or bind, the local socket to a name, i.e. a sockaddr_in structure that includes the host and port.
//Use the bind() call.
// - See bind() — Bind a name to a socket.
// - See lecture notes about bind()
// - The second parameter is a pointer to a sockaddr struct. The () changes, or casts, the type of &serv_addr to this pointer type.
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

//Once the socket is associated with a name, listen() indicates a readiness to accept client connections and creates a backlog of incoming requests.
//You listen like this:
// listen(int socket, int backlog)
//Where:
// 1. socket is the file descriptor id.
// 2. backlog is the number of allowed waiting connection requests. If the number of client connection requests exceeds the backlog, the connection requests are rejected.
//In our our example, we allow up to 20 queued connection requests.
//See listen() — Prepare the server for incoming client requests.
listen(serv_sock, 20);

//The code then receives a request from the client.
//First of all describe the client with a sockaddr_in name structure.
//Initialise a client sockaddr_in structure called clnt_addr:
struct sockaddr_in clnt_addr;

//Now use sizeof() to determine the size of clnt_addr in memory.
//This is of type socklen_t and is stored in the clnt_addr_size variable.
socklen_t clnt_addr_size = sizeof(clnt_addr);

//The accept() call creates a new socket descriptor; this will have the same properties as serv_sock in our example. The new socket descriptor is returned to the caller.
//This new socket descriptor is associated with the address structure provided by the client.
//So the server socket is associated with an address by bind() and listen() identifies this socket as accepting client connections.
//See accept() — Accept a new connection on a socket.
//As a note sockaddr is similar to sockaddr_in - sockaddr_in is for IPV4. struct sockaddr* is equivalent to struct sockaddr_in*
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);

//Once the connection has been accepted, the server can write to the client socket.
//str is a char array containing a message.
char str[] = "This is from server";
//This is written to the client socket.
write(clnt_sock, str, sizeof(str));

//Once this is done, the client socket is closed.
close(clnt_sock);
//And the server socket is closed.
close(serv_sock);
//And the program finishes.
return 0;
}