"Socket Programming" chapter Three

Source: Internet
Author: User
Tags htons

Below we implement the next echo client.

The so-called "echo", refers to the client sends a data to the server, the server then returns the data to the client as it is .

There is not much change in the code relative to article two and chapter one. As shown below:

Server-side:

#include <cstdio> #include <unistd.h> #include <stdlib.h> #include <cstring> #include < cassert> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa /inet.h>const int buffer_size = 4096;const int server_port = 2222;int Main () {int Server_socket;char buff[buffer_size]; int n;server_socket = socket (af_inet, sock_stream, 0); assert (Server_socket! =-1); struct sockaddr_in Server_addr;memset (&server_addr, 0, sizeof (SERVER_ADDR)); server_addr.sin_family = Af_inet;server_addr.sin_port = Htons (SERVER_PORT ); server_addr.sin_addr.s_addr = htonl (Inaddr_any); Assert (Bind (server_socket, (struct sockaddr *) &server_addr, sizeof (SERVER_ADDR))! =-1); Assert (Listen (Server_socket, 5)! = 1); struct sockaddr_in client_addr;socklen_t Client_ Addr_len = sizeof (CLIENT_ADDR), while (1) {printf ("waiting...\n"); int connfd = accept (server_socket, struct sockaddr*) &AMP;CLIENT_ADDR, &client_addr_len); if (CONNFD = =-1) continue;printf ("CoNnect from%s:%d\n ", Inet_ntoa (CLIENT_ADDR.SIN_ADDR), Ntohs (Client_addr.sin_port)); n = recv (CONNFD, Buff, buffer_size, 0); Send (CONNFD, buff, n, 0); close (CONNFD);} Close (server_socket); return 0;}



Client:

#include <cstdio> #include <unistd.h> #include <stdlib.h> #include <cstring> #include < cassert> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa /inet.h>const int buffer_size = 4096;const int server_port = 2222;int Main () {int client_socket;const char *server_ip = "127.0.0.1"; char Buffsend[buffer_size];char buffrecv[buffer_size];int n;fgets (buffsend, Buffer_size, stdin); Client_ Socket = socket (af_inet, sock_stream, 0), assert (Client_socket! =-1), struct sockaddr_in server_addr;memset (&server _ADDR, 0, sizeof (SERVER_ADDR)), server_addr.sin_family = Af_inet;server_addr.sin_port = Htons (server_port); Server_ ADDR.SIN_ADDR.S_ADDR = inet_addr (server_ip); Assert (Connect (client_socket, struct sockaddr *) &server_addr, sizeof (SERVER_ADDR))! =-1); Assert (Send (Client_socket, Buffsend, strlen (Buffsend), 0)! =-1); n = recv (Client_socket, BUFFRECV, buffer_size, 0); Buffrecv[n] = ';p rintf ("Echo:%s\n", BUFFRECV); ClosE (client_socket); return 0;} 




Socket buffer


After each socket is created, it allocates two buffers, input buffers, and an output buffer.

The socket's write function does not immediately transmit data to the network, but instead writes the data to the buffer and sends the data from the buffer to the target machine by the TCP/UDP protocol. Once the data is written to the buffer, the function can return successfully, regardless of whether they arrive at the target machine or when they are sent to the network, which is the responsibility of the TCP/UDP protocol.

The TCP/UDP protocol is independent of the socket read-write function, the data may just be written to the buffer is sent to the network, it may be in the buffer backlog, the data is sent once to the network, depending on the network situation, the current thread is idle and many other factors, not controlled by the programmer.



I/O buffers for sockets


The I/O buffer features are as follows:
1) I/O buffers exist separately in each Socket;
2) I/O buffers are generated automatically when the Socket is created;
3) The data remaining in the output buffer will continue to be transmitted even if the Socket is closed;
4) Closing the Socket will lose the data in the input buffer.




Blocking mode


For TCP sockets (by default) when sending data using write ()/send ():
1) The buffer is checked first, and if the free space of the buffer is less than the data to be sent, write ()/send () is blocked (paused) until the data in the buffer is sent to the target machine, freeing up enough space to wake the Write ()/send () function to continue writing the data ;

2) If the TCP protocol is sending data to the network, then the output buffer will be locked, not allowed to write, write ()/send () will also be blocked until the data is sent to the buffer unlocked, write ()/send () will be awakened;

3) If the data to be written is greater than the maximum length of the buffer, it will be written in batches;


4) until all data is written to buffer write ()/send () to return.


When reading data using read ()/recv ():
1) First check the buffer, if there is data in the buffer, then read, otherwise the function will be blocked until there is data on the network arrival;


