0% found this document useful (0 votes)
11 views13 pages

10.3 Networking MultipleClients C

The document discusses methods for handling multiple TCP client connections in a server environment. It outlines three main approaches: using a thread per connection, implementing non-blocking sockets, and utilizing kernel notifications for I/O multiplexing with epoll. Each method has its pros and cons, with epoll being highlighted as an efficient way to manage multiple connections without the overhead of threads or busy-waiting.

Uploaded by

akaaljot.mathoda
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views13 pages

10.3 Networking MultipleClients C

The document discusses methods for handling multiple TCP client connections in a server environment. It outlines three main approaches: using a thread per connection, implementing non-blocking sockets, and utilizing kernel notifications for I/O multiplexing with epoll. Each method has its pros and cons, with epoll being highlighted as an efficient way to manage multiple connections without the overhead of threads or busy-waiting.

Uploaded by

akaaljot.mathoda
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Networking:

Multiple Clients

25-03-23 CMPT 201 Slides 10.3 © Dr. B. Fraser 1


Topics


How can one program handle (very?) many requests?
– Specifically a server handle many TCP clients?

25-03-23 2
TCP Server Recap

Recall that on a TCP server:
– We open the first socket and call accept()
– accept() will return
file descriptor for
.. a brand new sochet
the new

client

How can we make our server work with multiple client
connection
sockets?

25-03-23 3
Idea 1: Thread per Connection

Idea 1: Thread
por connection
.. server
trad child process for each

creates a new

cusfited
– This thread handles the new client's socket. connection

Pros:
– Handle multiple clients cleanly. For multiple child process
Enreads lighter tran isolated
Cons:
are
can le
memory
● processes
– .. crushing one doesn't apput
Higher ou
head others

processes / threads ↓
creation
du to
of nu
Beet
is navy
on
memory

25-03-23 4

Ablocking socket is the default mode for sockets In it the ,

until that operation is completed I blocks the


program stops execution
Idea 2: Non-Blocking Sockets

Non-blocking accept() will either:
a) accept a new connection immediately or
-
b) or return immediately if no incoming connection.
-
– Also use non-blocking read() and write() either
is will perform
= 0 or return
Idea 2: Non Blocking sochet

sockies

– .. mediately If To is not
create an
array of open posible
– General Idea: &
pull with
Server will infinitely loop through calling: non-blocking
alls

non-blocking-accept to add any new socket to array

non-blocking-read or non-bloccking-write (or both)
on each socket in array as needed
– Pros: Avoids creating new processes/threads This is
>
- called
– Cons: .. Loop keeps Chucking all sochets .

25-03-23
It's a
busy-wait loop .
polling 5
=>>

doesn't get stock


In non-blocking program ,
It can

continue doing other work


Idea 3: Kernel Notify on Socket Event

Idea 3: Ida
.. Combining "1 & 2 butter & efficient way ,

– Use non-blocking sockets and kernel notifies program on


socket events.

.. Fo Multiplessing
– Use syscalls to monitor multiple file descriptors.
– Program is notified when
.. a monitored descriptors is fils ready for a read i
,
writes)
– Use: select(), poll(), and epoll() or an

Error

25-03-23 6

• Busy-wait = You keep asking every table: “Are you ready to order?” (wasting time).

• I/O multiplexing = Customers raise a flag (kernel tells you): “Hey, I’m ready now!”
Idea 3: (cont)

Generally speaking, this is how I/O multiplexing works:
– We add file descriptors to the monitored list.
– We indicate what events we want to monitor the file
descriptors for, e.g., read and write.
– We call the blocking function to wait for an event,
e.g., select() or epoll()
– When it returns, check which file descriptors can perform I/O.
– We perform the I/O.

Pros:
– No thread overhead, no polling.

Cons:
– .. Code becomes more complex, to maintain a list
of
file descriptors to monitor
25-03-23 7
Idea 3: Implementing Sketch with epoll

3 Calls to implement I/O Multiplexing with epoll():
epoll_create()
– Returns an epoll instance.
– We can think of this as a
..
monitoring object that maintains the
monitoring list
epoll_ctl()
– Allows us to
.. Add remove
, or
modify a
file descriptor to spoll instance

