Socket programming-socket basics in Linux

Source: Internet
Author: User


I. Socket Introduction


A socket interface is an API of a TCP/IP network. A socket interface defines many functions or routines that can be used by programmers to develop applications on a TCP/IP network. To learn TCP/IP network programming on the internet, you must understand the socket interface.
 
The socket interface designer first places the interface in the UNIX operating system. If you understand the input and output of Unix systems, you can easily understand socket. Network socket data transmission is a special type of I/O, and socket is also a file descriptor. Socket also has a function that calls socket () similar to opening a file.
Return to an integer socket descriptor. Subsequent connection establishment and data transmission operations are implemented through this socket. There are two common socket types: stream socket
(Sock_stream) and datagram socket (sock_dgram ). Stream is a connection-oriented socket for connection-oriented TCP Service applications; Data
The reporting socket is a connectionless socket that corresponds to a connectionless UDP Service Application.


Ii. Socket Creation


The socket function is prototype:

Include <sys/types. h>

Include <sys/socket. h>
Int socket (INT domain, int type, int Protocol );

Function: if the call succeeds, the socket file descriptor is returned; if the call fails,-1 is returned, and errno is set.

Parameter description:
 
Domain indicates the protocol family used, usually pf_inet, indicating the Internet protocol family (TCP/IP protocol ).
Family;

The type parameter specifies the socket type:

Sock_stream provides ordered, reliable, bidirectional, and connection-based byte streams

Sock_dgram supports Datagram

Sock_seqpacket
Provides ordered, reliable, bidirectional, and connection-based datagram Communication

Sock_raw provides access to the original network protocol

Sock_rdm provides a reliable datagram layer, but it does not guarantee orderliness.

Protocol is usually assigned "0 ".


The socket descriptor is a pointer to the internal data structure and points to the descriptor table entry. When the socket function is called, the socket execution body establishes a socket. In fact, "Creating a socket" means allocating storage space for a socket data structure. The socket execution body manages the descriptor table for you.
A network connection between two network programs includes five types of information: communication protocol, local Protocol address, local host port, remote host address, and remote protocol port. The socket data structure contains these five types of information.

Iii. Socket binding


 
The BIND function is prototype:

Include <sys/types. h>

Include <sys/socket. h>
Int BIND (INT sock_fd, struct sockaddr * my_addr, int addrlen );

Function Description: connect a socket to a specified port. 0 is returned. Otherwise,-1 is returned and errno is set.

Parameter description: sock_fd is the socket descriptor returned by the socket function,

My_addr is a sockaddr pointer that points to information such as the local IP address and port number;

Addrlen is often set to sizeof (struct sockaddr ).


The struct sockaddr structure type is used to save socket information:
Struct sockaddr {
Unsigned short sa_family;/* address family, af_xxx */

Char sa_data [14];/* 14-byte Protocol address */

};
Sa_family is generally af_inet, representing the Internet (TCP/IP) address family;

Sa_data contains the IP address and port number of the socket.


There is also a structure type:
Struct sockaddr_in {
Short int sin_family;/* address family */
Unsigned short int sin_port;/* Port Number */
Struct in_addr sin_addr;/* IP Address */
Unsigned char sin_zero [8];/* fill in 0 to keep the same size as struct sockaddr */
};
 
This structure is more convenient to use. Sin_zero is used to fill the sockaddr_in structure with struct
The same length of sockaddr can be set to zero using the bzero () or memset () function. Pointing to sockaddr_in
The pointer to sockaddr and the pointer to sockaddr can be converted to each other, which means that if the required parameter type of a function is sockaddr, you can
The addr_in pointer is converted to the sockaddr pointer; or vice versa.
When using the BIND function, you can use the following value assignment to automatically obtain the local IP address and randomly obtain an unused port number:
My_addr.sin_port = 0;/* The system randomly selects an unused Port Number */
My_addr.sin_addr.s_addr = inaddr_any;/* enter the local IP Address */
By setting my_addr.sin_port to 0, the function automatically selects an unused port for you to use. Similarly, by setting my_addr.sin_addr.s_addr to inaddr_any, the system automatically fills in the local IP address.
Note that when using the BIND function, you need to convert sin_port and sin_addr to the network byte priority, while sin_addr does not need to be converted.

There are two types of computer data storage priorities: high byte priority and low byte priority (large and small ). Data on the internet is transmitted over the network in the high byte priority. Therefore, for machines that store data in the low byte priority mode, data transmission over the Internet requires conversion, otherwise, data inconsistency may occur.
Below are several bytes sequence conversion functions:
· Htonl (): converts 32-bit values from the host's byte order to the network's byte order
· Htons (): converts 16-bit values from the host's byte order to the network's byte order
· Ntohl (): converts 32-bit values from the network byte order to the host byte order.
· Ntohs (): converts 16-bit values from the network byte order to the host byte order.
The BIND () function returns 0 when the call is successful. If an error occurs, it returns "-1" and sets errno as the corresponding error number.

