Skip to content

Development_Environment

rcutte edited this page Jun 6, 2024 · 2 revisions

Development Environment

Tests

NGINX

Use NGINX as a web server to test behaviour and compare with the program because there are both in HTTP 1.1.

You can use the following command to start an NGINX server in a Docker container:

docker run --rm -d -p 8080:80 --name nginx nginx

This command will start an NGINX server on port 8080. You can then access the server by visiting http://localhost:8080 in your web browser.

Allowed Functions

All in CPP98 standard library. (no boost)

External functions allowed

Function Prototype Description Example
execve
int execve(
  const char *filename,
  char *const argv[],
  char *const envp[]);
Executes the program pointed to by filename.
char *args[] = {"/bin/ls", "-l", NULL};
execve(args[0], args, NULL);
dup
int dup(int oldfd);
Creates a copy of the file descriptor oldfd.
int newfd = dup(oldfd);
dup2
int dup2(
  int oldfd, 
  int newfd);
Makes newfd be the copy of oldfd, closing newfd first if necessary.
dup2(oldfd, newfd);
close
int close(int fd);
Closes the file descriptor fd.
close(fd);
fork
pid_t fork(void);
Creates a new process by duplicating the calling process.
pid_t pid = fork();
waitpid
pid_t waitpid(
  pid_t pid, 
  int *status, 
  int options);
Waits for a specific child process to terminate.
int status;
pid_t pid = waitpid(child_pid, &status, 0);
wait
pid_t wait(int *status);
Waits for any child process to terminate.
int status;
pid_t pid = wait(&status);
signal
void (
  *signal(
    int signum,
    void (*handler)(int))
  )(int);
Assigns a signal handler to a signal.
void handler(int signum) {
  // Handle signal
}

signal(SIGINT, handler);
kill
int kill(
  pid_t pid, 
  int sig);
Sends a signal to a process.
kill(pid, SIGKILL);
socket
int socket(
  int domain,
  int type,
  int protocol);
Creates an endpoint for communication.
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
bind
int bind(
  int sockfd,
  const struct sockaddr *addr,
  socklen_t addrlen);
Assigns an address to a socket.
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;

bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
listen
int listen(
  int sockfd,
  int backlog);
Marks a socket as passive, allowing it to accept incoming connections.
listen(sockfd, 10);
accept
int accept(
  int sockfd,
  struct sockaddr *addr,
  socklen_t *addrlen);
Accepts a new incoming connection on a socket.
int newsockfd = accept(sockfd, NULL, NULL);
connect
int connect(
  int sockfd,
  const struct sockaddr *addr,
  socklen_t addrlen);
Initiates a connection on a socket.
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080); // Port number
inet_pton(AF_INET, "127.0.0.1", &(serv_addr.sin_addr)); // Server IP address
connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
send
ssize_t send(
  int sockfd,
  const void *buf,
  size_t len,
  int flags);
Sends data on a connected socket.
send(sockfd, "Hello, world!", 13, 0);
recv
ssize_t recv(
  int sockfd,
  void *buf,
  size_t len,
  int flags);
Receives data from a connected socket.
char buffer[1024];
recv(sockfd, buffer, 1024, 0);
select
int select(
  int nfds,
  fd_set *readfds,
  fd_set *writefds,
  fd_set *exceptfds,
  struct timeval *timeout);
Waits for one of a set of file descriptors to become ready to perform I/O.
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);

select(sockfd + 1, &readfds, NULL, NULL, NULL);
poll
int poll(
  struct pollfd *fds,
  nfds_t nfds,
  int timeout);
Waits for one of a set of file descriptors to become ready to perform I/O.
struct pollfd fds;
fds.fd = sockfd;
fds.events = POLLIN;

poll(&fds, 1, -1);
getaddrinfo
int getaddrinfo(
  const char *node,
  const char *service,
  const struct addrinfo *hints,
  struct addrinfo **res);
Resolves a host name and service name to a set of socket addresses.
struct addrinfo hints, *res;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

