Socket programming under Linux

Source: Internet
Author: User
Tags function prototype socket error htons

Http://blog.chinaunix.net/uid-20733992-id-3450058.html

The original address: Linux socket programming yulianliu1218

Socket programming under Linux

What is a socket
The socket interface is a TCP/IP network Api,socket interface that defines many functions or routines that programmers can use to develop applications on TCP/IP networks. To learn TCP/IP network programming on the Internet, you must understand the socket interface.
The socket interface Designer is the first to put the interface in the UNIX operating system. If you understand the input and output of UNIX systems, it is easy to understand the socket. The network socket data transmission is a special i/o,socket is also a kind of file descriptor. The socket also has a function call socket () that resembles an open file, which returns an integer socket descriptor, followed by connection creation, data transfer, and so on, through the socket. There are two common types of sockets: Streaming Sockets (SOCK_STREAM) and datagram Sockets (SOCK_DGRAM). Streaming is a connection-oriented socket for connection-oriented TCP service applications; A datagram socket is a non-connected socket that corresponds to a non-connected UDP service application.

Socket creation
To create a socket, the program can call the socket function, which returns a handle similar to a file descriptor. The socket function prototype is:
int socket (int domain, int type, int protocol);
Domain indicates the protocol family used, typically pf_inet, that represents the Internet Protocol family (TCP/IP protocol family), and the type parameter specifies the types of sockets: Sock_stream or SOCK_DGRAM, The socket interface also defines the original socket (SOCK_RAW), which allows the program to use low-level protocols, and protocol typically assigns a value of "0". The socket () call returns an integer socket descriptor that you can use later in the call.
The socket descriptor is a pointer to an internal data structure that points to the entry of the descriptor descriptor. When the socket function is called, the socket executor will create a socket that actually "creates 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 consists of five kinds of information: communication Protocol, local protocol address, local host port, remote host address, and remote protocol port. These five kinds of information are included in the socket data structure.

Socket Configuration
After you return a socket descriptor through the socket call, you must configure the socket before using the socket for network transport. The connection-oriented socket client stores local and remote information in the socket data structure by calling the Connect function. The client and server side of the disconnected socket and the server facing the socket are configured to configure local information by calling the BIND function.
The BIND function associates a socket with a port on the local computer, and then you can listen for service requests on that port. The BIND function prototype is:
int bind (int sockfd,struct sockaddr *my_addr, int addrlen);
SOCKFD is the socket descriptor returned by the call to the socket function, MY_ADDR is a pointer to a SOCKADDR type that contains information such as the native IP address and port number, and Addrlen is often set to sizeof (struct sockaddr).
The struct sockaddr struct type is used to hold the socket information:
struct SOCKADDR {
unsigned short sa_family; /* Address family, AF_XXX */
Char sa_data[14]; /* 14-byte protocol address */
};
Sa_family is typically af_inet, which represents the Internet (TCP/IP) address family, and Sa_data contains the IP address and port number of the socket.
There is another type of structure:
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 0 to keep the same size as the struct sockaddr */
};
This structure is more convenient to use. The Sin_zero is used to populate the SOCKADDR_IN structure with the same length as the struct sockaddr, which can be set to zero with the bzero () or the memset () function. Pointers to sockaddr_in and pointers to SOCKADDR can be converted to each other, which means that if a function requires a parameter type of SOCKADDR, you can convert a pointer to sockaddr_in to a pointer to sockaddr when the function is called , or vice versa.
When using the BIND function, the following assignment can be used to automatically obtain a native IP address and randomly get a port number that is not occupied:
My_addr.sin_port = 0; /* The system randomly selects an unused port number */
MY_ADDR.SIN_ADDR.S_ADDR = Inaddr_any; /* Fill in the native IP address */
By placing My_addr.sin_port at 0, the function automatically selects an unoccupied port for you to use. Similarly, by placing my_addr.sin_addr.s_addr as Inaddr_any, the system automatically fills in the native IP address.
Note that when you use the BIND function, you need to convert Sin_port and sin_addr to network byte precedence, while SIN_ADDR does not need to be converted.
There are two kinds of byte precedence for computer data storage: high byte precedence and low byte priority. Data on the Internet is transmitted over the network in high order byte precedence, so for machines that store data internally as a low-byte priority, the transfer of data over the Internet is required, or data inconsistency occurs.
Here are a few byte-order conversion functions:
htonl (): Converts 32-bit values from host byte order to network byte order
htons (): Converts 16-bit values from host byte order to network byte order
Ntohl (): Converts a 32-bit value from a network byte order to a host byte order
Ntohs (): Converts a 16-bit value from a network byte order to a host byte order
The Bind () function returns 0 when it is successfully invoked, returns "1" When an error occurs, and resets the errno to the corresponding error number. It is important to note that when calling the bind function, the port number is not typically set to a value less than 1024 because 1 to 1024 is the reserved port number, and you can choose any port number that is greater than 1024 that is not occupied.

