Socket Programming
The word socket can represent a number of concepts, in the TCP/IP protocol "IP address + TCP or UDP port number" uniquely identify a process in network communications, "IP + port number" is called the socket. In the TCP protocol, the two processes that make up the connection each have a socket to identify, then the socket pair of two sockets uniquely identifies a connection.
Preliminary knowledge
Network byte order: in-memory multi-byte data relative to the memory address has a big end, the multibyte data in the disk file relative to the file offset address also has a small end of the point. The network data stream also has the big end small point the cent, so the sending host usually sends the data in the sending buffer to the order of memory address from low to high, the receiving host saves the bytes received from the network in the order of memory from low to high, so the address of the network data stream should stipulate: The data sent first is low address, The data sent after is a high address. The TCP/IP protocol stipulates that the network data stream should be in the big endian byte order, namely low address high byte. So the sending host and the receiving host are small byte-ordered transformations that need to be done before sending and receiving.
In order for the network program to have portability, you can call the following function to convert the number of network bytes.
#include <arpa/inet.h>
uint32_t htonl (uint32_t hostlong);
uint16_t htons (uint16_t hostshort);
uint32_t Ntohl (uint32_t netlong);
uint16_t Ntohs (uint16_t netshort);
Socket address data types and related functions
SOCKADDR Data structure
The address types of IPV6 and Unixdomain sockets are defined as constant af_inet, Af_inet6, and Af_unix respectively. In this way, you can determine the contents of a structure based on the address Type field, as long as you obtain the first address of a SOCKADDR structure, and you do not need to know which type of SOCKADDR structure is specific. Therefore, the socket API can accept various types of sockaddr struct pointers, such as bind, accept, connect, and so on, that the parameters of these functions should be designed to be void * types to accept various types of pointers, but sock The implementation of the API was earlier than ANSI C standardization, when there was no null pointer type the parameters of these functions are ⽤ with the struct sockaddr type, and the type conversion is mandatory before passing the arguments.
This time only introduces IPV4 socket network programming, SOCKADDR_IN member struct in_addr sin_addr table ⽰ 32-bit IP address. But we usually ⽤ the IP address in dotted decimal notation, the following function can be converted between the string table ⽰ and the in_addr table ⽰. That is, you can convert a string to a in_addr type, or you can convert a local byte into a network byte. Instead, the exact usage of the function from the network to the local is also shown in the code below.
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int Inet_aton (const char *CP, struct in_addr *inp);
in_addr_t inet_addr (const char *CP);
in_addr_t inet_network (const char *CP);
Char *inet_ntoa (struct in_addr in);
struct IN_ADDR inet_makeaddr (int net, int host);
in_addr_t inet_lnaof (struct in_addr in)
Tcpsocket implementation
Implementation model:
1. Server-side socket-> Bind-> Listen-> accept (blocked, three-time handshake)-> send.
2. Client socket-> Connect (blocking, three times handshake)-> Rcv.
Function Description:
int socket (int family, int type, int protocol)
Family: Specifies the type of protocol for this selection af_inet (IPV4 protocol).
Type: Network data types, TCP is a-sock_stream for byte-throttling.
Protocol: The first two parameters generally determine that the protocol type usually passes 0.
Return value: The socket character was successfully returned.
Failure returns-1 to set the associated error code.
int bind (int sockfd, const struct SOCKADDR *servaddr, socklen_t Addrlen)
The socket descriptor that is returned when the Sockfd:socket function succeeds.
SERVADDR: The IP and port of the server.
Addrlen: Length (sizeof (SERVADDR)).
Return value: Successfully returned 0
Failure returns-1 and sets the associated error code.
int listen (int sockfd, int backlog)
The socket descriptor that is returned when the Sockfd:socket function succeeds.
Backlog: The maximum number of sockets queued in the kernel.
Return value: Successfully returned 0
Failure returns-1 and sets the associated error code.
int accept (int sockfd, const struct SOCKADDR *servaddr, socklen_t *addrlen)
The socket descriptor that is returned when the Sockfd:socket function succeeds.
SERVADDR: Output parameters, client IP and port.
Addrlen: Length (sizeof (SERVADDR)).
Return value: Success: Returning a connected socket from a listening socket
Failed: Failed to return-1 and set the associated error code.
int connect (int sockfd, const struct SOCKADDR *servaddr, socklen_t Addrlen)
SOCKFD: Socket descriptor returned by function
SERVADDR: IP and port of the server
Addrlen: Length (sizeof (SERVADDR)).
Return value: Successfully returned 0
Failed to return-1 and set the relevant error code implementation code
Server.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> static Usage (const char* proc) {printf ("usage:%s[local-ip][local-port]\n", proc);} static int start_up (const char *local_ip,in
T local_port) {//1.create sock int sock = socket (af_inet,sock_stream,0);
if (Sock < 0) {perror ("socket");
Close (sock);
Exit (1);
}//2,createbind struct sockaddr_in local;
Bzero (&local,sizeof (local));
local.sin_family = af_inet;
Local.sin_port = htons (Local_port);
LOCAL.SIN_ADDR.S_ADDR = inet_addr (LOCAL_IP);
if (bind (sock, struct sockaddr *) &local,sizeof (local) < 0) {perror ("bind");
Close (sock);
Exit (2);
}//3.listen if (Listen (sock,10) < 0) {perror ("listen");
Close (sock);
Exit (3); } RetuRN sock;
int main (int argc, char *argv[]) {if (argc!= 3) {usage (argv[0));
int sock = START_UP (Argv[1],atoi (argv[2));
struct SOCKADDR_IN client;
socklen_t len = sizeof (client);
while (1) {int new_sock = accept (sock, (struct sockaddr*) &client, &len);
if (New_sock < 0) {perror ("accept");
Close (sock);
return 1;
printf ("client_ip:%s client_port:%d\n", Inet_ntoa (CLIENT.SIN_ADDR), Ntohs (Client.sin_port));
Char buf[1024];
while (1) {ssize_t s = Read (New_sock,buf,sizeof (BUF)-1);
if (S > 0) {buf[s] = 0;
printf ("Client say#%s", buf);
else if (s = = 0) {printf ("Client quit!\n");
Break
Write (New_sock,buf, strlen (BUF));
} close (sock);
return 0; }
client.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> static
void usage (const char* proc) {printf ("usage:%s [Server-ip] [Server-port]", proc);} int main (int argc,char *argv[]) {
if (argc!= 3) {usage (argv[0]);
int sock = socket (af_inet,sock_stream,0);
if (Sock < 0) {perror ("socket");
return 1;
} struct SOCKADDR_IN server;
server.sin_family = af_inet;
Server.sin_port = htons (atoi (argv[2));
SERVER.SIN_ADDR.S_ADDR = inet_addr (argv[1]);
if (Connect (sock, (struct sockaddr *) &server,sizeof (server)) < 0) {perror ("connect");
return 2;
while (1) {printf ("entry#");
Fflush (stdout);
Char buf[1024];
ssize_t s = Read (0,buf,sizeof (BUF)-1);
if (S > 0)//read Success {Buf[s] = 0;
Write (Sock,buf,strlen (BUF));
ssize_t _s = Read (sock,buf,sizeof (BUF)-1);
if (_s > 0) {buf[_s-1] = 0;
printf ("Server echo#%s\n", buf);
} close (sock);
return 0; }
The above code can only handle a single user, in order to be able to handle multiple user requests we can write multiple processes or multi-threaded TCP sockets. The complete code is as follows.
Multi-Process Tcpsocket
https://coding.net/u/Hyacinth_Dy/p/MyCode/git/blob/master/%E5%A4%9A%E8%BF%9B%E7%A8%8BTCP%E5%A5%97%E6%8E%A5%E5%AD%97
Multithreading Tcpsocket
https://coding.net/u/Hyacinth_Dy/p/MyCode/git/blob/master/%E5%A4%9A%E7%BA%BF%E7%A8%8BTcp%E5%A5%97%E6%8E%A5%E5%AD%97
For the code above, the process or thread must wait for an event to occur, if the event does not occur, the process or thread is blocked, and the function is not immediately a reasonable choice for performance, so we need to improve the performance of the code. Here are three common high-performance socket programming methods. The Select function of I/O multiplexing
Select function Preparation knowledge struct Fd_set can be understood as a collection that holds a file descriptor (a file descriptor), which is a document handle, which can be the common meaning of what we call a file, of course, any device, pipeline, FIFO, etc. are file forms, all included, so there is no doubt that a socket is a file, the socket handle is a file descriptor. The Fd_set collection can be manipulated by some macros.
(1) fd_clr (INR fd,fd_set* Set): Used to clear bits that describe the associated FD in the phrase set
(2) fd_isset (int fd,fd_set *set): Used to test whether the bit of the associated FD in the phrase set is true
Fd_set (int fd,fd_set*set): Used to set the bit that describes the associated FD in the phrase set
2.struct Timeval is a commonly used structure that represents a time value with two members, one in seconds and the other in milliseconds.
Fd_zero (Fd_set *set); used to clear all bits of the description phrase set
Introduction to select function
int select (int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
MAXFDP: The maximum file description to monitor alphanumeric 1.
Readfds, Writefds, Errorfds: A collection of readable file descriptors to be detected, a collection of writable file descriptors, and a collection of exception file descriptors.
Timeout: Wait time, this time, the descriptor that needs to be monitored has no event
⽣ The function returns, the return value is 0. Set to NULL to indicate a blocking wait, until the event is ready, the function will return, 0 for non-blocking Wait, no event immediately returned, greater than 0 indicates the time to wait.
Return value: Greater than 0 indicates the number of ready time, equal to 0 indicates that the timeout wait time is up, and less than 0 indicates that the call failed.
The principle of Select function
Select system calls are used to let our programs monitor the state changes of multiple file handles. The program stops waiting in the select ⾥ until one or more of the file handles being monitored has a ⽣ state change. About the file handle, is actually ⼀ an integer, our most familiar handle is 0, 1, 23, 0 is standard input, 1 is standard output, 2 is standard error output. 0, 1, 2 is an integer representation, the corresponding file * structure is the representation of stdin, stdout, stderr.
1. We usually need to define an additional array to hold the file descriptor that needs to be monitored, and to initialize the other location without the save descriptor to a specific value, typically-1, so that we can traverse the array to determine if the corresponding file descriptor has occurred.
2. The above macro operation fd_set (int fd,fd_set*set) traverses the array to set the concerned file descriptor to the corresponding event set. And you need to iterate through the array before each call to set the file descriptor.
3. Call the Select function to wait for the file descriptor that you care about. The second step is the reason why the SELECT function returns when the event is ready on the file descriptor and the file descriptor with no event readiness is set to 0 in the file descriptor collection.
4.select returns the number of file descriptors that are ready for a value greater than 0, 0 indicates that the wait time is up, less than 0 means the call failed, so we can traverse the array using fd_isset (int fd,fd_set *set) to determine which file descriptor is ready for the event, Then do the appropriate action.
Implement code using a select TCP socket.
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <sys
/time.h> static void Usage (const char* proc) {printf ("%s [local_ip] [local_port]\n", proc);} int array[4096];
static int start_up (const char* _ip,int _port) {int sock = socket (af_inet,sock_stream,0);
if (Sock < 0) {perror ("socket");
Exit (1);
} struct sockaddr_in local;
local.sin_family = af_inet;
Local.sin_port = htons (_port);
LOCAL.SIN_ADDR.S_ADDR = inet_addr (_IP);
if (Bind (sock, (struct sockaddr*) &local,sizeof (local) < 0) {perror ("bind");
Exit (2);
} if (Listen (sock,10) < 0) {perror ("listen");
Exit (3);
Return sock;
int main (int argc,char* argv[]) {if (argc!= 3) {Usage (argv[0));
return-1; } int LIstensock = Start_up (Argv[1],atoi (argv[2));
int maxfd = 0;
Fd_set RfDs;
Fd_set Wfds;
Array[0] = Listensock;
int i = 1;
int array_size = sizeof (array)/sizeof (array[0]);
for (; i < array_size;i++) {Array[i] =-1;
while (1) {Fd_zero (&RFDS);
Fd_zero (&wfds); for (i = 0;i < Array_size;++i) {if (array[i) > 0) {fd_set (array[i],&
AMP;RFDS);
Fd_set (Array[i],&wfds);
if (Array[i] > maxfd) {maxfd = Array[i];
Switch (SELECT (Maxfd + 1,&rfds,&wfds,null,null)) {case 0:
{printf ("timeout\n");
Break
} case-1: {perror ("select");
Break } Default:
{int j = 0; for (; J < array_size; ++j) {if (j = = 0 && fd_isset array[j],&rf DS)) {//listensock happened read events s
Truct sockaddr_in Client;
socklen_t len = sizeof (client);
int new_sock = Accept (Listensock, (struct sockaddr*) &client,&len);
if (New_sock < 0)//accept failed {perror ("accept");
Continue } else//accept Success {printf ("G
Et a new client%s\n ", Inet_ntoa (CLIENT.SIN_ADDR));
Fflush (stdout);
int k = 1; for (; k < array_size;++k) {if (Array[k] & Lt
0) {Array[k] = New_sock;
if (New_sock > maxfd) maxfd = New_sock;
Break
} if (k = = array_size) {
Close (New_sock); }}//j = = 0 Else if (J!= 0 && Fd_iss
ET (Array[j], &rfds)) {//new_sock happend read events
Char buf[1024];
ssize_t s = Read (Array[j],buf,sizeof (BUF)-1); if (S > 0)//read success {Buf[s] =
0;
printf ("clientsay#%s\n", buf); if (Fd_isset (Array[j],&wfds)) {char *msg = ' HTTP/1
.0 OK <\r\n\r\n
The client end is the same as the upper Tcpsocket end. The pros and cons of select
Advantages:
(1) Select has good portability and does not support poll under some UNIX.
(2) Select provides a good precision for the timeout value, accurate to microseconds, and poll milliseconds.
Disadvantages:
(1) The number of FD that can be monitored by a single process is limited, and the default is 1024.
(2) It is necessary to maintain a data structure for storing a large amount of FD, which will make the user space and kernel space more expensive to replicate when passing the structure.
(3) to the FD scan is linear scan, FD surge, io efficiency, each call to the FD linear scan traversal, as the increase of FD will cause the problem of slow traversal.
(4) The Select function Timeout parameter is not defined when it is returned, and the timeout parameter is reset before entering the next select after each timeout, given the portability. Poll function Poll function preparation knowledge for I/O multiplexing
Unlike the Select function poll, a POLLFD pointer is passed to the kernel to convey the descriptor that needs attention and its associated events.
FD: A file descriptor that needs attention
Events: Matters of concern, legal events as follows
Pollin has data to read.
Pollrdnorm has normal data to read.
Pollrdband has priority data to read.
Pollpri has urgent data to read.
pollout Write data does not cause blocking.
Pollwrnorm Writing normal data does not cause blocking.
Pollwrband Write priority data does not cause blocking.
pollmsgsigpoll messages are available.
Revents: When the concerned event is ready, the revents is set to the corresponding event, which may be set to the following.
Poller The specified file descriptor has an error.
pollhup The specified file descriptor pending event.
pollnval The specified file descriptor is illegal.
Introduction to poll functions
#include <poll.h>
int poll (struct POLLFD * FDS, unsigned int nfds, int timeout);
Parameter introduction:
FDS: The structural body pointer corresponding to the above description
Nfds: Marks the total number of structural elements in an array.
Timeout: timeout, equal to 0 for non-blocking wait, less than 0 for blocking wait, greater than 0 for time to wait.
return value:
Returns the number of file descriptors for event-ready in FDS array when successful
A return of 0 indicates that the timeout time is up.
A return of 1 indicates that the call failed and the corresponding error code is set.
EBADF The file descriptor specified in one or more of the struct bodies is invalid.
the Efaultfds pointer points to an address that is outside the address space of the process.
Eintr The requested event generates a signal before the call can be initiated again. The
Einvalnfds parameter exceeds the Plimit_nofile value.
Enomem There is not enough available memory to complete the request.
Realization principle of poll function
(1) Put the file descriptor that needs care into the FDS array
(2) Calling the poll function
(3) After the function is successfully returned, the FDS array is traversed according to the return value, and the event and the revents in the structure are judged to be ready.
(4) The event is ready to perform the related operation.
Poll implement Tcpsocket code
#include <stdio.h> #include <stdlib.h> #include <string.h> #include < sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include < poll.h> static void usage (const char *proc) {printf ("%s [local_ip] [local_port]\n", proc);} int start_up (const CHA
R*_ip,int _port) {int sock = socket (af_inet,sock_stream,0);
if (Sock < 0) {perror ("socket");
return 2;
int opt = 1;
SetSockOpt (sock,sol_socket,so_reuseaddr,&opt,sizeof (opt));
struct SOCKADDR_IN local;
local.sin_family = af_inet;
Local.sin_port = htons (_port);
LOCAL.SIN_ADDR.S_ADDR = inet_addr (_IP);
if (Bind (sock, (struct sockaddr*) &local,sizeof (local) < 0) {perror ("bind");
return 3;
} if (Listen (sock,10) < 0) {perror ("listen");
return 4;
Return sock; int main (int argc, char*argv[]) {