Socket programming: Simple TCP server/client programming

Source: Internet
Author: User
Tags htons

In fact, for the socket: what we need to understand is that he provides a programming concept, using the socket can be used in the system has been encapsulated internal communication, we only need to focus on the application layer of data control is OK.

One. socket (socket)

    Socket in English for the meaning of the socket, that is, to provide users with a link to access the interface. In a computer network, an IP address identifies a single host, and a port number identifies the only application process in the host, so "ip+ port number" can be called a socket. .

Two hosts to communicate between the process, you can set up a socket, in fact, can be seen as a separate "socket", and then by connecting the "socket" at both ends of the socket is composed of these two sockets to identify the only one connection, This represents a one-to-one relationship in a network connection.

Let's start with a quick look at what the socket really is.

First, for socket programming, he needs to specify the address of the socket as a parameter, so there are different protocols in the network protocol, that is, there are different ways to define the address structure. For these structures, the sockaddr_ begins. Each protocol has a unique suffix, such as Ethernet, which is sockaddr_in, which is commonly used.

Then let's look at the generic socket structure:

struct sockaddr{sa_family sa_family; Char sa_data[14];}

This is the prototype of the socket, and note that in socket programming, the function of sockaddr_ is required to convert the type conversion to SOCKADDR.

The commonly used sockets structure in Ethernet is:
650) this.width=650; "src=" Http://s1.51cto.com/wyfs02/M01/80/63/wKioL1c_-8_z8ebhAABpuHEw0_w308.png "title=" QQ picture 20160521140619.png "alt=" Wkiol1c_-8_z8ebhaabpuhew0_w308.png "/>

The corresponding relationship is:

650) this.width=650; "src=" Http://s1.51cto.com/wyfs02/M01/80/65/wKiom1c_-z_RNXPoAACPm3Hd9Ew994.png "title=" QQ picture 20160521140749.png "alt=" Wkiom1c_-z_rnxpoaacpm3hd9ew994.png "/>

Sin_family: Corresponds to Address type: Af_inet represents IPv4.

Sin_port: Represents the port number.

SIN_ADDR.S_ADDR: Represents the IP address we have established.


Before programming, we need to be concerned that in the computer, the byte order storage is divided into big-endian small segment, in the network byte order, the use of the endian state. There may be small ends in the computer where there is a big end, so there are some byte-order conversion functions:

650) this.width=650; "src=" Http://s5.51cto.com/wyfs02/M02/80/63/wKioL1c__WaBI_aOAAA5iUM80Z8726.png "title=" QQ picture 20160521141301.png "alt=" Wkiol1c__wabi_aoaaa5ium80z8726.png "/>

Just like the function name. The host byte-order to is converted to net byte-order l Long4 byte length, and the rest function is the same.


And then we need to be concerned that in our sockaddr_in, there are sin_addr.s_addr types of IP addresses, dotted decimal types of strings, and binary types, so there is a series of IP address structure conversion functions:

650) this.width=650; "src=" Http://s5.51cto.com/wyfs02/M01/80/65/wKiom1c__VyAPHmTAABn0SAXcww503.png "title=" QQ picture 20160521141652.png "alt=" Wkiom1c__vyaphmtaabn0saxcww503.png "/>

Depending on the input parameters and output parameters of these functions, you can see what they convert from to what,

Then there are 2 functions for safe conversion:

650) this.width=650; "src=" Http://s4.51cto.com/wyfs02/M01/80/65/wKiom1c__k2DHUeKAAAblh9UflA479.png "style=" float: none; "title=" QQ picture 20160521142019.png "alt=" Wkiom1c__k2dhuekaaablh9ufla479.png "/>

650) this.width=650; "src=" Http://s1.51cto.com/wyfs02/M01/80/65/wKiom1c__k2DbQrTAAAk4ec8eI0939.png "style=" float: none; "title=" QQ picture 20160521142050.png "alt=" Wkiom1c__k2dbqrtaaak4ec8ei0939.png "/>

These 2 functions are for different protocol family address translation, the first parameter represents a network type protocol family.


Based on the above understanding, let's look at the connection transfer release process of the socket for reliable transmission based on the TCP protocol:

650) this.width=650; "src=" Http://s3.51cto.com/wyfs02/M02/80/66/wKiom1dAAVCwD7eqAABF_zIVkkM570.png "title=" Untitled. png "alt=" wkiom1daavcwd7eqaabf_zivkkm570.png "/> Then we need to look at a few questions:

    1. Client-to-server interaction process:

The client connection process, on the server side, is the receive process. The TCP connection is then established 3 times during the process.

The data exchange between the client and the server is a relative process, and the client's reading data corresponds to the process of writing data on the servers side. The client's write data corresponds to the server's read data process.

After the interaction is complete, close the socket connection.


The following begins the realization of socket programming.

First, let's take a look at the code and then explain it:

First look at the server-side code:

#include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <string.h> #include <error.h> #include <arpa/inet.h> #define  _prot_ 8888#define _ Backlog_ 5void process_conn_server (int s) {ssize_t size =  0;char  Buffer[1024];while (1) {size = read (s,buffer,1024); if (size == 0) {return ;} sprintf (buffer, "%d bytes altongether\n", size); Write (S,buffer,strlen (buffer)  + 1);}} Int main () {Int ss,sc;struct sockaddr_in server_sock;struct sockaddr_in client_ Sock;pid_t pid;int err;ss = socket (af_inet,sock_stream,0); if (ss < 0) {printf (" Sock build error "); return 1;} Bzero (&server_sock,sizeof (Server_sock)); Server_sock.sin_family = af_inet;server_sock.sin_addr.s_ Addr = htonl (Inaddr_any); server_sock.sin_port = htons (_prot_); Err = bind (sS, (struct sockaddr *) &server_sock,sizeof (Server_sock)), if (bind < 0) {printf ("bind  error "); return 2;} Err = listen (Ss,_backlog_); if (err < 0) {printf ("Listen is error");return  3;} while (1) {socklen_t len = sizeof (STRUCT&NBSP;SOCKADDR); Sc = accept (SS, (struct  sockaddr *) &client_sock,&len); if (sc < 0) {continue;} Pid = fork (); if (pid == 0) {process_conn_server (SC); close (ss);} Else{close (SC);}} return 0;}

For this code, let's look at some of the efficiency and security issues that the server needs to do with the function call process that the socket will need first.

First we need to create the network Jack function socket ():

650) this.width=650; "src=" Http://s4.51cto.com/wyfs02/M01/80/64/wKioL1dABMrgTd1dAAApZ4fe_Ww034.png "title=" QQ picture 20160521144318.png "alt=" Wkiol1dabmrgtd1daaapz4fe_ww034.png "/>

Domain to set up network communication domains and use Af_inet in Ethernet.

Type is the kind of socket communication that is set, TCP is for byte stream transmission, so use sock_stream.

Protocol is usually set to 0;

For this function, he is to open a file descriptor for our communication, the invocation succeeds in returning the file descriptor for easy data transfer, and the failure returns 1. Simultaneous outgoing error value.

In this set of functions in socket programming, the underlying encapsulation is provided, providing a relative excuse,

Let's take a look at the kernel implementation of the socket ():

650) this.width=650; "src=" Http://s2.51cto.com/wyfs02/M01/80/66/wKiom1dABmjwHIdbAAArT_CgKSM447.png "title=" 1.png " alt= "Wkiom1dabmjwhidbaaart_cgksm447.png"/>

After the user calls the socket (),

The system calls Sys_socket, where

    1. Generates the kernel socket structure.

    2. Binds to the file descriptor. Pass the binding file descriptor value to the application layer.

When the socket file descriptor succeeds, the socket is required for address and port binding, which is the bind () function, and we need to create a sockaddr_in struct to bind.

Consider the function:

650) this.width=650; "src=" Http://s1.51cto.com/wyfs02/M01/80/66/wKiom1dACM-CqTorAAAtgUmmb_0400.png "title=" QQ picture 20160521144424.png "alt=" Wkiom1dacm-cqtoraaatgummb_0400.png "/>

The first parameter is the file descriptor that we call the socket, and the second is the SOCKADDR_IN structure we created, of course, we need to cast the arguments when we pass the parameters, and the 3rd parameter is the length of the struct we set, and also the input and output data.

When bind returns 0 to indicate that the binding was successful, return 1 indicates that the binding failed.

And then we'll just take a look at his kernel call:

650) this.width=650; "src=" Http://s2.51cto.com/wyfs02/M01/80/64/wKioL1dADhjCTuVtAACBfzGfiNo617.png "title=" 2.png " alt= "Wkiol1dadhjctuvtaacbfzgfino617.png"/> can then enter the listening state, function listen () is used to initialize the server can connect the queue, the server unified time can only handle a client connection, When multiple client connection requests arrive at the same time, the queue needs to be queued, one for processing.

Listen function:

650) this.width=650; "src=" Http://s5.51cto.com/wyfs02/M00/80/64/wKioL1dACbHR-XzRAAAngrEak_w204.png "title=" QQ picture 20160521144355.png "alt=" Wkiol1dacbhr-xzraaangreak_w204.png "/>

SOCKFD represents the file descriptor,

The backlog represents the length of the wait queue, returns 0 successfully, and returns 1 for failure.

Listen kernel:
650) this.width=650; "src=" http://s4.51cto.com/wyfs02/M01/80/67/wKiom1dAEdjj41TeAAGP_sFenHI399.jpg "title=" Img_ 1364.JPG "alt=" Wkiom1daedjj41teaagp_sfenhi399.jpg "/>

The picture is programmed from the Linux network.

Then when we set the listener reason is accept () wait for the connection, then we will return a new socket file descriptor to indicate the client connection, the client connection information can be obtained by this new descriptor, and then through write and read to achieve data transfer.

Take a look at the Accept () function:

650) this.width=650; "src=" Http://s2.51cto.com/wyfs02/M00/80/67/wKiom1dAEVei-asvAAAsVX6B8Js612.png "title=" QQ picture 20160521154203.png "alt=" Wkiom1daevei-asvaaasvx6b8js612.png "/>

function parameters,

SOCKFD is the creation of a socket, this socket is and listen with the same socket, because it is sent to the listener to get a request to connect;

addr is a pointer to the network address information structure that is used to describe the request connection Party;

Addrlen is the size of the above structure;

The function succeeds in returning a valid received socket descriptor, and the failed return-1 resets the error code;

Kernel Call:

650) this.width=650; "src=" http://s3.51cto.com/wyfs02/M02/80/65/wKioL1dAEyLhOV3XAAIvUc6c1tQ805.jpg "title=" Img_ 1363.JPG "alt=" Wkiol1daeylhov3xaaivuc6c1tq805.jpg "/>

Okay, this is our server, he's connected.

Let's look at the writing of the client, the code:

 #include <stdio.h> #include <unistd.h> #include <string.h># include<error.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h># Define _port_ 8888void process_conn_client (int s) {ssize_t size  = 0; Char buffer[1024];while (1) {size = read (0,buffer,1024); if (size > 0) {write (S,buffer, size); Size = read (s,buffer,1024); write (1,buffer,size);}} Int main () {Int s;struct sockaddr_in server_sock;s = socket (AF_INET,SOCK_STREAM,0 ); if (s < 0) {printf ("sock error"); return 1;} Bzero (&server_sock,sizeof (Server_sock)); Server_sock.sin_family = af_inet;server_sock.sin_addr.s_ Addr = htonl (Inaddr_any); server_sock.sin_port = htons (_port_); Connect (s, (struct  sockaddr *) &server_sock,sizeof (server_sock));p rocess_conn_client (s); close (s); return 0;} 

For the client, we just need to connect to our service side, so we just need to focus on a function, connect function, and then we look at the Connect () function:

650) this.width=650; "src=" Http://s5.51cto.com/wyfs02/M00/80/67/wKiom1dAEuSDWOANAAAsZTlZ17E634.png "title=" QQ picture 20160521154841.png "alt=" Wkiom1daeusdwoanaaasztlz17e634.png "/>

function parameters,

SOCKFD is a socket file descriptor created by the connecting party;

addr Because it is the connection requester, is the remote to receive the connection request the network socket address information of the party;

Addrlen is the size of the above network address information structure;

The function returns 0 successfully, and the failed return-1 resets the error code;

His kernel implementations are:

650) this.width=650; "src=" Http://s2.51cto.com/wyfs02/M02/80/67/wKiom1dAEiHyOTyzAAIfq6lVouU771.jpg "title=" QQ picture 20160521152900.jpg "alt=" Wkiom1daeihyotyzaaifq6lvouu771.jpg "/>

For both the client and the server, our programming is nearly implemented, and then we need to consider several performance issues about the server side:

In the code I wrote, I used a multi-process approach, and each time we set up a connection, we fork a child process, transfer the data in the subprocess, and then our parent process closes the file descriptor generated by the accept, and the child process closes the unwanted listener's file descriptor.

In a multi-process, we need to notice that we are using a blocking I/O model, and if we call the wait function in our parent process, the WAITPID function will always be more wasteful of resource efficiency.

There is also a problem with non-blocking waits for waitpid, when we need to release the child process resources, but the following scenario occurs:

A client connection comes in, and the parent process waits for a non-blocking wait, but if there is no future connection, it will be stuck at accept, causing the zombie process to occur. So there is a problem with this approach, so there is a workaround, which is to use the SIGCHLD signal returned at the end of the child process to capture the custom child process resource release,

Another way is multithreaded programming, the calling thread is a detached state.

        pthread_t tid;// Create a thread         pthread_create (&tid, null, accept_fun,   (void *) accept_sock);         pthread_detach (tid);// Sets the thread to a detached state, and does not have to wait for the main thread to reclaim the resource when it ends 
Void* accept_fun (Void *sock) {    int accept_sock =  (int) sock;     char *buf[1024];    while (1)     {         memset (buf,  ',  sizeof (BUF));         size_t size = read (accept_sock, buf, sizeof (BUF)-1);         if (size < 0)          {            perror ("read");             break;         }        else if (size == 0)          {            printf ( "client is out...\n");nbsp;           break;         }        else             printf ("client# %s\n",  buf);     } }

Of course, he always has performance inefficiencies, whether it's multi-threaded or multi-process, because a thread/process can handle only one connection is inefficient, so we need to continue to learn several models of I/O, using a multiplexed state model for server-side authoring.

This article is from the "egg-left" blog, please be sure to keep this source http://memory73.blog.51cto.com/10530560/1775694

Socket programming: Simple TCP server/client programming

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.