Connection Creation
The connection-oriented client program uses the Connect function to configure the socket and establish a TCP connection to the remote server with a function prototype of:
int connect (int sockfd, struct sockaddr *serv_addr,int addrlen);
SOCKFD is the socket descriptor returned by the socket function; Serv_addr is a pointer to the IP address and port number of the remote host; Addrlen is the length of the remote geologic structure. The Connect function returns 1 when an error occurs and sets errno to the corresponding error code. Client-side programming does not need to call bind (), because in this case only need to know the destination machine's IP address, and the client through which port to establish a connection with the server does not require concern, the socket actuator for your program automatically select an unoccupied port, and notify you of the program data when the interruption.
The Connect function initiates a direct connection to the remote host. This socket must be connected to the remote host only if the connection-oriented client is using the socket. A no-connection protocol never establishes a direct connection. A connection-oriented server also never initiates a connection, it simply listens to the client's request on the protocol port.
The listen function causes the socket to be in passive listening mode and establishes an input data queue for the socket, saving the arriving service requests in this queue until the program processes them.
int listen (int sockfd, int backlog);
SOCKFD is the socket descriptor returned by the socket system call, the backlog specifies the maximum number of requests allowed in the request queue, and incoming connection requests will wait in the queue for accept () them (see below). The backlog limits the number of requests waiting in the queue for services, with most system defaults of 20. If a service request arrives, the input queue is full, the socket rejects the connection request, and the customer receives an error message.
When an error occurs, the Listen function returns-1, and the corresponding errno error code is placed.
The Accept () function lets the server 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.
int accept (int sockfd, void *addr, int *addrlen);
SOCKFD is the socket descriptor that is being monitored, and addr is usually a pointer to the SOCKADDR_IN variable that holds the information for the host that made the connection request service (a host makes the request from a port); Addrten is usually a pointer to a value of sizeof ( The integer pointer variable of the struct sockaddr_in. When an error occurs, the Accept function returns 1 and resets the corresponding errno value.
First, when the socket monitored by the Accept function receives a connection request, the socket executor creates a new socket that connects the new socket to the address of the requesting connection process, and the initial socket that receives the service request can still continue in the previous On the socket, you can also perform data transfer operations on the new socket descriptor.

Data Transfer
The Send () and recv () functions are used for data transfer on a connection-oriented socket.
The Send () function prototype is:
int send (int sockfd, const void *msg, int len, int flags);
SOCKFD is the socket descriptor that you want to use to transmit data; the MSG is a pointer to the data to be sent; Len is the length of the data in bytes; Flags are typically set to 0 (refer to the Man manual for the usage of this parameter).
The Send () function returns the number of bytes actually sent, possibly less than the data you want to send. The return value of Send () should be compared with the number of bytes to be sent in the program. This situation should be handled when the Send () return value does not match Len.
Char *msg = "hello!";
int Len, bytes_sent;
......
Len = strlen (msg);
Bytes_sent = Send (SOCKFD, msg,len,0);
......
The recv () function prototype is:
int recv (int sockfd,void *buf,int len,unsigned int flags);
SOCKFD is the socket descriptor that accepts data; BUF is the buffer that holds the received data; Len is the length of the buffer. The flags were also set to 0. RECV () returns the number of bytes actually received and, when an error occurs, returns 1 and resets the corresponding errno value.
Sendto () and recvfrom () are used for data transfer in a non-connected datagram socket mode. Since the local socket does not establish a connection to the remote machine, it should be the address of the eye when sending the data.
The SendTo () function prototype is:
int sendto (int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
The function has two more parameters than the Send () function, to represents the IP address and port number information of the target machine, and Tolen is often assigned to sizeof (struct sockaddr). The Sendto function also returns the length of the data bytes actually sent or returns 1 if a Send error occurs.
The Recvfrom () function prototype is:
int recvfrom (int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
From is a struct sockaddr variable that holds the IP address and port number of the source machine. Fromlen is often used as sizeof (struct sockaddr). When Recvfrom () returns, Fromlen contains the number of bytes of data actually deposited from the from. The Recvfrom () function returns the number of bytes received or returns 1 when an error occurs, collocated with the corresponding errno.
If you call the Connect () function on the datagram socket, you can also use Send () and recv () for data transfer, but the socket is still a datagram socket and leverages the transport layer's UDP service. However, when the data is sent or received, the kernel automatically adds the target and source address information to it.

End transfer
When all data operations are finished, you can call the close () function to release the socket, thereby stopping any data manipulation on that socket:
Close (SOCKFD);
You can also call the shutdown () function to close the socket. This function allows you to stop data transfer in one direction only, while data transfer in one Direction continues. If you can close a socket's write operation and allow the data to continue to be accepted on that socket until all data is read.
int shutdown (int sockfd,int how);
SOCKFD is the descriptor of the socket that needs to be closed. Parameter how allows you to choose the following methods for shutdown operations:
0-------not allowed to continue receiving data
1-------not allowed to continue sending data
·2-------is not allowed to continue sending and receiving data,
• Both allow to call Close ()
Shutdown returns 0 when the operation succeeds and returns 1 when an error occurs and the corresponding errno is placed.

Socket Programming Example
The server in the code instance sends the string "Hello, you are connected!" to the client through the socket connection. As long as the server software is running on the server, client software is run on the client and the clients receive the string.
The server software code is as follows:
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVPORT 3333/* Server listening port number */
#define BACKLOG 10/* Maximum simultaneous connection requests */
Main ()
{
int sockfd,client_fd; /*SOCK_FD: Monitor SOCKET;CLIENT_FD: Data transfer Socket */
struct sockaddr_in my_addr; /* Native Address information */
struct sockaddr_in remote_addr; /* Client Address information */
if ((SOCKFD = 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 (SOCKFD, struct sockaddr *) &my_addr, sizeof (struct sockaddr)) = =-1) {
Perror ("Bind Error! ");
Exit (1);
}
if (Listen (SOCKFD, BACKLOG) = =-1) {
Perror ("Listen Error! ");
Exit (1);
}
while (1) {
sin_size = sizeof (struct sockaddr_in);
if (client_fd = Accept (SOCKFD, (struct sockaddr *) &remote_addr, &sin_size)) = =-1) {
Perror ("Accept error");
Continue
}
printf ("received a connection from%s", Inet_ntoa (REMOTE_ADDR.SIN_ADDR));
if (!fork ()) {/* Child Process Code Snippet */
if (Send (CLIENT_FD, "Hello, connected!", 26, 0) = =-1)
Perror ("Send Error! ");
Close (CLIENT_FD);
Exit (0);
}
Close (CLIENT_FD);
}
}
}
The server's workflow is this: first call the socket function to create a socket, and then call the BIND function to bind it to the native address and a local port number, and then call listen to listen on the corresponding socket, when Accpet receives a connection service request, A new socket will be generated. The server displays the IP address of the client and sends the string "Hello,you is connected!" to the client through a new socket. Finally, close the socket.
The fork () function in the code instance generates a child process to process the data Transfer section, and the fork () statement returns a value of 0 for the child process. So the If statement that contains the fork function is the part of the child Process code that executes concurrently with the part of the parent process code that follows the IF statement.

The client program code is as follows:
#include
#include
#include
#include
#include
#include
#include
#include
#define Servport 3333
#define MAXDATASIZE 100//Maximum data transfer per time */
Main (int argc, char *argv[]) {
int SOCKFD, recvbytes;
Char Buf[maxdatasize];
struct Hostent *host;
struct sockaddr_in serv_addr;
if (ARGC < 2) {
fprintf (stderr, "Please enter the server" s hostname! ");
Exit (1);
}
if ((Host=gethostbyname (argv[1))) ==null) {
Herror ("GetHostByName Error! ");
Exit (1);
}
if ((SOCKFD = 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 (SOCKFD, struct sockaddr *) &serv_addr,
sizeof (struct sockaddr)) = =-1) {
Perror ("Connect Error! ");
Exit (1);
}
if ((Recvbytes=recv (SOCKFD, buf, maxdatasize, 0)) ==-1) {
Perror ("Recv Error! ");
Exit (1);
}
Buf[recvbytes] = "n";
printf ("Received:%s", buf);
Close (SOCKFD);
}
The client program first obtains the server IP address through the server domain name, then creates a socket, calls the Connect function to establish the connection with the server, receives the data sent from the server after the connection succeeds, and finally closes the socket.
The function gethostbyname () is the completion of the domain name conversion. Because the IP address is difficult to remember and read and write, so for convenience, people often use the domain name to represent the host, which requires the domain name and IP address conversion. The function prototypes are:
struct hostent *gethostbyname (const char *name);
The function returns the structure type of hosten, which is defined as follows:
struct Hostent {
Char *h_name; /* Official domain Name of host */
Char **h_aliases; /* A null-terminated array of host aliases */
int h_addrtype; /* Address type returned, af-inet in Internet environment */
int h_length; /* Byte length of address */
Char **h_addr_list; /* A 0-terminated array containing all the addresses of the host */
};
#define H_ADDR H_addr_list[0]/* First address in h-addr-list */
When the GetHostName () call succeeds, returns a pointer to the struct Hosten, which returns 1 when the call fails. When calling gethostbyname, you cannot use the perror () function to output an error message, instead use the Herror () function to output it.

A disconnected client/server program is the same as the client/server that is connected, the difference is that customers in a disconnected client/server generally do not need to establish a connection, and when sending received data, you need to 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 finishes its specified task. For example, when a program executes a function call that reads data, the next program statement will not be executed until the function completes the read operation. When the server runs to the Accept statement, and no Client connection service request arrives, the server stops waiting on the accept statement to wait for the connection service request to arrive. This condition is called blocking (blocking). The non-blocking operation can be done immediately. For example, if you want the server to just check to see if a customer is waiting for a connection, to accept the connection, or to continue doing something else, you can do so by setting the socket to non-blocking mode. A nonblocking socket causes the accept call to return immediately without a customer waiting.
#include
#include
......
SOCKFD = socket (af_inet,sock_stream,0);
Fcntl (Sockfd,f_setfl,o_nonblock);
......
By setting the socket to non-blocking mode, you can implement "poll" several sockets. When attempting to read data from a non-blocking socket that has no data waiting to be processed, the function returns immediately with a return value of-1 and a errno value of Ewouldblock. But this "polling" causes the CPU to be in a busy wait mode, which reduces performance and wastes system resources. The call to select () effectively solves this problem by allowing you to hang up the process itself while allowing the system kernel to listen for any activity on the requested set of file descriptors, as long as the activity is confirmed on any monitored file descriptor, select () The call returns information that indicates that the file descriptor is ready, allowing random changes to be selected for the process without wasting CPU overhead by the process itself testing the input. The Select function prototype is:
int select (int numfds,fd_set *readfds,fd_set *writefds,
Fd_set *exceptfds,struct timeval *timeout);
Where Readfds, Writefds, and Exceptfds are the set of file descriptors for read, write, and exception handling that are monitored by select (). If you want to determine whether you can read data from a standard input and a socket descriptor, you only need to add the standard input file descriptor 0 and the corresponding SOCKDTFD to the Readfds collection; The Numfds value is the highest number of file descriptions multibyte 1 that need to be checked, In this example, the value of Numfds should be sockfd+1; When Select returns, Readfds will be modified to indicate that a file descriptor is ready to be read and you can test it by Fd_issset (). To implement the setup, reset, and test of the corresponding file descriptor in Fd_set, it provides a set of macros:
Fd_zero (fd_set *set)----Clears a set of file descriptors;
Fd_set (int fd,fd_set *set)----Add a file descriptor to the file descriptor set;
FD_CLR (int fd,fd_set *set)----Remove 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 a struct TIMEVAL type that enables select () to return without a file descriptor ready after waiting for a timeout for a long time. The struct TIMEVAL data structure is:
struct Timeval {
int tv_sec; /* seconds */
int tv_usec; /* microseconds */
};

POP3 Client Instance
The following code instance connects to the mail server and retrieves messages for the specified user account based on the POP3 client agreement. The commands that interact with the mail server are stored in the string array popmessage, and the program sends these commands sequentially through a do-while loop.
#include
#include
#include
#include
#include
#include
#include
#include
#define POP3SERVPORT 110
#define MAXDATASIZE 4096

Main (int argc, char *argv[]) {
int sockfd;
struct Hostent *host;
struct sockaddr_in serv_addr;
Char *popmessage[]={
"USER userid",
"PASS password",
"STAT",
"LIST",
"RETR 1",
"DELE 1",
"QUIT",
Null
};
int ilength;
int imsg=0;
int iend=0;
Char Buf[maxdatasize];

if ((Host=gethostbyname ("Your.server")) ==null) {
Perror ("gethostbyname error");
Exit (1);
}
if ((SOCKFD = socket (af_inet, sock_stream, 0)) = = = 1) {
Perror ("socket error");
Exit (1);
}
Serv_addr.sin_family=af_inet;
Serv_addr.sin_port=htons (Pop3servport);
serv_addr.sin_addr = * (struct in_addr *) host->h_addr);
Bzero (& (Serv_addr.sin_zero), 8);
if (Connect (SOCKFD, (struct sockaddr *) &serv_addr,sizeof (struct sockaddr)) ==-1) {
Perror ("Connect error");
Exit (1);
}

do {
Send (Sockfd,popmessage[imsg],strlen (popmessage[imsg]), 0);
printf ("Sent:%s", popmessage[imsg]);

Ilength=recv (sockfd,buf+iend,sizeof (BUF)-iend,0);
Iend+=ilength;
Buf[iend]= "";
printf ("Received:%s,%d", buf,imsg);

imsg++;
} while (Popmessage[imsg]);

Close (SOCKFD);
}

Socket programming under Linux

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.