Linux network programming One-stop learning

Source: Internet
Author: User
Tags ack erro function prototype htons

Feed

Learned a lot of computer network, still do not network programming.

This will not be the case after reading this post.

Environment: Ubuntu14.04 64bit


What is a socket

The concept of data communication used in network application programming based on TCP/IP, often referred to as "sockets", is used to describe the IP address and port, which is a handle to a communication chain. Hosts on the Internet typically perform multiple service software, providing several services at the same time. Each service opens a socket and binds to a port, with different ports corresponding to different services. The socket is like a porous socket, as its English intended. A host is like a room full of various sockets, each with a number, some sockets provide 220 vac, some provide 110 volts AC, some provide cable TV programs. The customer software plugs into different numbered sockets and can get different services.


Network program based on TCP protocol

is the general flow of the Client/server program based on the TCP protocol:



With this diagram, we can very clearly relate some of the socket's interfaces to the server and client state transitions in TCP/IP.

Readiness Status

After initialization of the server call socket (), bind (), listen (), call accept () to block the wait, in the state of listening port, after the client calls the socket () initialization, call Connect () Sends a SYN segment and blocks waiting for the server to answer, the server answers a syn-ack segment, the client receives the return from connect (), answers an ACK segment at the same time, and the server returns from accept ().

The process of transferring data

After the connection is established, the TCP protocol provides full-duplex communication service, but the general process of the Client/server program is initiated by the client, and the server passively processes the request, a question and answer method. Therefore, after the server returns from the Accept () call read (), the read socket is like a read pipeline, assuming that no data arrives to block the wait, then the client calls write () to send a request to Server,server to return from Read (), The client request is processed, during which the client calls read () to block waiting for the server to answer, and the server calls write () to send the processing results back to the client, calling the read () plug again to wait for the next request, After the client receives a return from read (), the next request is sent, so the loop goes on.

Assuming that the client does not have many other requests, call Close () to close the connection, just like the end of the write-off pipeline, the server's read () returns 0, so that the server knows that the client closed the connection and also called Close () to close the connection. Note that no matter what side calls Close (), the two transmission direction of the connection is closed and no more data can be sent. Suppose a party calls shutdown () and the connection is in a semi-closed state and can still receive data from the other side.

When learning the socket API, be aware of how the application and the TCP protocol layer interact: * When an application calls a socket function, the TCP protocol layer finishes what action, such as calling Connect () emits a SYN segment * application How to know the state change of the TCP protocol layer, For example, returning from a blocked socket function indicates that the TCP protocol received some segments, and then read () returns 0 to indicate that the fin segment was received.


Code Bricks Start

Run the code before you explain it.

Service side, SERVER.C

/* SERVER.C */#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h># Include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h># Define MAXLINE 80#define serv_port 8000int Main (void) {struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int LISTENFD, Connfd;char Buf[maxline];char str[inet_addrstrlen];int i, N;LISTENFD = socket (af_inet, SOCK_STREAM, 0); Bzero ( &AMP;SERVADDR, sizeof (SERVADDR)); servaddr.sin_family = Af_inet;servaddr.sin_addr.s_addr = htonl (INADDR_ANY);    Servaddr.sin_port = htons (Serv_port); Bind (LISTENFD, (struct sockaddr *) &servaddr, sizeof (SERVADDR)); listen (LISTENFD);p rintf ("accepting Connections \ n "); while (1) {Cliaddr_len = sizeof (CLIADDR); connfd = Accept (LISTENFD, (struct sockaddr *) &cliaddr, &  Amp;cliaddr_len); n = Read (CONNFD, buf, MAXLINE);p rintf ("received from%s at PORT%d\n", Inet_ntop (Af_inet, &cliaddr.sin_addr, str , sizeof (str)), nTohs (Cliaddr.sin_port)); for (i = 0; i < n; i++) Buf[i] = ToUpper (buf[i]); write (CONNFD, buf, N); close (CONNFD);}}


Client,client.c