2) If the length of data that can be read is less than the length of the data in the buffer, then all the data in the buffer cannot be read out at once, and the remaining data will continue to accumulate until the read ()/RECV () function reads again;

3) the Read ()/recv () function will not return until the data is read, otherwise it will always be blocked;

This is the blocking mode for TCP sockets. The so-called blocking, is that the previous action is not completed, the next action will be paused until the previous action is completed before continuing to maintain synchronization.


The above description:

The receipt and sending of data is irrelevant, and the Read ()/RECV () function receives as much data as possible regardless of how many times the data is sent. In other words, read ()/recv () and write ()/send () may have different executions.

For example, write ()/send () executes three times, each time the string "ABC" is sent, then read ()/recv () on the target machine may receive three times, receive "ABC" each time, or it may receive two times, receive "Abcab" for the first time, receive "The second time" cabc The string "Abcabcabc" may also be received at one time.


Suppose we want the client to send a student's school number each time, let the server side return the student's name, address, scores and other information, this may be problematic, the server can not distinguish between the student's number. For example, when sending 1 for the first time, sending 3 for the second time, the server may be treated as 13来, and the returned information is obviously wrong.


This is the TCP protocol data "sticky packet" problem, the client sends a plurality of packets to be received as a packet, also known as the non-boundary of the data, the Read ()/recv () function does not know the beginning or end of the packet (in fact, there is no start or end flag), Just treat them as a continuous stream of data.


The following code demonstrates the problem of sticky packets, the client sends data to the server three times in a row, and the server receives all the data at once:

Server-side:

#include <cstdio> #include <unistd.h> #include <stdlib.h> #include <cstring> #include < cassert> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa /inet.h>const int buffer_size = 4096;const int server_port = 2222;int Main () {int Server_socket;char buff[buffer_size]; int n;server_socket = socket (af_inet, sock_stream, 0); assert (Server_socket! =-1); struct sockaddr_in Server_addr;memset (&server_addr, 0, sizeof (SERVER_ADDR)); server_addr.sin_family = Af_inet;server_addr.sin_port = Htons (SERVER_PORT ); server_addr.sin_addr.s_addr = htonl (Inaddr_any); Assert (Bind (server_socket, (struct sockaddr *) &server_addr, sizeof (SERVER_ADDR))! =-1); Assert (Listen (Server_socket, 5)! = 1); struct sockaddr_in client_addr;socklen_t Client_ Addr_len = sizeof (CLIENT_ADDR), while (1) {printf ("waiting...\n"); int connfd = accept (server_socket, struct sockaddr*) &AMP;CLIENT_ADDR, &client_addr_len); if (CONNFD = =-1) continue;printf ("CoNnect from%s:%d\n ", Inet_ntoa (CLIENT_ADDR.SIN_ADDR), Ntohs (Client_addr.sin_port)); sleep (5); Waiting for 5s to receive data, in fact, is to ensure that the data sent by the client has reached the server-side input buffer n = recv (CONNFD, Buff, buffer_size, 0); Send (CONNFD, buff, n, 0); close (CONNFD);} Close (server_socket); return 0;}

Client:

#include <cstdio> #include <unistd.h> #include <stdlib.h> #include <cstring> #include < cassert> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa /inet.h>const int buffer_size = 4096;const int server_port = 2222;int Main () {int client_socket;const char *server_ip = "127.0.0.1"; char Buffsend[buffer_size];char buffrecv[buffer_size];int n;fgets (buffsend, Buffer_size, stdin); Client_ Socket = socket (af_inet, sock_stream, 0), assert (Client_socket! =-1), struct sockaddr_in server_addr;memset (&server _ADDR, 0, sizeof (SERVER_ADDR)), server_addr.sin_family = Af_inet;server_addr.sin_port = Htons (server_port); Server_ ADDR.SIN_ADDR.S_ADDR = inet_addr (server_ip); Assert (Connect (client_socket, struct sockaddr *) &server_addr, sizeof (SERVER_ADDR))! =-1); for (int i = 0; i < 3; ++i)//Repeat 3 times ERT (Send (Client_socket, Buffsend, strlen (Buffsend), 0)! =-1); n = recv (ClieNt_socket, Buffrecv, buffer_size, 0); Buffrecv[n] = ';p rintf ' ("Echo:%s\n", BUFFRECV); close (client_socket); return 0;} 

In fact, the above code can be modified slightly.




Graceful disconnection--shutdown ()


Calling the close () function means that a complete disconnection, that is, the inability to send data and the inability to receive data, can sometimes seem less "elegant" than the "blunt" approach.


Close () disconnect


After host a sends the data, the unilateral call to close () disconnects, and then neither host a nor B can accept the data transmitted by the other party. In fact, it is completely impossible to invoke functions related to data sending and receiving.