Start by monitoring socket for accept()

Each new FD from accept() is added to set to monitor
epoll_wait()
– Waits for a file descriptor to be available for I/O

25-03-23 (Code in tcp_server_epoll.c) 8


1) int epfd = epoll_create1(0);

2) int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

struct epoll_event ev;


ev.events = EPOLLIN; // Monitor for readable data
ev.data.fd = sockfd; // Identify which socket this is
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);

int n = epoll_wait(epfd, events, MAX_EVENTS, timeout);


3)
• The second argument struct epoll_event *events is the most important one.
• It is a buffer passed to epoll_wait().
• Each entry is for a file descriptor that has something new to process. There can be multiple entries if different file descriptors each have something new to
process.
• This is the same struct epoll_event that you store with epoll_ctl() with its last argument (struct epoll_event *_Nullable event).
• The kernel stores struct epoll_event you pass to epoll_ctl(), and when the associated file descriptor has something new to process, the kernel returns it back
to you.
Waits for FDs in the epoll set to become ready.

• events is an array of struct epoll_event.

• The kernel fills this with FDs that have pending I/O.

- • Returns the number of ready events.

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUF_SIZE 100


#define PORT 8000
#define LISTEN_BACKLOG 32
#define MAX_EVENTS 10

#define handle_error(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
int main() {
struct sockaddr_in addr, remote_addr;
int sfd, cfd, epollfd;
int nfds;
ssize_t num_read;
socklen_t addrlen = sizeof(struct sockaddr_in);
char buf[BUF_SIZE];
struct epoll_event ev, events[MAX_EVENTS];

sfd = socket(AF_INET, SOCK_STREAM, 0);


if (sfd == -1)
handle_error("socket");

memset(&addr, 0, sizeof(struct sockaddr_in));


addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1)


handle_error("bind");

if (listen(sfd, LISTEN_BACKLOG) == -1)


handle_error("listen");

epollfd = epoll_create1(0);
if (epollfd == -1)
handle_error("epoll_create1");

ev.events = EPOLLIN | EPOLLOUT;


ev.data.fd = sfd; // save the accept socket
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, &ev) == -1)
handle_error("epoll_ctl");

for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1)
handle_error("epoll_wait");

for (int i = 0; i < nfds; ++i) {


if (events[i].data.fd == sfd) {
memset(&remote_addr, 0, sizeof(struct sockaddr_in));
cfd = accept(sfd, (struct sockaddr *)&remote_addr, &addrlen);
if (cfd == -1)
handle_error("accept");

// Set O_NONBLOCK
int flags = fcntl(cfd, F_GETFL, 0);
if (flags == -1)
handle_error("fcntl");
flags |= O_NONBLOCK;
if (fcntl(cfd, F_SETFL, flags) == -1)
handle_error("fcntl");

ev.events = EPOLLIN | EPOLLOUT;


ev.data.fd = cfd;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, cfd, &ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
while ((num_read = read(events[i].data.fd, buf, BUF_SIZE)) > 0) {
if (write(STDOUT_FILENO, buf, num_read) != num_read)
handle_error("write");

if (num_read == -1)
handle_error("read");
ABCD: Server choices

Match the server implementation idea with
the problem it suffers:
1) Non-blocking IO in a loop
2) epoll() to watch sockets
3) Thread per client

a) More complex code 2)


b) Only handle one socket at a time.
c) More likely to use too much system
3)
resources (such as RAM), or too high
kernel overhead. bes this
an also for i
d) Wastes CPU Time 17

25-03-23 9
Summary


accept() returns a new socket for each TCP client.

Server must likely handle many sockets at once:
– Can create a new thread per socket.
– Can use non-blocking IO to busy-wait checking for ready
sockets
– Can use epoll() or select() to have kernel monitor sockets

25-03-23 10
Summary


accept() returns a new socket for each TCP client.

Server must likely handle many sockets at once:
– Can create a new thread per socket.
– Can use non-blocking IO to busy-wait checking for ready
sockets
– Can use epoll() or select() to have kernel monitor sockets

25-03-23 10

You might also like