mirror of
https://github.com/Hizenberg469/Network-Programming-in-C.git
synced 2026-04-19 17:22:24 +03:00
poll() function complete
This commit is contained in:
33
Advanced_Technique/poll_func.c
Normal file
33
Advanced_Technique/poll_func.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
|
||||
int main(){
|
||||
|
||||
struct pollfd pfds[1]; // More if you want to monitor more
|
||||
|
||||
pfds[0].fd = 0; //Standard input
|
||||
pfds[0].events = POLLIN;// Tell me when ready to read
|
||||
|
||||
// If you needed to monitor other things, as well:
|
||||
// pfds[1].fd = some_socket; // Some socket descriptor
|
||||
// pfds[1].events = POLLIN; // Tell me when ready to read
|
||||
|
||||
printf("Hit RETURN or wait 2.5 seconds for timeout\n");
|
||||
|
||||
int num_events = poll(pfds, 1, 2500); // 2.5 second timeout
|
||||
|
||||
if(num_events == 0){
|
||||
printf("Poll timed out!\n");
|
||||
}
|
||||
else{
|
||||
int pollin_happened = pfds[0].revents & POLLIN;
|
||||
|
||||
if( pollin_happened ){
|
||||
printf("File descriptor %d is ready to read\n", pfds[0].fd);
|
||||
}
|
||||
else{
|
||||
printf("Unexpected event occured: %d\n", pfds[0].revents);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
215
Advanced_Technique/pollserver.c
Normal file
215
Advanced_Technique/pollserver.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
** pollserver.c -- a multiperson chat server
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
|
||||
#define PORT "9034" // Port we're listening on
|
||||
|
||||
// Get sockaddr, IPv4 or IPv6:
|
||||
void *get_in_addr(struct sockaddr *sa){
|
||||
if( sa->sa_family == AF_INET){
|
||||
return &(((struct sockaddr_in*)sa)->sin_addr);
|
||||
}
|
||||
|
||||
return &(((struct sockaddr_in6*)sa)->sin6_addr);
|
||||
}
|
||||
|
||||
// Return a listening socket
|
||||
int get_listener_socket(void){
|
||||
int listener; // Listening socket descriptor
|
||||
int yes=1; // For setsockopt() SO_REUSEADDR, below
|
||||
int rv;
|
||||
|
||||
struct addrinfo hints, *ai, *p;
|
||||
|
||||
// Get use a socket and bind it
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
if( (rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0){
|
||||
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for(p = ai; p != NULL; p = p->ai_next){
|
||||
listener = socket( p->ai_family, p->ai_socktype, p->ai_protocol);
|
||||
if( listener < 0 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lose the pesky "address already in use" error message
|
||||
|
||||
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
||||
|
||||
if(bind(listener, p->ai_addr, p->ai_addrlen) < 0){
|
||||
close(listener);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(ai); // All done with this
|
||||
|
||||
// If we got here, it means we didn't get bound
|
||||
if( p == NULL ){
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Listener
|
||||
if(listen(listener, 10) == -1){
|
||||
return -1;
|
||||
}
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
// Add a new file descriptor to the set
|
||||
void add_to_pfds(struct pollfd *pfds[], int newfd, int *fd_count, int *fd_size){
|
||||
|
||||
// If we don't have room, add more space in the pfds array
|
||||
if(*fd_count == *fd_size){
|
||||
*fd_size *= 2; // Double it
|
||||
|
||||
*pfds = realloc(*pfds, sizeof(**pfds) * (*fd_size));
|
||||
}
|
||||
|
||||
(*pfds)[*fd_count].fd = newfd;
|
||||
(*pfds)[*fd_count].events = POLLIN; // Check ready-to-read
|
||||
|
||||
(*fd_count)++;
|
||||
}
|
||||
|
||||
// Remove an index from the set
|
||||
void del_from_pfds(struct pollfd pfds[], int i, int *fd_count){
|
||||
|
||||
// Copy the one from the end over this one
|
||||
pfds[i] = pfds[*fd_count - 1];
|
||||
|
||||
(*fd_count)--;
|
||||
}
|
||||
|
||||
// Main
|
||||
int main(void){
|
||||
|
||||
int listener; // Listening socket descriptor
|
||||
|
||||
int newfd; // Newly accept()ed socket descriptor
|
||||
struct sockaddr_storage remoteaddr; // Client address
|
||||
socklen_t addrlen;
|
||||
|
||||
char buf[256]; // Buffer for client data
|
||||
|
||||
char remoteIP[INET6_ADDRSTRLEN];
|
||||
|
||||
// Start off with room for 5 connections
|
||||
// (We'll realloc as necessary)
|
||||
int fd_count = 0;
|
||||
int fd_size = 5;
|
||||
struct pollfd *pfds = malloc(sizeof *pfds * fd_size);
|
||||
|
||||
// Set up and get a listening socket
|
||||
listener = get_listener_socket();
|
||||
|
||||
if(listener == -1){
|
||||
fprintf(stderr, "error getting listening socket\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Add the listener to set
|
||||
pfds[0].fd = listener;
|
||||
pfds[0].events = POLLIN; // Report ready to read on incoming connection
|
||||
|
||||
fd_count = 1; // For the listener
|
||||
|
||||
// Main loop
|
||||
for(;;){
|
||||
int poll_count = poll(pfds, fd_count, -1);
|
||||
|
||||
if( poll_count == -1){
|
||||
perror("poll");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Run through the existing connections looking for data to read
|
||||
for(int i = 0; i < fd_count; i++){
|
||||
|
||||
// Check if someone's ready to read
|
||||
if( pfds[i].revents & POLLIN){
|
||||
// We get one !!
|
||||
|
||||
if(pfds[i].fd == listener){
|
||||
// If listener is ready to read, handle new connection
|
||||
|
||||
addrlen = sizeof remoteaddr;
|
||||
newfd = accept(listener,
|
||||
(struct sockaddr *)&remoteaddr,
|
||||
&addrlen);
|
||||
|
||||
if( newfd == -1){
|
||||
perror("accept");
|
||||
}
|
||||
else{
|
||||
add_to_pfds(&pfds, newfd, &fd_count, &fd_size);
|
||||
|
||||
printf("pollserver: new connection from %s on socket %d\n",
|
||||
inet_ntop(remoteaddr.ss_family,
|
||||
get_in_addr((struct sockaddr *)&remoteaddr),
|
||||
remoteIP, INET6_ADDRSTRLEN), newfd);
|
||||
|
||||
}
|
||||
}
|
||||
else{
|
||||
// If not the listener, we're just a regular client
|
||||
int nbytes = recv(pfds[i].fd, buf, sizeof buf, 0);
|
||||
|
||||
int sender_fd = pfds[i].fd;
|
||||
|
||||
if( nbytes <= 0){
|
||||
// Got error or connection closed by client
|
||||
|
||||
if( nbytes == 0){
|
||||
// Connection closed
|
||||
printf("pollserver: socket %d hung up\n", sender_fd);
|
||||
}
|
||||
else{
|
||||
perror("recv");
|
||||
}
|
||||
|
||||
close(pfds[i].fd); // Bye!
|
||||
|
||||
del_from_pfds( pfds, i, &fd_count);
|
||||
}
|
||||
else{
|
||||
// We got some good data from a client
|
||||
|
||||
for( int j = 0; j < fd_count ; j++ ){
|
||||
// Send to everyone!
|
||||
int dest_fd = pfds[j].fd;
|
||||
|
||||
// Except the listener and ourselves
|
||||
if( dest_fd != listener && dest_fd != sender_fd){
|
||||
if(send(dest_fd, buf, nbytes, 0) == -1){
|
||||
perror("send");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // END handle data from client
|
||||
} // END got ready-to-read from poll()
|
||||
} // END looping through file descriptor
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
98
Advanced_Technique/readme.md
Normal file
98
Advanced_Technique/readme.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Advanced Topics
|
||||
|
||||
These are some advance technique and functionality, which quite used in real world network programming in C in Unix environment.
|
||||
|
||||
## Blocking :-
|
||||
|
||||
Here, "block" is same as "sleep", in technical terms. Many functions we have discussed so far, some of them are blocking function call. For example, recvfrom() will be in blocking state until some data arrives.
|
||||
|
||||
Some of the functions that blocks are :
|
||||
|
||||
* recvfrom()
|
||||
* accept()
|
||||
* socket()
|
||||
|
||||
If you want socket to be non-blocking :
|
||||
|
||||
```
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
sockfd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
||||
```
|
||||
|
||||
Setting a socket to non-blocking, leads to "polling" socket for information. If we try reading from non-blocking socket and there's no data there, it's not allowed to block it will return -1 and `errno` will be set to `EAGAIN` or `EWOULDBLOCK`.
|
||||
|
||||
> [!NOTE]
|
||||
> The return value `EAGAIN` or `EWOULDBLOCK` is implementation dependent
|
||||
> and we should check both for portability.
|
||||
|
||||
However, this way of polling is not a great idea because it will be in constant busy-wait state and will be sucking out CPU time. However, the solution to this is using of `poll()`.
|
||||
|
||||
## poll() --- Synchronous I/O Multiplexing
|
||||
|
||||
* Purpose :
|
||||
|
||||
It is used to monitor a *bunch* of sockets at once and then handle the ones that have data ready.
|
||||
|
||||
> [!WARNING]
|
||||
> *poll()* is horribly slow when it comes to a giant number of connections.
|
||||
> In those circumstances, *libevent* will give better performance. It's a
|
||||
> event notification library.
|
||||
|
||||
* Function Prototype :
|
||||
|
||||
```
|
||||
#include <poll.h>
|
||||
|
||||
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
|
||||
```
|
||||
|
||||
1. fds[] :
|
||||
|
||||
Type : `struct pollfd`
|
||||
|
||||
Value : Array of file descriptor stored in *struct pollfd*.
|
||||
|
||||
Purpose : Each element store information about a particular fd, with *bitmap* of events we're interested in and also contain another buffer *bitmap* to stores the events that has occured.
|
||||
|
||||
2. nfds :
|
||||
|
||||
Type : `int`
|
||||
|
||||
Value : Count of releavent fds.
|
||||
|
||||
Purpose : To keep the count of releavent fds and discard the rest.
|
||||
|
||||
3. timeout :
|
||||
|
||||
Type : `int`
|
||||
|
||||
Value : timeout in millisecond.
|
||||
|
||||
Purpose : Time period to monitor this fds and let the process go to sleep, giving all the dirty work to the OS.
|
||||
|
||||
* Returns :
|
||||
|
||||
returns the number of elements in the array that have had an event occur.
|
||||
|
||||
After the poll() returns, check the `revents` field to see if POLLIN or POLLOUT is set, indicating the event occurred.
|
||||
|
||||
> [!NOTE]
|
||||
> Structure of `struct pollfd`
|
||||
>
|
||||
> ```
|
||||
> struct pollfd{
|
||||
> int fd; // the socket descriptor
|
||||
> short events; // bitmap of events we're interested in
|
||||
> short revents; // when poll() returns, bitmap of events that occured.
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> The events field is the bitwise-OR of the following:
|
||||
>
|
||||
> POLLIN - Alert me when data is ready to recv() on this socket.
|
||||
>
|
||||
> POLLOUT - Alert me when I can send() data to this socket without blocking.
|
||||
|
||||
Reference in New Issue
Block a user