/* client.c */#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h># Include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define MAXLINE 80#define serv_ PORT 8000int Main (int argc, char *argv[]) {struct sockaddr_in servaddr;char buf[maxline];int sockfd, N;char *str;    if (argc! = 2) {fputs ("Usage:./client message\n", stderr); exit (1);} str = argv[1];    SOCKFD = socket (af_inet, sock_stream, 0) bzero (&servaddr, sizeof (SERVADDR)); servaddr.sin_family = af_inet;inet_ Pton (Af_inet, "127.0.0.1", &servaddr.sin_addr); servaddr.sin_port = Htons (serv_port);    Connect (SOCKFD, (struct sockaddr *) &servaddr, sizeof (SERVADDR)); write (SOCKFD, str, strlen (str)); n = Read (SOCKFD, BUF, MAXLINE);p rintf ("Response from server:\n"); Write (Stdout_fileno, buf, N); close (SOCKFD); return 0;}

Compile

GCC client.c-o CLIENTGCC server.c-o Server

Execution results



Start with the beginning of the file explanation

The interfaces defined in Unistd.h-unistd.h are typically a large number of packages (English: Wrapper functions) for system calls, such as fork, pipe, and various I/O primitives (read, write, close, and so on).
Sys/socket.h-Provides socket functions and data structures.
Netinet/in.h-Defines the data structure sockaddr_in, which contains the address format definitions for IPV4 and IPV6.
Arpa/inet.h-provides IP address translation functions, such as the conversion of the size end, and the transformation of the expression of the IP address.


Look at the main function of the server.

16 lines, declaring two socketaddr_in structures. The IPV4 address is represented by the SOCKADDR_IN structure, which contains 16-bit port numbers and 32-bit IP addresses. The IPV6 address is represented by a sockaddr_in6 struct, which contains 16-bit port numbers, 128-bit IP addresses, and some control fields.

17 lines, declare a variable that records the length of the client address, socklen_t is the type used in the socket to denote length. LInus Torvalds (he wants to have a lot of other people, but apparently not very much) trying to explain to them that using size_t is completely wrong, because the length of the size_t and int is not the same in a 64-bit structure, which is the third parameter of the Accept function ) must be the same length as int, as this is the BSD socket interface standard. Finally, the POSIX guys found a solution, which is to create a new type of "socklen_t".  

23 Lines, open a network communication port, the function prototype is:

int socket (int family, int type, int protocol);

Socket () Open a network communication port, if successful, just like Open () return a file descriptor, the application can be like reading and writing files with Read/write on the network to send and receive data, assuming that the socket () call error is returned-1. For ipv4,family The parameter is specified as Af_inet. For the TCP protocol, the type parameter is specified as Sock_stream, which represents the stream-oriented transport protocol.


25-28 lines, the entire structure is cleared first, and then set the address type Af_inet, the network address is Inaddr_any, this macro represents a local random IP address, because the server may have multiple network cards, each network card may also bind multiple IP addresses, This setting is able to listen on all IP addresses until a connection with a client is established, which IP address is used, and the PORT number is serv_port, which we define as 8000.


30 lines, call bind bind a fixed network address and port number. Bind () successfully returns 0, and the failure returns-1. Function Prototypes:

int bind (int sockfd, const struct SOCKADDR *myaddr, socklen_t Addrlen);

The role of BIND () is to bind the SOCKFD and myaddr together so that sockfd, the file description descriptor for network communication, listens to the address and port number of the narrative described in Myaddr.


32 lines, declares that LISTENFD is listening, and agrees that there are at most 20 clients in a connected state, assuming that many other connection requests are ignored. Listen () successfully returned 0, failed to return-1.


25-49 lines, the whole is a while dead loop, each cycle of processing a client connection. Since Cliaddr_len is an incoming outgoing parameter, the initial value should be assigned again before each call to accept (). The LISTENFD of the Accept () is the previous listener file descriptor, and the return value of accept () is another file descriptor CONNFD, which then communicates with the client through this CONNFD communication, and finally closes the CONNFD disconnect. Instead of closing LISTENFD, return to the beginning of the loop again LISTENFD is still used as the accept parameter. Accept () successfully returns a file descriptor with an error of 1.


The client has less bind and more connect functions. Because the client does not need a fixed port number, it does not have to call bind (), and the client's port number is allocated by the kernel itself. Note that the client does not agree to call bind (), it is simply not necessary to call bind () to fix a port number, the server does not have to call bind (), but assuming that the server does not call bind (), the kernel will voluntarily assign the server to listen to port. Each time you start the server, the port number is different, and the client will have trouble connecting to the server. Connect function Prototype:

int connect (int sockfd, const struct SOCKADDR *servaddr, socklen_t Addrlen);

