Skip to content

Instantly share code, notes, and snippets.

@influentcoder
Created December 9, 2020 01:12
Show Gist options
  • Save influentcoder/35ea175e90c8ff44454eefc4a581efad to your computer and use it in GitHub Desktop.
Save influentcoder/35ea175e90c8ff44454eefc4a581efad to your computer and use it in GitHub Desktop.
Echo TCP Server With Epoll
from datetime import datetime
import socket
import sys
import time
def run():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 4444))
msg = "[{}] Hello world!!".format(str(datetime.now())).encode("UTF-8")
print("Sending: " + str(msg))
sock.sendall(msg)
received = 0
data = b""
while received < len(msg):
buff = sock.recv(16)
received += len(buff)
data += buff
print("Received: " + str(data))
sock.close()
if __name__ == "__main__":
run()
time.sleep(1)
run()
#include <stdlib.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
// So we can use accept4()
#define _GNU_SOURCE 1
int main()
{
int port = 4444;
/**
* Create a socket in the kernel, and get a file descriptor back so we can refer to
* it later.
*/
// Internet socket, that binds to a port (as opposed to, for example, a Unix domain
//socket which binds to a file).
int socketDomain = AF_INET;
// In our case, TCP protocol, as opposed to SOCK_DGRAM which is UDP.
int socketType = SOCK_STREAM;
// No need to specify in our case, with SOCK_STREAM the TCP protocol is
// automatically picked up.
int protocol = 0;
int socketFd = socket(socketDomain, socketType, protocol);
if (socketFd == -1) {
perror("Error when creating a socket");
exit(EXIT_FAILURE);
}
/**
* Bind the socket to the port.
*/
struct sockaddr_in sockAddr;
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(port); // htons => convert to network-byte order
sockAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(socketFd, (struct sockaddr *) &sockAddr, sizeof(sockAddr)) < 0) {
perror("Error on binding");
exit(EXIT_FAILURE);
}
/**
* Set the socket in listening mode so it can accept connections.
*/
// Backlog of unprocessed data that the socket can accept before sending an error to
// the client.
int backlog = 4096;
if (listen(socketFd, backlog) < 0) {
perror("Error on listen");
exit(EXIT_FAILURE);
}
/**
* Create a new "epoll" instance in the kernel.
* We interact using this epoll instance using its file descriptor.
*/
int epollFlags = 0;
int epollFd = epoll_create1(epollFlags);
if (epollFd == -1) {
perror("Error when creating epoll FD");
exit(EXIT_FAILURE);
}
/**
* Register an interest on the listening socket, so that we are notified when data
* comes in.
*/
int op = EPOLL_CTL_ADD; // We want to register interest on a file descriptor.
struct epoll_event ev;
ev.events = EPOLLIN; // For read operations.
ev.data.fd = socketFd;
if (epoll_ctl(epollFd, op, socketFd, &ev) == -1) {
perror("Error on epoll_ctl");
exit(EXIT_FAILURE);
}
printf("Listening on %d...\n", port);
int n, nfds, connSockFd, readSize, sentSize, readBufSize = 1024, timeout = -1,
maxEvents = 10;
int readBuf[readBufSize];
struct epoll_event events[maxEvents];
for (;;) {
// This is a blocking call. We have registered interests in listening in some
// file descriptors, and when data is available, this will return the number of
// file descriptors having available data. The file descriptors themselves are
// populated in "events".
nfds = epoll_wait(epollFd, events, maxEvents, timeout);
if (nfds == -1) {
perror("Error on epoll_wait");
exit(EXIT_FAILURE);
}
// Process each file descriptor having available data.
for (n = 0; n < nfds; ++n) {
// If the data is available on the listening socket, this means we have a
// new incoming connection with a client. We then accept the connection,
// which creates a dedicated socket to talk to this client. We then need to
// register this dedicated socket in epoll as we are interested to see when
// data is available, i.e. the client sent some data on the socket.
if (events[n].data.fd == socketFd) {
connSockFd = accept4(socketFd, NULL, 0, SOCK_NONBLOCK);
if (connSockFd == -1) {
perror("Error on accept");
exit(EXIT_FAILURE);
}
ev.data.fd = connSockFd;
// Register interest on the dedicated socket.
if (epoll_ctl(epollFd, op, connSockFd, &ev) == -1) {
perror("Error on epoll_ctl");
exit(EXIT_FAILURE);
}
} else {
// If the file descriptor is not the listening socket, it must be a
// dedicated socket for a specific client.
// Read the data from the socket, up to "readBufSize" bytes, and store
// the data in "readBuf". The number of bytes read is stored in
// "readSize".
readSize = read(events[n].data.fd, readBuf, readBufSize);
if (readSize == -1) {
perror("Error on read");
exit(EXIT_FAILURE);
}
// Send the data to the same socket, i.e. back to the client. We send
// the same read buffer, and readSize bytes.
sentSize = send(events[n].data.fd, readBuf, readSize, MSG_NOSIGNAL);
if (sentSize == -1) {
perror("Error on send");
exit(EXIT_FAILURE);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment