Linux Socket Network Programming (IBM Web site)

the second part

Chapter One
client set up
the first few lines exactly the same UDP client and the corresponding row of TCP client. We mainly used several include statements contain socket function, or other basic I / O functions.


          #include <stdio.h>
          #include <sys/socket.h>
          #include <arpa/inet.h>
          #include <stdlib.h>
          #include <string.h>
          #include <unistd.h>
          #include <netinet/in.h>

          #define BUFFSIZE 255
          void Die(char *mess) { perror(mess); exit(1); }
         

There is not much left to set. It is worth noting that the buffer size is much larger buffer than we assign TCP version (in size but still limited). TCP can loop iterations pending data, and that data to be transmitted through a plurality of socket opening in each cycle. For this version of the UDP, we want a large enough to accommodate the entire message buffer, the entire message is sent in a single datagram (which may be less than 255 bytes, but not larger than 255 bytes). This version also defines a small error handler.

Declaration and use of information
at the beginning of the most main () function, we assign two sockaddr_in structure comprises an integer number for the string size, the other type of int socket handle for the variables, and for containing a return buffer string. After that, we check whether the command-line arguments appear to be correct.


          int main(int argc, char *argv[]) {
            int sock;
            struct sockaddr_in echoserver;
            struct sockaddr_in echoclient;
            char buffer[BUFFSIZE];
            unsigned int echolen, clientlen;
            int received = 0;

            if (argc != 4) {
              fprintf(stderr, "USAGE: %s <server_ip> <word> <port>\n", argv[0]);
              exit(1);
            }
         

Here there have been local contrast to the Python code. For C client, you must use a dotted-quad IP addresses. In Python, the names of all modules socket behind the analysis function processing. If you want to look at many types of C client, you need to write a DNS function - the function such as described in the first part of this tutorial.

In fact, check the IP address of the incoming server as an IP address really looks like a dotted quad, this is not an extreme idea. If you forget the name of the incoming address, you may receive the error message is a bit misleading: "Mismatch in number of sent bytes: No route to host (does not match the number of bytes sent, there is no route to host)." The address is actually the equivalent of naming any unused or reserved IP address (which of course can not be checked by a simple pattern to exclude).

Create and configure the server socket structure
socket () call parameter determines the type of socket: PF_INET just means that it uses IP (you can always use the IP); SOCK_DGRAM and IPPROTO_UDP meet up for UDP socket. When ready to echo (echo) a message, we use the command-line parameters to fill the expected server architecture.


            /* Create the UDP socket */
            if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
              Die("Failed to create socket");
            }
            /* Construct the server sockaddr_in structure */
            memset(&echoserver, 0, sizeof(echoserver));       /* Clear struct */
            echoserver.sin_family = AF_INET;                  /* Internet/IP */
            echoserver.sin_addr.s_addr = inet_addr(argv[1]);  /* IP address */
            echoserver.sin_port = htons(atoi(argv[3]));       /* server port */
          

socket () return value is called a socket handle it and the file handle similar; in particular, if socket creation fails, the call will return -1 instead of returning a positive handle. The inet_addr support function () and the htons () (and atoi ()) for converting a string to an appropriate data structure parameters.

Send a message to the server
to work, the first part of the client than the introduction of this tutorial series is similar to TCP echo client simpler. As we can see from the Python version, send a message not based on first establish a connection. You only need to use sendto () to send a message to the specified address instead send on an established connection (). Of course, this requires two additional parameters to specify the desired server address.


            /* Send the word to the server */
            echolen = strlen(argv[2]);
            if (sendto(sock, argv[2], echolen, 0,
                       (struct sockaddr *) &echoserver,
                       sizeof(echoserver)) != echolen) {
              Die("Mismatch in number of sent bytes");
            }
          

This call is usually in error checking to determine whether there is a path to the server. If you mistakenly used the name of the address, it throws an error message, but it looks valid but unreachable addresses will lead to error messages.