Note that do not set the port number to a value smaller than 1024 when calling the BIND function, because the port numbers from 1 to 1024 are reserved, you can select any unused port number in the range greater than 1024.



Iv. Connection


Connection-oriented client programs use the connect function to configure the socket and establish a TCP connection with the remote server. The function prototype is:

Include <sys/types. h>

Include <sys/socket. h>
Int connect (INT sock_fd, struct sockaddr * serv_addr, int addrlen );

Function Description: the client sends a service request. 0 is returned. Otherwise,-1 is returned and errno is set.
Parameter description: sock_fd
Is the socket descriptor returned by the socket function; serv_addr is a pointer containing the IP address and port number of the remote host; addrlen is the length of the remote geological structure.

You do not need to call BIND () for client program design, because in this case, you only need to know the target machine
Which port does the customer need to establish a connection with the server,
Socket execution is your program
Automatically select an unused port and
Notifies you when your program data is sent to the port.
Connect function starts a direct connection with the remote host. The socket must be connected to the remote host only when the connection-oriented client program uses the socket. No connection protocol never establishes direct connection. The connection-oriented server never starts a connection, but passively listens to customer requests on the protocol port.


5. Listener

The listen function enables the socket to be in passive listening mode and creates an input data queue for the socket. The incoming service requests are stored in the queue until the program processes them.

Include <sys/socket. h>
Int listen (INT sock_fd, int backlog );

Function Description: Wait for the client to connect to the specified port. If the call is successful, 0 is returned. Otherwise,-1 is returned and errno is set.
Parameter description: sock_fd
Is the socket returned by the Socket System Call.
Descriptor;

Backlog specifies the maximum number of requests allowed in the Request queue. incoming connection requests will wait for accept () in the queue (refer to the following ).

Backlog limits the number of requests waiting for service in the queue. The default value is 20 for most systems. If a service request arrives, the input queue is full, and the socket rejects the connection request, the customer will receive an error message.

Vi. Accept

The accept () function allows the server to receive client connection requests. After the input queue is established, the server calls the accept function and then sleeps and waits for the client's connection request.

Include <sys/types. h>

Include <sys/socket. h>


Int accept (INT sock_fd, void * ADDR, int * addrlen );

Function Description: This interface is used to accept client service requests. A new socket descriptor is returned successfully. If a socket descriptor fails,-1 is returned and errno is set.
 
