Sockets - Server & Client 2 - 2020
Continued from Socket - Server & Client
Sharing between processes - Sockets using PThreads
The port number and IP address used in the AF_INET socket address structure are expected to follow the network byte ordering (big-endian). This is the opposite of x86's little-endian byte ordering, so these values must be converted. There are specialized functions for the conversions, and they are defined in netinet.h and arpa/inet.h.
"Basically, we want to convert the numbers to Network Byte Order before they go out on the wire, and convert them to Host Byte Order as they come in off the wire."
htonl (long value) Host-to-Network Long
Converts a 32-bit integer from the host's byte order to network byte order.htons (short value) Host-to-Network Short
Converts a 16-bit integer from the host's byte order to network byte order.ntohl (long value) Network-to-Host Long
Converts a 32-bit integer from network byte order to the host's byte order.ntohs (short value) Network-to-Host Short
Converts a 16-bit integer from network byte order to the host's byte order.- For C code for the conversions, please visit
Little Endian/Big Endian & TCP Sockets.
inet_aton (char *ascii_addr, struct in_addr *network_addr) ASCII-to-Network
Converts an ASCII string containing IP address in dotted-number format into an in_addr, which only contains a 32-bit integer representing the IP address in network byte order.inet_ntoa (struct in_addr *network_addr) Network to ASCII
It is passed a pointer to an in_addr structure containing an IP address, and the function returns a character pointer to an ASCII string containing the IP address in dotted-number format. This string is held in a statically allocated memory buffer in the function, so it can be accessed until the next call to inet_ntoa(), and the string will be overwritten.
To establish a connection, TCP uses a three-way handshake. Before a client attempts to connect with a server, the server must first bind to and listen at a port to open it up for connections: this is called a passive open. Once the passive open is established, a client may initiate an active open.
In other words, before communication begins, TCP establishes a new connection using a three-way handshake. This is because
- Both sender and receiver must be ready before data transport starts, and they need to agree on set of parameters such as the Maximum Segment Size (MSS).
- TCP end points maintain state about communications in both directions, and the handshake allows the state to be created and initialized.
- TCP establishes a stream of bytes in both directions, and the three-way handshake allows both streams to be established and acknowledged.
To establish a connection, the three-way handshake occurs:
- SYN: The active open is performed by the client sending a SYN to the server.
- SYN-ACK: In response, the server replies with a SYN-ACK.
- ACK: Finally, the client sends an ACK back to the server.
The picture above: from TCP Fast Open: expediting web services
Here is the sample using Wireshark when I requested a page from apple.com, and then closed it:
It is also possible to terminate the connection by a 3-way handshake, more strictly it's a 2 (FIN/ACK) x 2 (FIN/ACK) handshake:
- host A sends a FIN
- host B replies with a ACK (with data) + FIN
- host A replies with an ACK
picture from wiki
- ipconfig (internet protocol configuration)
A console application that displays all current TCP/IP network configuration values.
In other words, it is commonly used to identify the addresses information of a computer on a network.
It can show the physical address as well as the IP address.
It can modify Dynamic Host Configuration Protocol DHCP and Domain Name System DNS settings.
- nslookup
A network administration command-line tool available for many computer operating systems for
querying the Domain Name System (DNS) to obtain domain name or IP address mapping or for any other specific DNS record.
- netstat(network statistics)
A command-line tool that displays network connections (both incoming and outgoing), routing tables, and a number of network interface (network interface controller or software-defined network interface) and network protocol statistics.
Simply put, tt provides useful information about the current TCP/IP settings of a connection.
- traceroute
Traceroute is a computer network diagnostic tool for displaying the route (path) and measuring transit delays of packets across an Internet Protocol (IP) network. Tracert is a Windows utility program that can used to trace the route taken by data from the router to the destination network. It also shows the number of hops taken during the entire transmission route.
Here's how traceroute works.
Traceroute probes successive hops in order to find the network path between a host that's doing the probing and a destination host which might be some remote web server. And, we want to find the path, the network path between our computer and the remote host server.
What traceroute does is it sends a packet towards that remote host, only a single hop onto the network. And then, causes the network to send a message back, or a reply back. Then, it sends a packet two hops into the network. It elicits a response from there, and so on. And eventually, the packet will reach the remote host, which will then send a response back.
This gives us information about what routers are between our computer and the host, the number of them, and the sequence in which they're organized.
The internet daemon (Disk And Execution MONitor) [xinetd/inetd] listens for connections on many ports at once. When a client connects to a service, the daemon program runs the appropriate server. So, this boils down to the need for servers to be running all the time.
xinetd can be configured by modifying its configuration file. They are typically in /etc/xinetd.conf and files in /etc/xinetd.d directory.
Here is the list all of the services that are enabled extracted by using grep -v "^#" /etc/inetd.conf
defaults { log_type = SYSLOG daemon info log_on_failure = HOST log_on_success = PID HOST DURATION EXIT cps = 50 10 instances = 50 per_source = 10 v6only = no groups = yes umask = 002 } includedir /etc/xinetd.d
As an example of xinetd configuration file, here is the daytime service:
# This is the configuration for the tcp/stream daytime service. service daytime { # This is for quick on or off of the service disable = yes # The next attributes are mandatory for all services id = daytime-stream type = INTERNAL wait = no socket_type = stream # protocol = socket type is usually enough ..... }
The daytime service that the getdate program connects to is actually handled by xinetd itself, and it can be made available using both SOCK_STREAM (tcp) and SOCK_DGRAM (udp) sockets.
Another example for file transfer service:
# default: off # description: The kerberized FTP server accepts FTP connections \ # that can be authenticated with Kerberos 5. service ftp { flags = REUSE socket_type = stream wait = no user = root server = /usr/kerberos/sbin/ftpd server_args = -l -a log_on_failure += USERID disable = yes }
The ftp file transfer service is aailable only via SOCK_STREAM sockets and is provided by external program, in this case gssftp. The daemon will start these external program when a client connects to the ftp port.
To activate service configuration, we can edit the xinetd configuration and send a hang-up signal to the daemon process. If a daemon process has a configuration file which is modified after the process has been started, there should be a way to tell that process to re-read its configuration file, without stopping the process. Many daemons provide this mechanism using the SIGHUP signal handler. When we want to tell the daemon to re-read the file we simply send it the SIGHUP signal.
killall -HUP xinetd
On a switched network, each packet moves between a computer and a port on a switch, or between two switches. It's the job of the switch to transmit a packet only when the line is clear, and only to the necessary ports. This way, it's like each computer is on it's own private network. We never have packet collisions and we leave as much of our network clear as possible. This means we get the most throughput possible out of our network.
On an unswitched network, all packets go to all ports and visible to all computers. If two computers want to send a packet at the same time, we have a collision. Both computers must resend, and that particular bit of bandwidth used for the broken transmission is completely wasted. This means that actual network throughput is much lower than the theoretical potential.
On an unswitched network, where Ethernet packets pass through every device on the network, expecting each system device to only look at the packets sent to its destination address. But it is quite trivial to set a device to promiscuous mode, which causes it to look at all packets, regardless of the destination address. Most packet-capturing codes, such as tcpdump, drop the device they are listening to into promiscuous mode by default. This promiscuous more can be set using ifconfig:
$ ifconfig eth0 eth0 Link encap:Ethernet HWaddr 00:1D:09:67:11:69 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:4186029513 errors:0 dropped:0 overruns:0 frame:0 TX packets:3343760394 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:1289597250 (1.2 GiB) TX bytes:332184455 (316.7 MiB) Interrupt:169 Memory:f8000000-f8012800
$ sudo ifconfig eth0 promisc $ ifconfig eth0 eth0 Link encap:Ethernet HWaddr 00:1D:09:67:11:69 UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1 RX packets:4186031551 errors:0 dropped:0 overruns:0 frame:0 TX packets:3343761162 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:1290474745 (1.2 GiB) TX bytes:332296284 (316.9 MiB) Interrupt:169 Memory:f8000000-f8012800
To go back to the state before:
$ sudo ifconfig eth1 -promisc
tcpdump allows us to save the packets that are captured, so that we can use it for future analysis. The saved file can be viewed by the same tcpdump command.
We can also use open source software like wireshark to read the tcpdump pcap (Packet CAPture) files (Unix-like systems implement pcap in the libpcap library).
Capturing packets sometimes called sniffing when it's not necessarily meant for public viewing. Sniffing packets in promiscuous more on an unswitched network can turn up lots of information.
However, our network is only unswitched within individual access points, and there is some provision in wifi to allow multiple access points in the same space.
In this section, we are accessing the network at lower level than transport/session layer. By using raw sockets, all the details are exposed and must be handled explicitly. Note that we've been using stream sockets, where the data is neatly wrapped in a TCP/IP connection. Accessing session layer, the OS takes care of all of the lower-level details of transmission, correction, and routing.
By specifying SOCK_RAW, we can use raw sockets. The protocol matters because there are multiple options. The protocol can be IPPROTO_TCP or IPPROTO_UDP. Here we are sniffing packet of TCP.
// sn.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> void packet_dump(const unsigned char *buf, const unsigned int len) { unsigned char c; int i,j; for(i = 0; i < len; i++) { printf("%02x ", buf[i]); if((i % 16) == 15 || (i == len-1)) { for(j = 0; j < 15 - (i % 16); j++) printf(" "); printf("| "); for(j = (i - (i % 16)); j <= i; j++) { c = buf[j]; if((c > 31) && (c < 127)) printf("%c", c); else printf("."); } printf("\n"); } } } int main() { int received_length, sock_fd; int i; u_char buf[5000]; if((sock_fd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1) { printf("errro: socket"); exit(1); } for(i = 0; i < 5; i++) { received_length = recv(sock_fd, buf, 4000, 0); printf("%d byte packet\n", received_length); packet_dump(buf, received_length); } }
We opened a raw TCP socket and listens for five packets, print out the raw data of each one with the packet_dump(). The recv() function receives a message from a socket. The recv() call can be used on a connection mode socket or a bound, connectionless socket. If no messages are available at the socket, the recv() call waits for a message to arrive unless the socket is nonblocking. If a socket is nonblocking, -1 is returned and the external variable errno is set to EWOULDBLOCK.
Here the buf is declared as a u_char type, and it's for our convenience since it's been repeatedly used in network programming. The u in u_char stands for unsigned. Actually, the char data-type isn't always used to store characters. Since char is the only data type whose size is always 1 byte on any platform, it is used often to store 1 byte data. 1 byte can hold 255 values but the regular char datatype is a signed type and hence stores values from -127 to 127 i.e. After 127, the number is represented in 2's complement notation and hence the numbers are represented as negative. To use only the values 0 to 255, the unsigned type is used. In this case, everything is considered as a positive number and 2's complement is not taken.
To run the compiled code, we need to have root privilege since the use of raw sockets requires it.
$ gcc -o sn sn.c $ sudo ./sn 40 byte packet 45 00 00 28 87 ee 40 00 74 06 dd 0c 63 43 d6 dd | E..(..@.t...cC.. d3 2b 94 88 07 ef 00 16 c5 bc d9 8c ab c6 ca 28 | .+.............( 50 10 fa 63 f6 5d 00 00 | P..c.].. 40 byte packet 45 00 00 28 87 f1 40 00 74 06 dd 09 63 43 d6 dd | E..(..@.t...cC.. d3 2b 94 88 07 ef 00 16 c5 bc d9 8c ab c6 cb 2c | .+............., 50 10 ff ff ef bd 00 00 | P....... 40 byte packet 45 00 00 28 87 f3 40 00 74 06 dd 07 63 43 d6 dd | E..(..@.t...cC.. d3 2b 94 88 07 ef 00 16 c5 bc d9 8c ab c6 cc 30 | .+.............0 50 10 fe fb ef bd 00 00 | P....... 40 byte packet 45 00 00 28 87 f4 40 00 74 06 dd 06 63 43 d6 dd | E..(..@.t...cC.. d3 2b 94 88 07 ef 00 16 c5 bc d9 8c ab c6 cd 34 | .+.............4 50 10 fd f7 ef bd 00 00 | P....... 40 byte packet 45 00 00 28 87 f5 40 00 74 06 dd 05 63 43 d6 dd | E..(..@.t...cC.. d3 2b 94 88 07 ef 00 16 c5 bc d9 8c ab c6 ce 38 | .+.............8 50 10 fc f3 ef bd 00 00 | P.......
We captured packets, but it is not reliable and will miss some of the packets in case when there is a lot of traffic. And we only captured TCP packets. To capture other packets such as UDP, we need to open additional raw sockets. The biggest issue of capturing raw socket packets is the dependency on OS.
For more tcpdump, check here.
libpcap was originally developed by the tcpdump developers in the Network Research Group at Lawrence Berkeley Laboratory. The low-level packet capture, capture file reading, and capture file writing code of tcpdump was extracted and made into a library, with which tcpdump was linked. It is now developed by the same tcpdump.org group that develops tcpdump.
The libpcap provides a portable framework for low-level network monitoring. It can provide network statistics collection, security monitoring and network debugging. Since almost every system vendor provides a different interface for packet capture, the libpcap authors created this system-independent API to ease in porting and to alleviate the need for several system-dependent packet capture modules in each application.
We can use the libpcap to smooth out the inconsistencies of raw sockets. While the library is still using raw sockets, it knows how to correctly work on with raw sockets on multiple platforms/architectures.
// pcap-sn.c #include <stdio.h> #include <stdlib.h> #include <pcap.h>; void packet_dump(const unsigned char *buf, const unsigned int len) { unsigned char c; int i,j; for(i = 0; i < len; i++) { printf("%02x ", buf[i]); if((i % 16) == 15 || (i == len-1)) { for(j = 0; j < 15 - (i % 16); j++) printf(" "); printf("| "); for(j = (i - (i % 16)); j <= i; j++) { c = buf[j]; if((c > 31) && (c < 127)) printf("%c", c); else printf("."); } printf("\n"); } } } int main() { struct pcap_pkthdr header; const u_char *packet; char err_buf[PCAP_ERRBUF_SIZE]; char *device; pcap_t *pcap_handle; int i; device = pcap_lookupdev(err_buf); if(!device) { printf("Error in %s: %s\n","pcap_lookupdev",err_buf); exit(1); } printf("Sniffing on device %s\n", device); pcap_handle = pcap_open_live(device, 4096, 1, 0, err_buf); if(!pcap_handle) { printf("Error in %s: %s\n","pcap_open_live",err_buf); exit(1); } for(i = 0; i < 5; i++) { packet = pcap_next(pcap_handle, &header;); printf("%d byte packet\n", header.len); packet_dump(packet, header.len); } pcap_close(pcap_handle); }
The pcap functions use a error buffer to return error and status messages, so we used this function to display this buffer:
if(!pcap_handle) { printf("Error in %s: %s\n","pcap_open_live",err_buf); exit(1); }
The err_buf is the error buffer, its size coming from a define in pcap.h set to 256. The header variable is a pcap_pkthdr structure containing extra capture information about the packet, the time it captured and its length. The pcap_handle pointer is similar to a file descriptor but it is used to reference a packet-capturing object:
struct pcap_pkthdr header; const u_char *packet; char err_buf[PCAP_ERRBUF_SIZE]; char *device; pcap_t *pcap_handle;
The pcap_lookupdev() looks for a suitable device to sniff on. This device is returned as a string pointer referencing static function memory. In our case, it is /dev/eth0. It will return NULL if it can't find a suitable interface:
device = pcap_lookupdev(err_buf); if(!device) { printf("Error in %s: %s\n","pcap_lookupdev",err_buf); exit(1); } printf("Sniffing on device %s\n", device);
The pcap_open_live() opens a packet-capturing device, and then returns a handle to it. The arguments are the device to sniff, the maximum packet size, a promiscuous flag, a timeout value, and a pointer to the error buffer. Because we want to capture in promiscuous mode, the flag is set to 1:
pcap_handle = pcap_open_live(device, 4096, 1, 0, err_buf); if(!pcap_handle) { printf("Error in %s: %s\n","pcap_open_live",err_buf); exit(1); }
The pcap_next() is used in the packet capture loop to get the next packet. Tis function is passed the pcap_handle and a pointer to a pcap_pkthdr structure so it can fill it with details of the capture. The function returns a pointer to the packet and then prints the packet. The pcal_close() closes the capture interface:
for(i = 0; i < 5; i++) { packet = pcap_next(pcap_handle, &header;); printf("%d byte packet\n", header.len); packet_dump(packet, header.len); } pcap_close(pcap_handle);
Output should look like this:
$ gcc -o pcap_sn pcap_sn.c -l pcap $ sudo ./pcap_sn Sniffing on device eth0 1134 byte packet 01 00 5e 04 50 01 00 22 19 55 11 27 08 00 45 00 | ..^.P..".U.'..E. 04 60 71 6b 00 00 10 11 8e 65 d3 2b 94 8b ef 04 | .`qk.....e.+.... 50 01 07 c4 2c a0 04 4c 50 bf 80 61 2d 22 a2 f3 | P...,..LP..a-".. e8 a4 00 00 2f 6d 00 00 01 b6 16 58 24 60 5f db | ..../m.....X$`_. 7f 1b 6d fc 6d b7 f1 b6 df c6 db 7f 1b 6d fc 6d | ..m.m........m.m b7 f1 b6 df c6 db 7f 1b 6d fc 6d b7 ed 83 79 90 | ........m.m...y. ..... 62 3e f2 b0 5e c6 07 fe 07 02 99 5e 60 71 75 4f | b>..^......^`quO aa c8 7d f4 40 c2 92 c5 86 b6 b7 5e d9 84 e1 0d | ..}.@......^.... 30 f9 b4 cd 17 dd c6 f7 3c 38 9e 53 71 4a | 0.......<8.SqJ 1134 byte packet 01 00 5e 04 50 01 00 22 19 55 11 27 08 00 45 00 | ..^.P..".U.'..E. 04 60 71 6c 00 00 10 11 8e 64 d3 2b 94 8b ef 04 | .`ql.....d.+.... 50 01 07 c4 2c a0 04 4c 6f 18 80 61 2d 23 a2 f3 | P...,..Lo..a-#.. e8 a4 00 00 2f 6d 25 a2 84 74 f9 51 05 26 e4 66 | ..../m%..t.Q.&.f 51 01 88 de e7 6f 07 0a 71 75 b7 bc 51 fe ac 4a | Q....o..qu..Q..J b0 a8 7a 0c df d2 b3 f6 29 71 6f 9a e5 9f aa e0 | ..z.....)qo..... ..... 0b a4 b5 a1 d1 77 f5 ba 59 ac 15 28 2d df 5e 4f | .....w..Y..(-.^O 97 b3 be a1 de 79 e2 1b 8a 84 85 33 bf b3 54 c0 | .....y.....3..T. ff f9 5a 03 0c 07 97 f9 73 19 8d 77 ed fb f7 24 | ..Z.....s..w...$ 80 cb ec cc fc 2d 0b 41 92 0c 90 0e a4 06 | .....-.A......
In this section, we will check the response to the http url hitting-
http://ad.doubleclick.net/ad/DY212/;order=sny0003;sz=1x1;ord=1322627560,
to see we are getting http 200 response.
We're going to do packet dump tcpdump (we may use wireshark) when our server fires the tracking pixel? I am using linux commend:
sudo tcpdump port 80 -XX -s 1024
and use another linux window to run:
wget http://ad.doubleclick.net/ads/813/;order=smsn0009;sz=1x1;o=1922687960
Then we get the output something like this:
23:50:12.616992 IP hx-in-f149.1e100.net.http > 201.13.198.196.54564: P 475:859(384) ack 268 win 1230x0000: 001d 0967 1169 2c6b f562 1105 0800 4500 ...g.i,k.b....E. 0x0010: 01b4 f2f9 0000 2d06 9f7a 4a7d 4795 d32b ......-..zJ}G..+ 0x0020: 9492 0050 d524 dca0 9756 c704 1fe5 8018 ...P.$...V...... 0x0030: 007b a053 0000 0101 080a 7d60 c38b a55e .{.S......}`...^ 0x0040: 1066 4854 5450 2f31 2e30 2032 3030 204f .fHTTP/1.0.200.O 0x0050: 4b0d 0a43 6f6e 7465 6e74 2d54 7970 653a .K..Content-Type: 0x0060: 2069 6d61 6765 2f67 6966 0d0a 4c61 7374 .image/gif..Last 0x0070: 2d4d 6f64 6966 6965 643a 2053 756e 2c20 -Modified:.Sun,. 0x0080: 3031 2046 6562 2032 3030 3920 3038 3a30 01.Feb.2009.08:0 0x0090: 303a 3030 2047 4d54 0d0a 4461 7465 3a20 0:00.GMT..Date:. 0x00a0: 5765 642c 2033 3020 4e6f 7620 3230 3131 Wed,.30.Nov.2011 0x00b0: 2032 313a 3433 3a31 3420 474d 540d 0a45 .21:43:14.GMT..E 0x00c0: 7870 6972 6573 3a20 5468 752c 2030 3120 xpires:.Thu,.01. 0x00d0: 4465 6320 3230 3131 2032 313a 3433 3a31 Dec.2011.21:43:1 0x00e0: 3420 474d 540d 0a58 2d43 6f6e 7465 6e74 4.GMT..X-Content 0x00f0: 2d54 7970 652d 4f70 7469 6f6e 733a 206e -Type-Options:.n 0x0100: 6f73 6e69 6666 0d0a 5365 7276 6572 3a20 osniff..Server:. 0x0110: 7366 6665 0d0a 436f 6e74 656e 742d 4c65 sffe..Content-Le 0x0120: 6e67 7468 3a20 3433 0d0a 582d 5853 532d ngth:.43..X-XSS-
The 200 OK means doubleClick is returning the response properly. In other words, the pixel is fired properly, then we can assure it's been registered a hit.
Also see php cURL
By default, TCP socket is set as blocking (sleeping) mode.
What does it mean blocking? Well, we're doing communication. We do something, and wait for the response. So, until we get the response, we cannot do anything. Just wait... and wait. What's the response? It could be either normal message or error.
For example, client did call receive() to remote server to read from a stream, control isn't returned to our program until at least one byte of data is read from the remote site. We can call this waiting process as blocking. If we use recv() in non-blocking mode by setting a descriptor as such, it will return any data that the system has in it's read buffer for that socket. But, it won't wait for that data.
Among the socket APIs we used, accept(), connect(), recv(), and recvfrom() are blocking functions.
When we first create the socket descriptor with socket(), the kernel sets it to blocking. If we do not want a socket to be blocking, we have to make a call to fcntl():
#include <unistd.h> #include <fcntl.h> sockfd = socket(PF_INET, SOCK_STREAM, 0); fcntl(sockfd, F_SETFL, O_NONBLOCK);By setting a socket to non-blocking, we can poll the socket for information. If we try to read from a 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 EWOULDBLOCK. The non-blocking mode is set by changing one of the socket's flags.
The flags are a series of bits, each one representing a different capability of the socket.
Actually, it's a three-step process:
- use F_GETFL to get the current flags
- set or clear the O_NONBLOCK flag
- then use F_SETFL to set the flags.
For more info, Blocking vs. non-blocking sockets.
However, in general, the polling is not a good idea because it makes our program in a busy-wait looking for data on the socket, and it will consume the precious CPU time.
We have another way of checking to see if there's data waiting to be read: select()!
Using non-blocking I/O means that we have to poll sockets to see if there is data to be read from them. Polling should usually be avoided since it uses more CPU time than other techniques.
Using SIGIO allows our application to do what it does and have the operating system tell it (with a signal) that there is data waiting for it on a socket. The only drawback to this solution is that it can be confusing, and if we're dealing with multiple sockets we will have to do a select() anyway to find out which one(s) is ready to be read.
#include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
The function monitors sets of file descriptors; in particular readfds, writefds, and exceptfds. If we want to see if we can read from standard input and some socket descriptor, sockfd, just add the file descriptors 0 and sockfd to the set readfds. The parameter numfds should be set to the values of the highest file descriptor plus one. In this example, it should be set to sockfd+1, since it is assuredly higher than standard input (0).
When select() returns, readfds will be modified to reflect which of the file descriptors you selected which is ready for reading.
Using select() is great if our application has to accept data from more than one socket at a time since it will block until any one of a number of sockets is ready with data. In other words, select() gives us the power to monitor several sockets at the same time. It'll tell us which ones are ready for reading, which are ready for writing, and which sockets have raised exceptions, if we really want to know that.
One other advantage to select() is that we can set a time-out value after which control will be returned to us whether any of the sockets have data for us or not.
The select(), though very portable, is one of the slowest methods for monitoring sockets. One possible alternative is libevent that encapsulates all the system-dependent stuff involved with getting socket notifications.
Continued to Socket - Server & Client 3
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization