Network Programming in Embedded linux (6) -- multi-thread File Server

Source: Internet
Author: User

This article describes an example of a multi-threaded file server. The server sends the required files to the client based on the client's request. multithreading is used to provide services for multiple clients at the same time. such requirements exist in many practical applications.

The entire server program consists of four files: fileserver. c, fileclient. c, rw. h and rw. c. fileserver. c is the main program of the multi-thread server, fileclient. c is the client program used for testing, rw. h and rw. c mainly providesSend and receive large data volumesFunction.

In previous articles, we have discussed the problem of multi-threaded program design, and compared with multi-process programs, it is concluded that in the embedded system, multi-thread processing should be prioritized. although it is most common to create a sub-process to implement server services on multiple clients at the same time, we still discuss the implementation of multithreading considering the reality of the embedded system. in fact, the basic principle is the same, that is, to create a processing subroutine for each connection with the client.

The code of the server master program is listed below.

/*************************************** **************************************** * ****** // * File: fileserver. c * // * Description: The main program of the multi-threaded file server. *//************************************* **************************************** * *******/# Include <stdio. h> # include <stdlib. h> # include <errno. h> # include <string. h> # include <sys/types. h> # include <sys/socket. h> # include <netinet/in. h> # include <arpa/inet. h> # include <pthread. h> # include "rw. h "# define BACKLOG 10/* Working thread function */void * deal_request (void * arg) {int connfd = * (int *) arg); char file_name [MAX_LEN]; /* In the separation mode, the resources are cleared and exited */pthread_detach (pthread_self () after the thread completes work;/* process client requests */if (read_cmd (connfd, file_name, sizeof (file_name )) =-1) {printf ("read file name error! \ N ");} else {/* file data for sending the request */if (send_file (connfd, file_name) =-1) {printf ("Send file error \ n") ;}/ * Corresponds to the malloc () function */free (arg); close (connfd) in the main program ); pthread_exit (NULL);} int main (int argc, char * argv []) {int sockfd; int * connfd; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; struct sockaddr_in tempaddr; socklen_t templen; socklen_t clilen;/* Create a TCP socket */if (sockfd = socket (AF_INET, SOCK_STREAM, 0) =-1) {perror ("Socket"); exit (1);} bzero (& servaddr, sizeof (servaddr); servaddr. sin_family = AF_INET; servaddr. sin_addr.s_addr = htonl (INADDR_ANY); servaddr. sin_port = 0;/* random port number * // * bind TCP socket */if (bind (sockfd, (struct sockaddr *) & servaddr, sizeof (servaddr )) =-1) {perror ("bind"); exit (1);} templen = sizeof (struct sockaddr ); /* obtain the local address of the socket */if (getsockname (sockfd, (struct sockaddr *) & tempaddr, & templen) =-1) {perror ("getsoc Kname "); exit (1);} printf (" Server is listening on port % d \ n ", ntohs (tempaddr. sin_port);/* Start listening port */if (listen (sockfd, BACKLOG) =-1) {perror ("listen"); exit (1 );} for (;) {pthread_t th; clilen = sizeof (cliaddr);/* Communication socket, must be dynamically allocated to distinguish different threads */connfd = (int *) malloc (sizeof (int); * connfd = accept (sockfd, (struct sockaddr *) & cliaddr, & clilen); if (* connfd =-1) {perror ("accept"); continue;}/* creates a thread for each connection to the client to process its request */if (Pthread_create (& th, NULL, deal_request, connfd )! = 0) {perror ("pthread_create"); break ;}} return 0 ;}
Next, we will analyze this program in detail.

To meet certain requirements, the server's listening port needs to be dynamically allocated. The getsockname () function can implement this function. The getsockname () function is used to obtain the local address of the specified socket. Its prototype is:

#include <sys/socket.h>int getsockname (int sockfd, struct sockaddr *addr, int *addrlen);
The parameter meanings are as follows:

On a TCP server bound with an INADDR_ANY IP address, you can call this function to obtain local IP address, port, and other address information. in the above program, because the listening port of the server is random (0), in order to let the client know the port number of the server, use this function to obtain the actually allocated port number, in this way, the listener port can be dynamically allocated.

Now let's analyze this code in detail. in fact, most of the Code is similar to the example in the previous blog, and The for loop code is quite different. therefore, we mainly analyze the content of for loop and thread processing functions. to facilitate the description, we will list the for loop separately and mark the row number:

In this for loop, the task to be completed is to establish a connection socket descriptor, create a new data processing subthread, and pass this descriptor as a parameter to the data processing subthread. this process seems normal, but there is a place that is easy to make mistakes. Please refer to the comments of lines 93 and 103, which involves threads.Access to contributed data.

We are familiar with the accept function. The connfd parameter is the function parameter passed to the data processing thread. The connfd variable stores the connection socket descriptor. if connfd is not dynamically allocated, but a variable defined in the main function, the connfd parameter of each sub-thread points to the same memory address. Due to the multi-threaded environment, all threads are in the same memory address space as the process that created them. If the previous thread has not completed data transmission, a new connection is established, in this case, the connfd value will be changed to the new socket descriptor of the connection. The socket that has not been processed disappears, and the data transmission will inevitably fail.

Therefore, you can create a memory for each connected socket descriptor to store its content. You can avoid the loss of the socket descriptor by releasing the memory after data is processed.

To facilitate thread exit, we set the thread separation attribute: When the thread stops running, it automatically clears the occupied resources and exits. the specific implementation can look at the source code provided above.

The large data volume refers to the data volume that can be carried by a read () or write () operation. we know that both the read () function and the write () function have a feature. If the specified data volume is large, it is often impossible to complete all data I/O work through a read/write operation, these read/write operations are usually performed based on the actual data volume they return. in rw. the c file provides the readall () and writeall () functions to solve this problem. the Code is as follows:

/*************************************** **************************************** * ****** // * File: rw. h * // * Description: header file for custom I/O functions. *//************************************* **************************************** * *******/# Ifndef _ RW_H # define _ RW_H/* size of the read/write buffer */# define MAX_LEN 1024*10/* 10KB */ssize_t readall (int fd, void * buf, size_t * len); ssize_t writeall (int fd, void * buf, size_t * len); int read_cmd (int sockfd, char * cmd_buf, int len ); int send_file (int sockfd, char * file_name); # endif
The following is the implementation of these functions:

/*************************************** **************************************** * ****** // * File: rw. c * // * Description: UDF implementation file. *//************************************* **************************************** * *******/# Include <sys/types. h> # include <stdio. h> # include <unistd. h> # include <sys/stat. h> # include <fcntl. h> # include <string. h> # include "rw. h "/* read () function extension to solve the problem of incomplete Reading */ssize_t readall (int fd, void * buf, size_t * len) {size_t nleft; ssize_t nread; ssize_t total; char * ptr; ptr = buf; nleft = * len; total = 0;/* read repeatedly until no new data is readable */while (Nleft> 0) {if (nread = read (fd, ptr, * len) =-1) {perror ("readall"); break ;} if (nread = 0) break; nleft-= nread; ptr + = nread; total + = nread; * len = nleft;} * len = total; return (nread =-1 )? -1:0;}/* write () function extension to solve the write insufficiency problem */ssize_t writeall (int fd, void * buf, size_t * len) {size_t nleft; ssize_t nwrite; ssize_t total; const char * ptr; ptr = buf; nleft = * len; total = 0;/* write repeatedly, until all data is written */while (nleft> 0) {if (nwrite = write (fd, ptr, * len) =-1) {perror ("write all"); break;} nleft-= nwrite; ptr + = nwrite; total + = nwrite; * len = nleft;} * len = total; return (nwrite =-1 )? -1:0;}/* analyze the client command to extract the requested file name */int read_cmd (int sockfd, char * cmd_buf, int len) {char line [MAX_LEN]; int my_len = 0; int total_len = 0; char * ptr; int can_read; if (len> MAX_LEN) len = MAX_LEN; can_read = 1; strcpy (pai_buf, "\ 0"); while (can_read) {if (my_len = read (sockfd, line, len) <0) {perror ("read "); return-1;} total_len + = my_len; if (total_len> len) {printf ("Recieve command error! \ N "); return-1;} if (ptr = strstr (line," \ r \ n ") = NULL) {if (total_len <= len) strcat (cmd_buf, line);} else {strncat (cmd_buf, line, ptr-line); can_read = 0;} printf ("Client requests file: % s \ n ", pai_buf);} return 0;}/* send the file to the client */int send_file (int sockfd, char * file_name) {int file_fd; int file_size; int read_left; int len; int error_flag; int readlen; struct stat file_state; char buffer [MAX_LEN]; int dot_number = 0; if (fi Le_fd = open (file_name, O_RDONLY) =-1) {perror ("open"); return-1 ;}if (fstat (file_fd, & file_state) =-1) {perror ("fstat"); return-1;} file_size = file_state.st_size; read_left = file_size; len = MAX_LEN; while (read_left> 0) {/* now read the file */readlen = MAX_LEN; error_flag = readall (file_fd, buffer, & readlen); if (error_flag <0) {return-1 ;} read_left-= readlen; len = readlen; error_flag = writeall (sockfd, bu Ffer, & len); if (error_flag =-1) return-1; if (readlen = 0 & read_left! = 0) {printf ("the file is not read fully! \ N "); return-1;} if (read_left = 0) {printf (" \ nServer sent file over! \ N ") ;}} close (file_fd); return 0 ;}
In order to solve the problem that the read and write Functions cannot read or write the required data at one time, the read_all and write_all functions adopt the repeated read or write methods. Based on the return values of the read or write functions, determine the amount of data to be read or written, calculate the offset, and continue the previous read or write operation.