The client needs to call Connect () to connect server,connect and bind in the same way, the difference is that the bind is its own address, and connect is the number of the address of the other. Connect () successfully returned 0, error returned-1.


A little package, please.

Although the code can work, but look at a bit of egg pain, and the entire code has a process error, errors are also very difficult to deal with, first of all, the socket related functions encapsulated once,

Wrap.h

#include <stdlib.h> #include <sys/socket.h> #include <errno.h>//print error message and exit.void Perr  _exit (const char *s);//server Accept message from Client.int accept (int fd, struct sockaddr *sa, socklen_t *salenptr); void Bind (int fd, const struct  sockaddr *sa, socklen_t salen), void Connect (int fd, const struct  sockaddr *sa, Socklen_ T salen), void Listen (int fd, int backlog), int Socket (int family, int type, int protocol), ssize_t Read (int fd, void *ptr, S ize_t nbytes); ssize_t readn (int fd, void *vptr, size_t n); ssize_t Write (int fd, const void *ptr, size_t nbytes); ssize_t Wr Iten (int fd, const void *vptr, size_t n), static ssize_t my_read (int fd, char *ptr), ssize_t Readline (int fd, void *vptr, si ze_t maxlen); void Close (int fd);

Wrap.c

#include "wrap.h"//print error message and exitvoid perr_exit (const char *s) {perror (s); exit (1);} Server Accept Message from Client.int accept (int fd, struct sockaddr *sa, socklen_t *salenptr) {int n;again:if (n = accep T (FD, SA, salenptr)) < 0) {if (errno = = econnaborted) | | (errno = = eintr)) Goto Again;elseperr_exit ("Accept Error");} return n;} void Bind (int fd, const struct SOCKADDR *sa, socklen_t salen) {if (Bind (FD, SA, Salen) < 0) {Perr_exit ("bind error");}} void Connect (int fd, const struct SOCKADDR *sa, socklen_t salen) {if (Connect (FD, SA, Salen) < 0) {perr_exit ("Connet Erro R ");}} void Listen (int fd, int backlog) {if (Listen (FD, Backlog) < 0) {perr_exit ("Listen error");}} int Socket (int family, int type, int protocol) {int n;if (n = Socket (family, type, protocol)) < 0) Perr_exit ("Socket Erro R "); return n;} ssize_t Read (int fd, void *ptr, size_t nbytes) {ssize_t N;again:if ((n = Read (fd, PTR, nbytes)) = =-1) {if (errno = = eintr) got o again;elsereturn-1;} return n;} ssize_t readn (int fd, VOID *vptr, size_t N) {size_t nleft;ssize_t Nread;char *ptr;ptr = Vptr;nleft = N;while (Nleft > 0) {if (nread = Read (FD, PTR, nleft)) < 0) {if (errno = = eintr) nread = 0;elsereturn-1;} else if (nread = = 0) Break;nleft-= nread;ptr + = nread;} return n-nleft;} ssize_t Write (int fd, const void *ptr, size_t nbytes) {ssize_t N;again:if ((n = Write (fd, PTR, nbytes)) = =-1) {if (errno = = E INTR) goto again;elsereturn-1;} return n;} ssize_t writen (int fd, const void *vptr, size_t n) {size_t nleft;ssize_t nwritten;const char *ptr;ptr = Vptr;nleft = N;whil E (Nleft > 0) {if (Nwritten = write (FD, PTR, nleft) <= 0) {if (Nwritten < 0 && errno = = eintr) {nwritten = 0; }else{return-1;}} Nleft-= nwritten;ptr + = Nwritten;} return n;}  static ssize_t my_read (int fd, char *ptr) {static int read_cnt;static char *read_ptr;static char read_buf[100];if (read_cnt  <= 0) {again:if ((read_cnt = Read (fd, read_buf, sizeof (READ_BUF))) < 0) {if (errno = = eintr) goto again;return-1;} else if (read_cnt= = 0) return 0;read_ptr = Read_buf;} Read_cnt--;*ptr = *read_ptr++;return 1;} ssize_t Readline (int fd, void *vptr, size_t maxlen) {ssize_t n, rc;char c, *ptr;ptr = vptr;for (n = 1; n < maxlen; n+ +) {if (rc = My_read (FD, &c) = = 1) {*ptr++ = c;if (c = = ' \ n ') break;} else if (rc = = 0) {*ptr = 0;return n-1;} el sereturn-1;} *ptr = 0;return N;} void Close (int fd) {if (Close (FD) = =-1) {Perr_exit ("close Error");}}