In general, this will not be a problem, but there are special moments when you need to disconnect only one data transmission channel while retaining the other.

This can be achieved using the shutdown () function, which is prototyped as:

int shutdown (int sock, int howto);
Howto has the following values under Linux:
1) SHUT_RD: disconnects the input stream. The socket cannot receive data (even if the input buffer receives the data is erased), the input correlation function cannot be called.
2) SHUT_WR: Disconnect the output stream. The socket cannot send data, but if there is still data in the output buffer that is not transferred, it is passed to the destination host.
3) Shut_rdwr: Disconnect the I/O stream at the same time. The equivalent of two calls to shutdown (), one for the SHUT_RD parameter, and another with SHUT_WR as the parameter.


The difference between close () and shutdown ():

To be exact, close () closes the socket, clears the socket descriptor from memory, and can no longer use the socket, similar to fclose () in C. After the application closes the socket, the connection and cache associated with the socket also loses meaning, and the TCP protocol automatically triggers the operation to close the connection.

Shutdown () is used to close the connection, not the socket, regardless of how many times shutdown () is called, and the socket persists until close () is called to clear the socket from memory. When close () is called, or when the output stream is called shutdown (), the FIN packet is sent to the other side. Fin package means that the data transfer is complete, the computer receives the FIN packet will know that no longer have to transmit.

By default, close () sends a FIN packet to the network immediately, regardless of whether there is data in the output buffer, and shutdown () waits for the data in the output buffer to complete before sending the fin packet. It also means that calling close () loses the data in the output buffer, and calling shutdown () does not.




Obtaining an IP address from a domain name


The direct use of IP addresses in the client can be a big disadvantage, and the client software will get an error once the IP address changes (the IP address will change constantly). And the use of domain names will be more convenient, after the registered domain name as long as the annual renewal will always belong to their own, change the IP address when the domain name resolution can be changed, will not affect the normal use of the software.

Domain name is just a mnemonic of IP address, the purpose is to facilitate the memory, through the domain name can not find the target computer, communication must be converted to IP address before the domain.

The gethostbyname () function can accomplish this transformation, and its prototype is:

struct hostent *gethostbyname (const char *hostname);

Hostname is the hostname, that is, the domain name. When you use this function, the IP address of the domain name is returned whenever the domain name string is passed. The address information returned is loaded into the hostent struct, which is defined as follows:

struct hostent{    char *h_name;  Official name    char **h_aliases;  Alias list    int  h_addrtype;  Host address type    int  h_length;  Address lenght    char **h_addr_list;  Address List}

As you can see from this struct, not only the IP address, but also other information, we only need to focus on the last member H_addr_list. The following is a description of each member:
1) H_name: Official domain name (official domain name). The official domain name represents a home page, but in fact some reputable companies ' domain names are not registered with the official domain name.
2) H_aliases: aliases, you can access the same host through multiple domain names. The same IP address can bind multiple domain names, so you can specify a different domain name in addition to the current domain name.
3) H_addrtype:gethostbyname () not only supports IPV4, but also supports IPV6, which can obtain the address family (address type) information of the IP address by this member, IPV4 corresponds to Af_inet,ipv6.
4) H_length: Save IP address length. The length of the IPv4 is 4 bytes, and the length of the IPv6 is 16 bytes.
5) H_addr_list: This is the most important member. The IP address of the domain name is saved by this member as an integer. For servers with more users, multiple IP addresses may be assigned to the same domain name, and multiple servers can be used to balance the load.


The composition of the hostent structure variable is as follows:



Here's a simple way to use the function:

#include <iostream> #include <netdb.h> #include <sys/socket.h> #include <netinet/in.h># Include <arpa/inet.h>using namespace Std;int main () {char hostname[100];cin >> hostname;struct hostent *host = gethostbyname (hostname); Char **pc;if (host) {//official Nameif (host, h_name) cout << "official name:" < < ' \ t ' << host-h_name << endl;//alias listcout << "Alias list:" << endl;pc = host-H _aliases;while (*pc! = NULL) {cout << ' \ t ' << *pc++ << Endl;} Host address type and address Lengthif (host-H_addrtype = = af_inet) cout << "host address type:ipv4\t" <&lt ; Host, H_length << "-byte" << endl;else if (host, H_addrtype = = Af_inet6) cout << "host address T Ype:ipv6\t "<< host h_length <<"-byte "<< endl;//address listpc = host, H_addr_list;cout &L t;< "Address list:" << endl;while (*pc! = NULL) {cout << ' \ t ' << ineT_ntoa (* (struct in_addr*) pc++) << Endl;}} Elsecout << "ERROR" << Endl;}


"Socket Programming" chapter Three

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.