getaddrinfo("www.example.com", "http", &hints, &res);
freeaddrinfo
void freeaddrinfo(struct addrinfo *res);
Frees the memory allocated for a linked list of addrinfo structures.
freeaddrinfo(res);
htons
uint16_t htons(uint16_t hostshort);
Converts a 16-bit hostshort from host byte order to network byte order.
uint16_t port = 8080;
uint16_t network_port = htons(port);
htonl
uint32_t htonl(uint32_t hostlong);
Converts a 32-bit hostlong from host byte order to network byte order.
uint32_t address = 0x7F000001; //
uint32_t network_address = htonl(address);
epoll_create
int epoll_create(int size);
Creates an epoll instance.
int epfd = epoll_create(1);
epoll_ctl
int epoll_ctl(
  int epfd,
  int op,
  int fd,
  struct epoll_event *event);
Controls an epoll instance.
struct epoll_event event;

event.events = EPOLLIN;
event.data.fd = sockfd;

epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
epoll_wait
int epoll_wait(
  int epfd,
  struct epoll_event *events,
  int maxevents,
  int timeout);
Waits for events on an epoll instance.
struct epoll_event events[10];
int n = epoll_wait(epfd, events, 10, -1);

... and more.

Poll and Select

The poll() and select() functions are used to monitor multiple file descriptors, waiting until one or more of the file descriptors become ready for I/O operations. Here's a brief explanation of each function:

  • poll(): The poll() function waits for one of a set of file descriptors to become ready to perform I/O. It is similar to select(), but it provides more flexibility and is more efficient for large numbers of file descriptors. The poll() function is available on most Unix-like operating systems.

  • select(): The select() function waits for one of a set of file descriptors to become ready to perform I/O. It is commonly used in network programming to monitor multiple sockets for read/write events. The select() function is available on most Unix-like operating systems.

Both functions allow you to specify a timeout value, so you can wait for a certain amount of time before returning if no file descriptors become ready. They also allow you to specify sets of file descriptors to monitor for read, write, and error events.

Epoll

The epoll API is a more scalable and efficient mechanism for monitoring multiple file descriptors in Unix-like operating systems. It provides better performance than poll() and select() for large numbers of file descriptors. Here's a brief explanation of the epoll API:

  • epoll_create(): The epoll_create() function creates an epoll instance, which is used to monitor file descriptors for I/O events.

  • epoll_ctl(): The epoll_ctl() function controls an epoll instance, allowing you to add, modify, or remove file descriptors from the set of monitored descriptors.

  • epoll_wait(): The epoll_wait() function waits for events on an epoll instance, returning information about the file descriptors that are ready for I/O operations.

Allowed Macros

The FD_SET, FD_CLR, FD_ISSET, and FD_ZERO macros are essential tools for working with the select() function in Unix-like operating systems. They operate on fd_set structures, which are used to track the readiness of file descriptors for various I/O operations. Here's a brief explanation of each macro:

Macro Prototype Description
FD_SET
void FD_SET(int fd, fd_set *set);

Adds the file descriptor fd to the set set. It marks the file descriptor as ready for the operation specified in the select() call (e.g., reading, writing). This is useful for initializing the set of file descriptors you're interested in monitoring.

FD_CLR
void FD_CLR(int fd, fd_set *set);

Removes the file descriptor fd from the set set. It clears the mark indicating readiness for the specified operation. This is useful when you want to stop monitoring a particular file descriptor.

FD_ISSET
int FD_ISSET(int fd, fd_set *set);

Checks if a given file descriptor is part of a file descriptor set. It returns a non-zero value if the file descriptor is marked as ready for the specified operation, and 0 otherwise. This is used after calling select() to determine which file descriptors are now ready for the requested operation.

FD_ZERO
void FD_ZERO(fd_set *set);

Initializes a file descriptor set to have zero bits for all file descriptors. It effectively clears all previously added file descriptors from the set. This is usually called before adding specific file descriptors to the set using FD_SET.

These macros are part of the mechanism that allows select() to monitor multiple file descriptors simultaneously, waiting for them to become ready for reading, writing, or having an error condition. By manipulating these sets, you can dynamically control which file descriptors select() should consider.

Clone this wiki locally