To recover messages from the server
to recover the data and works very similar to TCP echo client. The only real change is the call to recvfrom () is replaced on recv () call to the TCP.


            /* Receive the word back from the server */
            fprintf(stdout, "Received: ");
            clientlen = sizeof(echoclient);
            if ((received = recvfrom(sock, buffer, BUFFSIZE, 0,
                                     (struct sockaddr *) &echoclient,
                                     &clientlen)) != echolen) {
              Die("Mismatch in number of received bytes");
            }
            /* Check that client and server are using same socket */
            if (echoserver.sin_addr.s_addr != echoclient.sin_addr.s_addr) {
              Die("Received a packet from an unexpected server");
            }
            buffer[received] = '\0';        /* Assure null-terminated string */
            fprintf(stdout, buffer);
            fprintf(stdout, "\n");
            close(sock);
            exit(0);
          }
          

Echoserver structure has been used during a particular port of the sendto () calls to a good distribution; accordingly, echoclient structure via a call to recvfrom () has been filled similar. If a server or other port to send data packets while we wait for the acceptance of echo, which will allow us to compare the two addresses. We should guard against at least minimally unrelated packets we are not interested (in order to ensure completely sure, you can also check .sin_port members).

At the end of this process, we print starting back packets, and close the socket.


Chapter two

Server Settings
compared with TCP applications, UDP client and server are more similar to each other. In essence, each of which are mixed together mainly by a number of sendto () and recvfrom () call composition. The main difference is that it is usually the server but its body in an infinite loop in order to maintain the service.

Let us first examine the usual include statements and error handling function:


          #include <stdio.h>
          #include <sys/socket.h>
          #include <arpa/inet.h>
          #include <stdlib.h>
          #include <string.h>
          #include <unistd.h>
          #include <netinet/in.h>

          #define BUFFSIZE 255
          void Die(char *mess) { perror(mess); exit(1); }

Declarations and usage information
Similarly, the number of new content UDP echo server statement and the use of message does not. We need a socket for server and client, and some will be used to test a variable transfer size, of course, also need to read and write buffers message.


          int main(int argc, char *argv[]) {
            int sock;
            struct sockaddr_in echoserver;
            struct sockaddr_in echoclient;
            char buffer[BUFFSIZE];
            unsigned int echolen, clientlen, serverlen;
            int received = 0;

            if (argc != 2) {
              fprintf(stderr, "USAGE: %s <port>\n", argv[0]);
              exit(1);
            }

Create, configure and bind the server socket
first real difference between UDP client and server is that the server needs to bind socket. We've seen this in the Python example, the situation here is the same. The actual socket server socket is not transmitted through the message; instead, it acts as a special socket factory, this particular socket is soon we will see recvfrom () call configuration.


          /* Create the UDP socket */
          if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
            Die("Failed to create socket");
          }
          /* Construct the server sockaddr_in structure */
          memset(&echoserver, 0, sizeof(echoserver));       /* Clear struct */
          echoserver.sin_family = AF_INET;                  /* Internet/IP */
          echoserver.sin_addr.s_addr = htonl(INADDR_ANY);   /* Any IP address */
          echoserver.sin_port = htons(atoi(argv[1]));       /* server port */

          /* Bind the socket */
          serverlen = sizeof(echoserver);
          if (bind(sock, (struct sockaddr *) &echoserver, serverlen) < 0) {
            Die("Failed to bind server socket");
          }
         

Readers will also notice echoserver structure is configured in slightly different ways. To allow connections on any IP address of the server hosting, we use a special constant INADDR_ANY members .s_addr.

receive / send cycle
of major initiatives UDP server is its main loop - though much better than this. Basically, we are in a recvfrom () call waiting to receive a message permanently. At this time, echoclient structure will be populated with members associated socket connection. Then we use the structure in subsequent sendto () call.


            /* Run until cancelled */
            while (1) {
              /* Receive a message from the client */
              clientlen = sizeof(echoclient);
              if ((received = recvfrom(sock, buffer, BUFFSIZE, 0,
                                       (struct sockaddr *) &echoclient,
                                       &clientlen)) < 0) {
                Die("Failed to receive message");
              }
              fprintf(stderr,
                      "Client connected: %s\n", inet_ntoa(echoclient.sin_addr));
              /* Send the message back to client */
              if (sendto(sock, buffer, received, 0,
                         (struct sockaddr *) &echoclient,
                         sizeof(echoclient)) != received) {
                Die("Mismatch in number of echo'd bytes");
              }
            }
          }
         