The error handling here mainly uses the Erron and PERR functions.

When the C API function in Linux is abnormal, the errno variable (need include errno.h) is usually assigned an integer value, different values represent different meanings, and can be viewed by guessing the cause of the error, and in the actual programming with this trick to overcome many of the original seemingly inexplicable problems. But errno is a number, representing the detailed meaning of the errno.h to read the macro definition, and each review is a very tedious thing. There are several ways to easily get error messages


(1) void perror (const char *s)
Function description
The Perror () is used to output the cause of the previous function error to a standard error (STDERR), and the string referred to in S is printed first, followed by the error reason string. The cause of this error follows the global variable
The errno value to determine the string to output.

(2) Char *strerror (int errno)
Converts the error code to a string error message that can be combined to output the string and other information to the user interface such as
fprintf (stderr, "Error in CreateProcess%s, Process ID%d
", Strerror (errno), ProcessID)
Note: Suppose ProcessID is an ID that has been acquired


(3) printf ("%m", errno);
And not all of them. Error codes can be obtained by mistake when they occur, such as the following code snippet


Change the client to a mode that can send messages multiple times after a successful connection.

Client.c

/* CLIENT.C */#define MAXLINE 80#define serv_port 8000int Main (int argc, char *argv[]) {struct sockaddr_in Servaddr;char bu F[maxline];int sockfd, N;char *str;    SOCKFD = Socket (af_inet, sock_stream, 0) bzero (&servaddr, sizeof (SERVADDR)); servaddr.sin_family = af_inet;inet_ Pton (Af_inet, "127.0.0.1", &servaddr.sin_addr); servaddr.sin_port = Htons (serv_port);    Connect (SOCKFD, (struct sockaddr *) &servaddr, sizeof (SERVADDR)), while (Fgets (buf, MAXLINE, stdin)! = NULL) {Write ( SOCKFD, buf, strlen (BUF)), n = Read (SOCKFD, buf, MAXLINE), if (n = = 0) {printf ("The other side have been closed.\n");} Else{write (Stdout_fileno, buf, n);}} Close (SOCKFD); return 0;}


SEVER.C also has to make a bit of change so that it can handle requests from the same client multiple times.

/* SERVER.C */#define MAXLINE 80#define serv_port 8000int Main (void) {struct sockaddr_in servaddr, cliaddr;socklen_t cliad Dr_len;int listenfd, Connfd;char buf[maxline];char str[inet_addrstrlen];int i, N;LISTENFD = Socket (AF_INET, SOCK_STREAM , 0); Bzero (&servaddr, sizeof (SERVADDR)); servaddr.sin_family = Af_inet;servaddr.sin_addr.s_addr = Htonl (INADDR_    any); Servaddr.sin_port = Htons (Serv_port); Bind (LISTENFD, (struct sockaddr *) &servaddr, sizeof (SERVADDR)); Listen (LISTENFD);p rintf ("accepting connections ... \ n"), while (1) {Cliaddr_len = sizeof (CLIADDR); connfd = Accept ( LISTENFD, (struct sockaddr *) &cliaddr, &cliaddr_len), while (1) {n = Read (CONNFD, buf, MAXLINE), if (n = = 0) {printf (" The other side have been closed!\n "); printf ("received from%s at PORT%d\n", Inet_ntop (Af_inet, &cliaddr.sin_addr, str, sizeof (STR)), Ntohs (CLI    Addr.sin_port)); for (i = 0; i < n; i++) Buf[i] = ToUpper (Buf[i]); Write (CONNFD, buf, n);} Close (CONNFD);}}

Execution results



Small jobs-Simple webserver

Follow the steps below:

1. Terminal input ifconfig, to see their own IP address;

2. Execute the above compiled server program;

3. Type in the address bar of the browser: x.x.x.x:80

(x.x.x.x is your IP address)

Get the results of the browser execution is



This is a simple HTTP request.


In this communication process, the server is our server program, the browser is the client. After the server starts, it starts to listen to the corresponding port, when you enter the IP and port of the computer in the browser, the HTTP protocol header sent by the browser to the server, the processing in server is to change all the letters to uppercase, and then write back to the socket, What the client finally gets is the ToUpper request message.


The webserver we implement is just to parse the first line correctly, which is a GET request, which is the root folder of the service folder/(in this case,/var/www in fact). Webserver should send the index page under the folder (the default is index.html) to the browser, which is to send/var/www/index.html to the browser. If the contents of the file such as the following (HTML file is not required to "\ r \ n" line, with "\ n" line to be able to). Simple index.html.


The data returned by the server is

http/1.1 okcontent-type:text/html


The HTTP header of the server reply is also the end of each line with a carriage return and a newline ending followed by a carriage return with a blank line.

The first line of the HTTP header is the protocol version number and the answer code, 200 indicates success, the following message OK in fact can write freely, the browser is not concerned about, mainly for the developers to see the debugging. Although the network protocol is finally the dialogue between the program and the program, but in the development process is a dialogue between people and programs, a design transparent network protocol can provide a lot of intuitive information to the developer, therefore, very many application layer network protocols, such as HTTP, FTP, SMTP, POP3 are text-based protocols for transparency (transparency).

The second line of the HTTP header indicates the type of file to be sent (called the MIME type), here is the text/html, the plain text file is Text/plain, the picture is Image/jpg, image/png, and so on.


Then send the contents of the file, after sending the active close connection, so that the browser will know that the file has been sent. This is more special: Usually the network communication is the client initiates the connection, initiates the request actively, shuts down the connection actively, the server only passively handles the various situations, The HTTP protocol specifies that the server actively shuts down the connection (some webserver can be configured as keep-alive, and we do not discuss such a situation).


Finally implemented server code such as the following

/* SERVER.C */#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string.h># Include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include "wrap.h" #define MAXLINE 80void loadconfig (int *port, char *path); int main (void) {struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int LISTENFD, Connfd;char Buf[maxline];char str[inet_addrstrlen];int i, n;int pd_index,ret;int port;char *path = NULL; Loadconfig (&port, path); listenfd = Socket (af_inet, sock_stream, 0); int opt=1; SetSockOpt (listenfd,sol_socket,so_reuseaddr,&opt,sizeof (opt)); Bzero (&servaddr, sizeof (SERVADDR));    servaddr.sin_family = Af_inet;servaddr.sin_addr.s_addr = htonl (inaddr_any); servaddr.sin_port = htons (port); Bind (LISTENFD, (struct sockaddr *) &servaddr, sizeof (SERVADDR)); Listen (LISTENFD);p rintf ("accepting connections ... \ n"), while (1) {Cliaddr_len = sizeof (CLIADDR); connfd = Accept ( LISTENFD, (struct sockaddr *) &cliaddr, &cliaddr_len); while (1) {n = Read (CONNFD, buf, MAXLINE), if (n = = 0) {printf ("The other side have been closed!\n"); printf ("received from%s at PORT%d\n", Inet_ntop (Af_inet, &cliaddr.sin_addr, str, sizeof (STR)), Ntohs (CLI   Addr.sin_port)); i=0;   Char title[15];    while (buf[i]!= ' \ R ') {Title[i]=buf[i];   i++;   }title[i]= ' + ';     if (0 = = (strcmp (title, "get/http/1.1"))) {bzero (buf,maxline);    strcpy (buf, "http/1.1 0k\r\n");    Write (Connfd,buf,strlen (BUF));    Bzero (Buf,maxline);    strcpy (buf, "conent_type:text/html\r\n");    Write (Connfd,buf,strlen (BUF));    Bzero (Buf,maxline);    Buf[0]= ' \ r ';    Buf[1]= ' \ n ';    Write (connfd,buf,2);    Pd_index=open ("/var/www/index.html", o_rdonly);    if (pd_index<0) {perr_exit ("Open File failed.\n");    } bzero (Buf,maxline);     while ((Ret=read (pd_index,buf,maxline)) >0) {write (Connfd,buf,ret);     printf ("Send index data...\n"); Bzero (Buf,maxline);}}} Close (CONNFD);}} void Loadconfig (int *port, char *path){FILE *FD;FD = fopen ("/etc/myhttpd.conf", "R"), if (FD < 0) perr_exit ("Can ' t load config.\n"), fscanf (FD, "port=%d\ ndirectory=%s ", port, Path);}


Finally execute the result




References

Linux C Programming One-stop learning-http://c4linux.letaoba.info/

Implement a simple webserver-http://www.linuxidc.com/linux/2013-10/91160.htm under Ubuntu


Linux network programming One-stop learning

Related Article

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.