Read_cmd and send_file functions are the main functions of the file server to implement its service functions. These functions are commonly used and are suitable for many applications. You can analyze them carefully.

With the above three files (http://download.csdn.net/detail/ce123/6764657), you can construct this multi-threaded file server. at the same time, in order to test the functions of this server, you also need to write a suitable client program, so that the following client program for the test is available. this Client program communicates with the server through the TCP protocol. Since the TCP Client has been analyzed in detail, only the code of this test program is listed here. Let's analyze it by yourself.

/* File: fileclient. c * // * Description: client test program. *//************************************* **************************************** * *******/# Include <stdio. h> # include <stdlib. h> # include <string. h> # include <sys/types. h> # include <sys/socket. h> # include <netinet/in. h> # include <unistd. h> # include <sys/stat. h> # include <fcntl. h> # include "rw. h "/* line feed count */# define DOT_PERIOD 50int main (int argc, char * argv []) {int sockfd; int conn_ret; struct sockaddr_in servaddr; Char performance_buf [MAX_LEN]; char recvbuf [MAX_LEN]; int error_flag; int len = MAX_LEN; int file_fd; int dot_number; int total_len = 0; /* parameter check */if (argc! = 5) {printf ("Usage: fileclient <address> <port> <src file> <des file> \ n"); return 0 ;} /* Create a socket */if (sockfd = socket (AF_INET, SOCK_STREAM, 0) =-1) {perror ("sock"); exit (1 );} /* connect to the server */bzero (& servaddr, sizeof (servaddr); servaddr. sin_family = AF_INET; servaddr. sin_port = htons (atoi (argv [2]); inet_ton (AF_INET, argv [1], & servaddr. sin_addr); conn_ret = connect (sockfd, (struct sockaddr *) & servaddr, sizeof (servaddr ); If (conn_ret =-1) {perror ("connect");}/* Create the received file */file_fd = open (argv [4], o_CREAT | O_WRONLY); if (file_fd =-1) {perror ("open"); exit (1) ;}/ * construct the client request message, the format is "file name \ r \ n" */len = strlen (argv [3]) + 3; snprintf (cmd_buf, len, "% s \ r \ n ", argv [3]);/* Send request */if (error_flag = writeall (sockfd, cmd_buf, & len) =-1) {exit (1 );} /* the buffer size determines the data volume of each I/O operation */len = MAX_LEN; printf ("\ nfile is transferring: \ n "); while (error_flag = readal L (sockfd, recvbuf, & len) = 0) {if (len = 0) {printf ("\ nClient has stored ed file! \ N "); break;}/* display the data size received this time */printf (". "); printf (" read length is % d \ n ", len); dot_number ++; if (dot_number % DOT_PERIOD) = 0) {printf ("\ n"); dot_number = 0;} total_len + = len;/* write the received data to the file */if (writeall (file_fd, recvbuf, & len) =-1) {printf ("\ nclient has some error when receive the file! \ N "); break;} len = MAX_LEN;} printf (" \ nRecevied % d bytes \ n ", total_len); close (file_fd); close (sockfd ); return 0 ;}

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.