We're done! We can continue to receive and send messages, and connections to the console reports in the process. Of course, as we shall see in the next section, this arrangement once only do one thing, this could be a problem (for this simple echo server is probably not a problem for servers handling many clients who, but more complex cases may introduce delays worse).

third chapter

The complexity of the server work
we have studied the server (in addition to echo the message, but do not do anything else) to handle each client request very quickly. But for the more general case, we may want the server to perform the operation may take longer, such as database lookups, access to remote resources, or perform complex calculations in order to determine the responsiveness of the client. Our "time to do one thing," the model can not scale well to handle multiple clients.

Server stress tests
in order to allow the server has some work to do, we can modify the client to make multiple requests (each thread to send a request), these requests need to be met as quickly as possible:

Server stress test, continue to
run the previous one server for this client will only run for a few seconds (but of course will not return the string converted to uppercase); editions no thread overhead for the previous server run even more fast. Assuming that this process is not purely hypothetical server CPU-bound, then I should be able to respond faster than the speed of 100+. Also note that those threads are not generally in the order they were created to be serviced.

Threaded server
we set the "Long operation" Server way to ensure that it takes at least five seconds to provide services to any given request. But there is no reason why not run multiple threads within that same five seconds. Similarly, the process of CPU-bound obviously not threaded through and run faster, but in the actual server that five seconds is mainly spent on another machine, such as a database query for such things. In other words, we should be able to multiple clients in parallel threads to provide services.

An obvious approach is to make the server threading, as the client thread of the same:

In my test system (as before using localhost), the client will be reduced so the running time to about 9 seconds - five seconds of which is spent on call sleep (), 4 seconds spent the rest of the thread and connection overhead (generally the case).

Branch server
on UNIX-like systems, branch even easier than threading. Thread process usually better than "heavy", but on such popular Posix systems such as Linux, FreeBSD and Darwin, process creation is still quite efficient.

Use Python, we have "long operation" server version can be as simple as:


          #!/usr/bin/env python
          from socket import *
          from sys import argv, exit
          from os import fork

          def lengthy_action(sock, message, client_addr):
              from time import sleep
              print "Client connected:", client_addr
              sleep(5)
              sock.sendto(message.upper(), client_addr)
              exit()

          sock = socket(AF_INET, SOCK_DGRAM)
          sock.bind(('',int(argv[1])))
          while 1:    # Run until cancelled
              message, client_addr = sock.recvfrom(256)
              if fork():
                  lengthy_action(sock, message, client_addr)
         

On my test system, I actually found this branch version than the threaded version of seconds faster. As a little difference in behavior, after providing services to a group of clients of threads, while the main process loop to the back-end, although the server is started at the front desk. However, the server usually start from the back, this distinction is not relevant.

Asynchronous server
to another is called asynchronous or non-blocking socket technology may even than threaded or branch method is more efficient. The concept behind asynchronous programming is to perform the holding in a single thread, but to poll each open socket, to determine if it has more data waiting to be read or write. However, non-blocking socket will only really be useful by the I / O bound processes. We use sleep () to create a simulation of the server CPU-bound to a certain extent, missed the point. In addition, non-blocking socket for TCP connections than UDP connections more meaningful, because the former may still have to keep a connection open pending data.

In summary, the structure of an asynchronous peer (client or server) is a polling cycle - typically using the function select () or one of its advanced packaging, such as a Python asyncore. Each time through the loop, you must check all open socket, to determine which current is readable, and which is currently writable. This check up quickly, and you can simply ignore any socket is currently no I / O operations are ready. This socket programming style avoids any overhead associated with threads or processes linked.

Having a slow client socket connected
to an analog low-bandwidth connection, we can create a client, it is artificial delay introduced when transmitting data, and issues a message byte by byte. In order to simulate many of these connections, we can create multiple connection threads (each is slow). Generally, this client and we see above DPechoclient2.py Similarly, only a TCP Version:


          #!/usr/bin/env python
          from socket import *
          import sys, time
          from thread import start_new_thread, get_ident

          threads = {}
          start = time.time()

          def request(n, mess):
              sock = socket(AF_INET, SOCK_STREAM)
              sock.connect((sys.argv[1], int(sys.argv[3])))
              messlen, received = len(mess), 0
              for c in mess:
                  sock.send(c)
                  time.sleep(.1)
              data = ""
              while received < messlen:
                  data += sock.recv(1)
                  time.sleep(.1)
                  received += 1
              sock.close()
              print "Received:", data
              del threads[get_ident()]

          for n in range(20):
              message = "%s [%d]" % (sys.argv[2], n)
              id = start_new_thread(request, (n, message))
              threads[id] = None
          while threads:
              time.sleep(.2)
          print "%.2f seconds" % (time.time()-start)
         

