KEMBAR78
10.3 Networking MultipleClients C | PDF | Network Socket | Software Engineering
0% found this document useful (0 votes)
9 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)
9 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
You are on page 1/ 13

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