Parameter description: sock_fd is the socket descriptor to be monitored. ADDR is usually a pointer to the sockaddr_in variable, this variable is used to store the information of the host requesting the service (a host sends this request from a port); addrten is usually a point value of sizeof (struct
Sockaddr_in. When an error occurs, the accept function returns-1 and sets the corresponding errno value.
First, when the accept function monitors
When the socket receives the connection request, the socket execution body establishes a new socket, and the execution body associates the new socket with the address of the Request connection process, and receives
The initial socket can still be listened on the previous socket, and data transmission can be performed on the new socket descriptor.

VII. Data Transmission


The send () and Recv () functions are used for data transmission on the connected socket.
The send () function is prototype:

Include <sys/types. h>

Include <sys/socket. h>
Size_t send (INT sock_fd, const void * MSG, int Len, int flags );

Function Description: send data. The number of bytes of the actually unsent data. -1 is returned for failure, and errno is set.
Parameter description: sock_fd is the socket descriptor you want to use to transmit data; MSG is a pointer to the data to be sent; Len is the length of the data in bytes; flags is usually set to 0 (for the usage of this parameter, refer to the man Manual ).
The send () function returns the number of actually sent bytes, which may be less than the data you want to send. In the program, the return value of send () should be compared with the number of bytes to be sent. When the return value of send () does not match Len, this situation should be processed.
Char * MSG = "Hello! ";
Int Len, bytes_sent;
......
Len = strlen (MSG );
Bytes_sent = Send (sockfd, MSG, Len, 0 );
......
The Recv () function is prototype:

Include <sys/types. h>

Include <sys/socket. h>
Int Recv (INT sock_fd, void * Buf, int Len, unsigned int flags );

Function Description: if data is accepted, 0 is returned. Otherwise,-1 is returned and errno is set.
Parameter description: sock_fd is the socket descriptor for receiving data, Buf is the buffer for storing received data, and Len is the buffer length. Flags is also set to 0. Recv () returns the number of actually received bytes.



Sendto () and recvfrom () are used for data transmission in the connectionless datagram socket mode. Because the local socket does not establish a connection with the remote machine, the destination address should be specified when sending data.
The prototype of the sendto () function is:
Int sendto (INT sockfd, const void * MSG, int Len, unsigned int flags, const struct sockaddr * To, int tolen );

This function has two more parameters than the send () function. To indicates the IP address and port number of the host, while tolen is often assigned sizeof (struct sockaddr ). The sendto function also returns the actual length of data bytes or-1 in case of a sending error.
The original recvfrom () function is:
Int recvfrom (INT sockfd, void * Buf, int Len, unsigned int flags, struct sockaddr * From, int * fromlen );

 
From is a variable of the struct sockaddr type, which saves the IP address and port number of the source machine. Fromlen is often set to sizeof (struct
Sockaddr ). When recvfrom () is returned, fromlen contains the number of data bytes actually stored in from. The recvfrom () function returns the number of bytes received or
If an error occurs,-1 is returned and the corresponding errno is set.
If you call the connect () function on the datagram socket, you can also use send () and Recv () for data transmission, but the socket is still a datagram socket, the UDP Service at the transport layer is used. However, when sending or receiving data reports, the kernel automatically adds the object and source address information.


VIII. End Transmission


After all data operations are completed, you can call the close () function to release the socket and stop any data operations on the socket:

Int close (sock_fd );

You can also call the shutdown () function to close the socket. This function allows you to stop data transmission in a certain direction, while data transmission in one direction continues. For example, you can close the write operation of a socket and allow the socket to continue to accept data until all data is read.
Int Shutdown (INT sock_fd, int how );

Sockfd is the descriptor of the socket to be closed. The how parameter allows you to select the following methods for the shutdown operation:
· 0 ------- you are not allowed to continue receiving data.
· 1 ------- data cannot be sent again
· 2 ------- you are not allowed to send or receive data,
· If both allow, close () is called ()
If the shutdown operation succeeds, 0 is returned. If an error occurs,-1 is returned and the corresponding errno is set.

Socket programming instance


The server in the Code instance sends the string "Hello, you are connected!" to the client through a socket connection! ". If you run the software on the server and the client runs the software on the client, the client receives the string.
The software code of the server is as follows:
# Include <stdio. h>
# Include <stdlib. h>
# Include <errno. h>
# Include <string. h>
# Include <sys/types. h>
# Include <netinet/in. h>
# Include <sys/socket. h>
# Include <sys/Wait. H>
# Define servport 3333/* server listening port number */
# Define backlog 10/* Maximum number of simultaneous connection requests */
Int main ()
{
Int sock_fd, client_fd;/* sock_fd: Listener socket; client_fd: Data Transmission socket */
Struct sockaddr_in my_addr;/* local address information */
Struct sockaddr_in remote_addr;/* client address information */
If (sock_fd = socket (af_inet, sock_stream, 0) =-1 ){
Perror ("socket creation error! ");

Exit (1 );
}
My_addr.sin_family = af_inet;
My_addr.sin_port = htons (servport );
My_addr.sin_addr.s_addr = inaddr_any;
Bzero (& (my_addr.sin_zero), 8 );
If (BIND (sock_fd, (struct sockaddr *) & my_addr, sizeof (struct sockaddr) =-1 ){
Perror ("BIND error! ");
Exit (1 );
}
If (Listen (sock_fd, backlog) =-1 ){
Perror ("Listen error! ");
Exit (1 );
}
While (1 ){
Sin_size = sizeof (struct sockaddr_in );
If (client_fd = accept (sock_fd, (struct sockaddr *) & remote_addr, & sin_size) =-1 ){
Perror ("Accept error ");
Continue;
}
Printf ("received a connection from % s/n", inet_ntoa (remote_addr.sin_addr ));
If (! Fork () {/* child process code segment */
If (send (client_fd, "Hello, you are connected! /N ", 26, 0) =-1)
Perror ("send error! ");
Close (client_fd );
Exit (0 );
}
Close (client_fd );
}
}

Return 0;
}
 
The server workflow is as follows:

1. Call the socket function to create a socket;

2. Call the BIND function to bind it to the local address and a local port number;

3. Then call
Listen listens on the corresponding socket. When accpet receives a connection service request, a new socket is generated. The server displays the IP address of the client and
The new socket sends the string "Hello, you are connected! ". Close the socket.
 

The client code is as follows:
# Include <stdio. h>
# Include <stdlib. h>
# Include <errno. h>
# Include <string. h>
# Include <netdb. h>
# Include <sys/types. h>
# Include <netinet/in. h>
# Include <sys/socket. h>
# Define servport 3333
# Define maxdatasize 100/* maximum data transmission volume each time */
Int main (INT argc, char * argv [])

{
Int sock_fd, recvbytes;
Char Buf [maxdatasize];
Struct hostent * Host;
Struct sockaddr_in serv_addr;
If (argc <2 ){
Fprintf (stderr, "Please enter the server's hostname! /N ");

Exit (1 );

}


If (host = gethostbyname (argv [1]) = NULL ){

Herror ("gethostbyname error! ");

Exit (1 );

}
If (sock_fd = socket (af_inet, sock_stream, 0) =-1 ){

Perror ("socket creation error! ");
Exit (1 );

}
Serv_addr.sin_family = af_inet;
Serv_addr.sin_port = htons (servport );
Serv_addr.sin_addr = * (struct in_addr *) Host-> h_addr );
Bzero (& (serv_addr.sin_zero), 8 );
If (connect (sock_fd, (struct sockaddr *) & serv_addr ,/
Sizeof (struct sockaddr) =-1 ){

Perror ("Connect error! ");

Exit (1 );

}
If (recvbytes = Recv (sock_fd, Buf, maxdatasize, 0) =-1 ){

Perror ("Recv error! ");

Exit (1 );

}
Buf [recvbytes] = '/0 ';
Printf ("Received: % s", Buf );
Close (sock_fd );
}
The client first obtains the Server IP address through the server domain name, creates a socket, calls the connect function to establish a connection with the server, receives data sent from the server after the connection is successful, and closes the socket.


Struct hostent * gethostbyname (const char * Name );


Function: converts a domain name to an IP address;

Parameter description: name indicates the domain name to be converted.

Returned value: the hosten structure type pointer when the call is successful. If the call fails,-1 is returned. The error cause can be returned by the herror () function.


The hosten structure is as follows:
Struct hostent {
Char * h_name;/* Host's official domain name */
Char ** h_aliases;/* an array of host aliases ending with null */
Int h_addrtype;/* return address type, in the Internet environment is AF-INET */
Int h_length;/* the length of the address in bytes */
Char ** h_addr_list;/* an array ending with 0, containing all addresses of the Host */
};
# Define h_addr h_addr_list [0]/* The first address in H-ADDR-list */
  

The principle of a connectionless client/server program is the same as that of a connected Client/Server. The difference between the two is that customers in a connectionless Client/Server generally do not need to establish a connection, when sending and receiving data, you must specify the address of the remote machine.


Blocking and non-blocking

 
The blocking function does not allow the program to call another function until it completes the specified task. For example, when a program executes a function call to read data, the next program statement is not executed until the function completes the read operation. When the server runs the accept statement without a customer connection request, the server stops waiting for the connection request on the accept statement. This is called blocking.
(Blocking ). The non-blocking operation can be completed immediately. For example, if you want the server to check whether a client is waiting for a connection, the server will accept the connection; otherwise, the server will continue to do other things.
You can set the socket to a non-blocking method. The non-blocking Socket enables the Accept call to return immediately when no client is waiting.
# Include <unistd. h>
# Include <fcntl. h>
......

Sock_fd = socket (af_inet, sock_stream, 0 );

Fcntl (sock_fd, f_setfl, o_nonblock );

......
 
By setting the socket as a non-blocking method, you can implement "polling" Several sockets. When an attempt is made to read data from a non-blocking socket without data waiting for processing, the function immediately returns-1 and sets errno to ewouldblock. however, this "Round Robin" will make the CPU in a busy waiting mode, thus reducing performance and wasting system resources. And call
Select () effectively solves this problem. It allows you to hook up the process itself and enable the system kernel to listen to any activity of a set of file descriptors required by the system kernel, just confirm in any monitored File
If there is an activity on the descriptor, the Select () call will return information indicating that the file descriptor has been prepared, so as to select random changes for the process, it is not a waste of testing input by the process itself.
CPU overhead. The prototype of the select function is:

Int select (INT numfds, fd_set * readfds, fd_set * writefds,


Fd_set * required TFDs, struct timeval * timeout );

 
Readfds, writefds, and limit TFDs are the collection of read, write, and exception handling file descriptors monitored by select. If you want to determine whether data can be read from the standard input and a socket descriptor, you only need to add the file descriptor 0 and the corresponding socket_fd of the standard input to the readfds set; the numfds value is the file descriptor with the highest number to be checked plus 1. In this example, the numfds value should be sockfd + 1. When the SELECT statement is returned, readfds will be modified to indicate a file.
The descriptor is ready to be read. You can test it through fd_issset. To set, reset, and test the file descriptor corresponding to fd_set, it provides a set of macros:
Fd_zero (fd_set * Set) ---- clears a file descriptor set;
Fd_set (int fd, fd_set * Set) ---- Add a file descriptor to the file descriptor set;
Fd_clr (int fd, fd_set * Set) ---- clears a file descriptor from the file descriptor set;
Fd_isset (int fd, fd_set * Set) ---- try to determine whether the file descriptor is set.
The timeout parameter is a pointer to the struct timeval type, which enables select () to return if no file descriptor is ready after waiting for a long time. Struct timeval data structure:
Struct timeval {
Int TV _sec;/* seconds */
Int TV _usec;/* microseconds */

};


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.