The client has a slow connection socket, continue
we need a "traditional" server to test above the slow client. Essentially, in the first portion following this code tutorial second (lower) identical to the Python server. The only real difference is that the increase in the maximum number of connections to 20.


          #!/usr/bin/env python
          from socket import *
          import sys

          def handleClient(sock):
              data = sock.recv(32)
              while data:
                  sock.sendall(data)
                  data = sock.recv(32)
              newsock.close()

          if __name__=='__main__':
              sock = socket(AF_INET, SOCK_STREAM)
              sock.bind(('',int(sys.argv[1])))
              sock.listen(20)
              while 1:    # Run until cancelled
                  newsock, client_addr = sock.accept()
                  print "Client connected:", client_addr
                  handleClient(newsock)

The client has a slow connection socket, summed
let's run the "slow connection" for the "time to do one thing," the server client (As before, the output is cut):


          $ ./echoclient2.py localhost "Hello world" 7
          Received: Hello world [0]
          Received: Hello world [1]
          Received: Hello world [5]
          ...
          Received: Hello world [16]
          37.07 seconds
         

UDP stress test with the same client, the thread is not necessarily in the order they were initiated connections. However, the most noteworthy is that it takes time service for 20 threads basically introduced at the byte write one by one through the socket of the sum of all delays. Nothing here is parallelized, because we need to wait for each individual socket connection to complete its task.

Use select () to multiplex socket
now we're ready to see the functions select () can be used to how to avoid the kind we have just introduced a delay (or due to really slow connection frequently generated the kind of delay) . We have discussed the general concept before subsections; Let us examine a detailed code:


          #!/usr/bin/env python
          from socket import *
          import sys, time
          from select import select

          if __name__=='__main__':
              while 1:
                  sock = socket(AF_INET, SOCK_STREAM)
                  sock.bind(('',int(sys.argv[1])))
                  print "Ready..."
                  data = {}
                  sock.listen(20)
                  for _ in range(20):
                      newsock, client_addr = sock.accept()
                      print "Client connected:", client_addr
                      data[newsock] = ""
                  last_activity = time.time()
                  while 1:
                      read, write, err = select(data.keys(), data.keys(), [])
                      if time.time() - last_activity > 5:
                          for s in read: s.shutdown(2)
                          break
                      for s in read:
                          data[s] = s.recv(32)
                      for s in write:
                          if data[s]:
                              last_activity = time.time()
                              s.send(data[s])
                              data[s] = ""
         

This server is fragile, because it is always before connecting select from among those clients (), waiting for the exact 20 client connections. But it still illustrates the use of intensive training in rotation cycle, and when the data is available to read on a particular socket / write data only if the basic concepts. Triplet of select () returns the values ​​are readable, writable, and errors of the socket list. This type of each of which are required in the process cycle.

Incidentally, the use of such asynchronous server allows "slow connection" client completes all 20 connected in about six seconds, rather than requiring 37 seconds (at least on a test system I so).

Written in C scalable server
example provides a more scalable servers all use of Python. Indeed, the quality of Python libraries mean not significantly slower than a similar server written in C server exists. For this tutorial, relatively simple statement is very important.

In presenting the above server, I stick with the Python relatively low-level functions. SocketServer such as asyncore or some advanced modules - or even threading instead of thread - are likely to provide more technical "nature with Python" is. However, these low-level functions I use on the same structure is still fairly close to what you want to write in C. Python's dynamic typing and concise syntax still save a few lines of code, but C programmers should be able to use my example as the basic framework similar to the C server.

Reproduced in: https: //www.cnblogs.com/shelvenn/archive/2007/11/22/969227.html

Guess you like

Origin blog.csdn.net/weixin_34095889/article/details/93272915