当前位置:网站首页>C language simple webserver

C language simple webserver

2022-06-11 14:41:00 qq_ forty-two million one hundred and twenty thousand eight hun

Simple use C Language version implemented webserver

Abstract

This project refers to dark horse Linux Network programming teaching C Language version webserver The server . This project is BS Pattern , It realizes the request for a resource on the server sent by the browser (http message ), Server side ( That's what we achieved webserver) Return the response message to the browser and the requested resource ( Ordinary files or directory files ).

Technical points

  1. multiple IO Multiplexing technology epoll
  2. http Request and answer protocol
  3. TCP agreement : Three handshakes, four waves , After the connection is established, carry out data transmission
  4. web The server : Parse the request data sent by the browser , Get the request file name
  5. Directory access function
  6. The problem of accessing the Chinese Directory

BS Mode diagram ( function )

 Insert picture description here

The execution flow chart of the program

 Insert picture description here

Concrete realization

establish socket And bind the port number

Here we will create a listener file descriptor lfd And binding port number and ip The operation of is encapsulated in a function

int tcp4bind(short port,const char *IP)
{
    
    struct sockaddr_in serv_addr;
    int lfd = Socket(AF_INET,SOCK_STREAM,0); // Create listening file descriptor 
    bzero(&serv_addr,sizeof(serv_addr));// Empty serv_addr Address   contrast  memset()
    if(IP == NULL){
    
        // If used in this way  0.0.0.0, arbitrarily ip Will be able to connect 
        serv_addr.sin_addr.s_addr = INADDR_ANY;
    }else{
    
        if(inet_pton(AF_INET,IP,&serv_addr.sin_addr.s_addr) <= 0){
    
            perror(IP);// switch views 
            exit(1);
        }
    }
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port   = htons(port);// Host byte order to network byte order ( big-endian )
    int opt = 1;
 	// Set up port multiplexing 
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    // binding 
    Bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    return lfd;
}

Call this function , The port number is set to 9999,IP by NULL, Then use INADDR_ANY, Server listening 0.0.0.0 establish socket, No matter use 127.0.0.1 Or native ip Can be established tcp Connect

 int lfd = tcp4bind(9999, NULL);

Set listening

We will also listen Encapsulate as a function , Internal error judgment

// Set listening , The listening queue length is 128
Listen(lfd, 128); // At the end of the article wrap.c Give in 

establish epoll Trees

int epfd = epoll_create(1024);
if(epfd < 0)
{
    
    perror("epoll create error");
    close(lfd);
    return  -1;
}

Will listen for file descriptors lfd On epoll Trees

struct epoll_event ev;
ev.data.fd = lfd;
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);

Call in loop epoll_wait Loop waiting for the event to occur

Something happened , The kernel writes data to epoll_event Array , And the corresponding processing is performed according to the corresponding descriptor type :

int i = 0;
int nready;
int cfd;
int sockfd;
struct epoll_event events[1024]; 
while(1)
    {
    
        // Waiting for events to happen , Parameters -1 It means blocking 
        nready = epoll_wait(epfd, events, 1024, -1);
        if(nready < 0)
        {
    
            // If it is an interrupt signal , Don't think it's wrong 
            if (errno == EINTR)
            {
    
                continue;
            }
            break;
        }
        for(i = 0;i < nready; ++i)
        {
    
            sockfd = events[i].data.fd;
            // There are client connection requests 
            if(sockfd == lfd)
            {
    
                // Accept new client connections 
                cfd = Accept(lfd, NULL, NULL);

                // Set up cfd Non blocking 
                int flag = fcntl(cfd, F_GETFL);
                flag |= O_NONBLOCK;
                fcntl(cfd, F_SETFL, flag);

                // The newly acquired cfd Up a tree 
                ev.data.fd = cfd;
                ev.events = EPOLLIN;
                epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);

            }
            else // There is client data sent 
            {
    
                http_request(sockfd, epfd); // Parse request message 
            }
        }
    }

Processing requests from browsers http message (httprequest function )

Pre knowledge :http message
  • http Request message message

 Insert picture description here

  • http Response message

 Insert picture description here

Thought analysis

Processing the request message sent by the browser can be divided into the following steps :

  1. Read request line data , Resolve the requested resource file name
  2. Cycle through the remaining data
  3. Judge whether the file exists
    1. non-existent : return http The response message + Error page content
    2. There is :
      1. Ordinary documents
      2. Directory file
Read the request data and parse
int n;
char buf[1024];
// Read request line , Cut out the requested file name 
memset(buf, 0x00, sizeof(buf));
// Returns the number of bytes read 
n = Readline(cfd, buf, sizeof(buf));
if(n <= 0) // Return in non blocking mode -1 I have read nothing .
{
    
    //printf("read error or client closed, n = [%d]\n", n);
    // Read exception or client disconnection 
    close(cfd);
    epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
    return -1;
}
//GET /hanzi.c HTTP/1.1
printf("buf = [%s]\n", buf);
char reqType[16] = {
    0};
char fileName[255] = {
    0};
char protocol[16] = {
    0};

// Resolve the request type , File name and Protocol 
//[^ ] Indicates that the match is any string that is not a space 
sscanf(buf, "%[^ ] %[^ ] %[^ \r\n]", reqType, fileName, protocol)

char *pFile = fileName; //  You have to assign a value first , Prevent the back strcpy Operation field pointer 
// Server input :http://192.168.200.150:9000 perhaps http://192.168.200.150:9000/
// The act of getting a request  GET / HTTP/1.1
// You should be able to identify the root directory of the current resource 
if(strlen(fileName) <= 1)
{
    
    strcpy(pFile, "./");
}
else
{
    
    // here FileName Intercept as /hanzi.c, To get rid of /
    pFile = fileName + 1;
}

// Convert Chinese character code ( The name is %xx%xx Strings like that )
strdecode(pFile, pFile);
Cycle through the remaining data

The purpose is to Prevent sticking Affect the data interpretation of the request sent by the next client , Due to the internal call read, It's blocking , So it's important take cfd Set to non blocking

while((n = Readline(cfd, buf, sizeof(buf))) > 0);
Send response message status line and header
//code: Status code 
//msg: Status code corresponding information ( Such as OK)
//fileType: file type ( Such as Content-Type:text/plain;char....)
//len: Data length (Content-Length)
int send_header(int cfd, char *code, char *msg, char *fileType, int len)
{
    
    char buf[1024] = {
    0};
    
    sprintf(buf, "HTTP/1.1 %s %s\r\n", code, msg);
    // Go to buf+strlen(buf) The memory area pointed to starts , Write in the following contents ,
    sprintf(buf + strlen(buf), "Content-Type:%s\r\n", fileType);
    if(len > 0)
    {
    
        sprintf(buf + strlen(buf), "Content-Length:%d\r\n", len);
    }
    strcat(buf, "\r\n");
    Write(cfd, buf, strlen(buf));
    return 0;
}
Send response message body
int send_file(int cfd, char *fileName)
{
    
    // Open file 
    int fd = open(fileName, O_RDONLY);
    if(fd < 0)
    {
    
        perror("open error");
        return -1;
    }

    // Loop through the file and send 
    char buf[1024];
    int n;
    while(1)
    {
    
        memset(buf, 0x00, sizeof(buf));
        n = read(fd, buf, sizeof(buf));

        // Less than 0 Abnormal reading , be equal to 0 Indicates to the end of the file 
        if(n <= 0)
        {
    
            break;
        }
        else
        {
    
            Write(cfd, buf, n);
        }
    }
}
Judge whether the file exists
struct stat st;
stat(pFile, &st); //  Less than 0 For there is no such thing , On the contrary 
Handle when the file does not exist

Organize reply messages :http The response message + Error page :

printf("file not exist\n");

// Organize reply messages :http The response message + Error page 
send_header(cfd, "404", "NOT FOUND", get_mime_type(".html"), 0);

send_file(cfd, "error.html");
When a file exists and is a normal file

For ordinary documents , below get_mime_type() Get file type by file name , The implementation will be given at the end :

if(S_ISREG(st.st_mode))
{
    
    printf("file exist\n");
    // Send status line and header information 
    send_header(cfd, "200", "OK", get_mime_type(pFile), st.st_size);
    // Send the body of the file 
    send_file(cfd, pFile);
}
The file exists and is a directory file

If it is a directory file, we should return the status line and message header , We return The response body should be a html The page displays the files in the directory ( Here's the picture ), So we need to splice to generate a html file , It's on it html head , In the middle is the contents of the directory ( Program implementation is required , Can't write dead ) And then add html Tail of .

 Insert picture description here

send out html The first half dir_header.html

<html><head><title>Index of ./</title></head>
<body bgcolor="#99cc99"><h4>Index of ./</h4>
<ul type=circle>

send out html Part of the directory display of our program implementation , Be careful ** If it's a directory href At the end of the value, add /,** The following code should focus on , If it is not added, an error will occur when accessing the inner file .

<li><a href= Balsam pear .txt>  Balsam pear .txt </a></li>
<li><a href=aa/> aa </a></li>

send out html At the end of dir_tail.html

</ul>
<address><a href="http://www.baidu.com/">xhttpd</a></address>
</body></html>

The implementation of the code is as follows :

else if(S_ISDIR(st.st_mode))// Directory file 
{
    
    // The directory file uses html To present the content 
    printf(" Directory file \n");

    // Send a header message 
    send_header(cfd, "200", "OK", get_mime_type(".html"), 0);

    // send out html File header 
    send_file(cfd, "html/dir_header.html");

    // File list information ( Deposit struct dirent*  Array of elements )
    struct dirent **namelist;
    int num;
    char buffer[1024];
    
    // From catalog pFile Read the file information in the directory and write it to namelist Array 
    // Returns the number of files in the directory 
    num = scandir(pFile, &namelist, NULL, alphasort);
    if (num < 0) 
    {
    
        perror("scandir");
        close(cfd);
        // The event tree corresponding to the file descriptor 
        epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
        return -1;
    }
    else 
    {
    
        while (num--) 
        {
    
            printf("%s\n", namelist[num]->d_name);
            // Send directories one by one 
            memset(buffer, 0x00,sizeof(buf));
            if(namelist[num]->d_type == DT_DIR) // Catalog ,href The value should be added to the end /
            {
    
                sprintf(buffer, "<li><a href=%s/> %s </a></li>", namelist[num]->d_name, namelist[num]->d_name);
            }
            else // Ordinary documents 
            {
    
                sprintf(buffer, "<li><a href=%s> %s </a></li>", namelist[num]->d_name, namelist[num]->d_name);
            }
            free(namelist[num]);
            Write(cfd, buffer, strlen(buffer));
        }
        free(namelist);
    }   

    //sleep(10);
    // send out html The tail 
    send_file(cfd, "html/dir_tail.html");

}

miscellaneous

  • Change the current working directory of the process , Let the program search under the specified directory when looking for files :

    char path[255] = {
          0};
    //getenv("HOME") Get environment variables HOME Value 
    sprintf(path, "%s/%s", getenv("HOME"), "webpath");
    chdir(path);
    

     Insert picture description here

  • The problem of unable to access Chinese files has been solved

    When we click on a file named Chinese, the file name we receive is UTF8 code , If you click Balsam pear .txt when , The file name parsed from the request message we received is % spaced utf8 Encoded string form :

     Insert picture description here

     Insert picture description here

    Balsam pear utf8 code ( Hexadecimal ) by : bitter :e8 8b a6 ; melon :e7 93 9c

    notes : Chinese utf8 Coding 3 Bytes

     Insert picture description here

    The solution to this problem is : Put each Chinese utf8 code ( Stored in string format ) Convert to decimal and store in three char Variable ( One char Variables of 1B Enough two hexadecimal digits ) in :

     Insert picture description here

    The code implementation is as follows :

    void strdecode(char *to, char *from)
    {
          
        for ( ; *from != '\0'; ++to, ++from) {
          
    
            if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) {
           // Judge in turn from in  %20  Three characters 
    
                *to = hexit(from[1])*16 + hexit(from[2]);// character string E8 Become a real 16 It's binary E8
                from += 2;                      // Move past the two characters that have been processed (%21 Pointer to 1), expression 3 Of ++from One more character will be moved back 
            } else
                *to = *from;
        }
        *to = '\0';
    }
    
    //16 The base number is converted to 10 Base number , return 0 There will be no 
    int hexit(char c)
    {
          
        if (c >= '0' && c <= '9')
            return c - '0';
        if (c >= 'a' && c <= 'f')
            return c - 'a' + 10;
        if (c >= 'A' && c <= 'F')
            return c - 'A' + 10;
    
        return 0;
    }
    
  • SIGPIPE Signal problem ( The receiving end is disconnected while the sending end still sends data –》 Pipe break )

Problem description :** When the server is sending data to the client , When the data has not been sent , browser ( client ) Just disconnect ,** At this time, it is equivalent to that the reading end of the pipe is closed , So the server will receive SIGPIPE The signal

The test method : When sending the catalog file ,** send out html In the tail sleep(10), Take the initiative to close the browser ,** Check the server program and find that the program has ended .

resolvent : Because of this situation, even if the resources are wasted , But we don't want to stop the server program casually , So we use the method of ignoring the signal , stay main Function to register the signal processing function :

int main()
{
    
    .....
    struct sigaction act;
    act.sa_handler = SIG_IGN;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGPIPE, &act, NULL);
	.....
}

matters needing attention

  1. When the browser returns to the previous level , In fact, the original client is closed cfd, Then establish a new connection to get cfd To send request data .

appendix

webserver.c

//web Server program : Use epoll Model 
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include "pub.h"
#include "wrap.h"

// Handle the request message sent by the client 
int http_request(int cfd, int epfd);

// Send the status line and header of the response message 
int send_header(int cfd, char *code, char *msg, char *fileType, int len);

// Send the message body of the response message 
int send_file(int cfd, char *fileName);


int main()
{
    
    // if web When the server sends data to the browser, the client disconnects 
    //web The server will receive SIGPIPE The signal 
    // Ignore SIGPIPE The signal 
    //signal(SIGPIPE, SIG_IGN);

    struct sigaction act;
    act.sa_handler = SIG_IGN;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGPIPE, &act, NULL);

    // Change the current working directory , Let it access the resource file 
    char path[255] = {
    0};
    //getenv("HOME") Get environment variables HOME Value 
    sprintf(path, "%s/%s", getenv("HOME"), "webpath");
    chdir(path);

    // establish socket、 Set up port multiplexing 、bind
    int lfd = tcp4bind(9999, NULL);

    // Set listening 
    Listen(lfd, 128);

    // establish epoll Trees 
    int epfd = epoll_create(1024);
    if(epfd < 0)
    {
    
        perror("epoll create error");
        close(lfd);
        return  -1;
    }

    // Will listen to the file descriptor on the tree 
    struct epoll_event ev;
    ev.data.fd = lfd;
    ev.events = EPOLLIN;
    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);

    int i = 0;
    int nready;
    int cfd;
    int sockfd;
    struct epoll_event events[1024]; 
    while(1)
    {
    
        // Waiting for events to happen , Parameters -1 It means blocking 
        nready = epoll_wait(epfd, events, 1024, -1);
        if(nready < 0)
        {
    
            // If it is an interrupt signal , Don't think it's wrong 
            if (errno == EINTR)
            {
    
                continue;
            }
            break;
        }
        for(i = 0;i < nready; ++i)
        {
    
            sockfd = events[i].data.fd;
            // There are client connection requests 
            if(sockfd == lfd)
            {
    
                // Accept new client connections 
                cfd = Accept(lfd, NULL, NULL);

                // Set up cfd Non blocking 
                int flag = fcntl(cfd, F_GETFL);
                flag |= O_NONBLOCK;
                fcntl(cfd, F_SETFL, flag);

                // The newly acquired cfd Up a tree 
                ev.data.fd = cfd;
                ev.events = EPOLLIN;
                epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);

            }
            else // There is client data sent 
            {
    
                http_request(sockfd, epfd);
            }
        }
    }
} 


int http_request(int cfd, int epfd)
{
    
    int n;
    char buf[1024];
    // Read request line , Cut out the requested file name 
    memset(buf, 0x00, sizeof(buf));
    // Returns the number of bytes read 
    n = Readline(cfd, buf, sizeof(buf));
    if(n <= 0) // Return in non blocking mode -1 I have read nothing .
    {
    
        //printf("read error or client closed, n = [%d]\n", n);
        // Read exception or client disconnection 
        close(cfd);
        epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
        return -1;
    }
    //GET /hanzi.c HTTP/1.1
    printf("buf = [%s]\n", buf);
    char reqType[16] = {
    0};
    char fileName[255] = {
    0};
    char protocol[16] = {
    0};
    
    sscanf(buf, "%[^ ] %[^ ] %[^ \r\n]", reqType, fileName, protocol);
    //printf("[%s]\n", reqType);
    printf("[%s]\n", fileName);
    //printf("[%s]\n", protocol);
    
    char *pFile = fileName; //  You have to assign a value first , Prevent the back strcpy Operation field pointer 
    // Server input :http://192.168.200.150:9000 perhaps http://192.168.200.150:9000/
    // You should be able to identify the root directory of the current resource 
    if(strlen(fileName) <= 1)
    {
    
        strcpy(pFile, "./");
    }
    else
    {
    
        // here FileName Intercept as /hanzi.c, To get rid of /
        pFile = fileName + 1;
    }

    // Convert Chinese character code ( The name is %xx%xx Strings like that )
    strdecode(pFile, pFile);

    // Cycle through the remaining data , Prevent sticky packets from affecting the data interpretation of the request sent by the next client 
    // Due to the internal call read, It's blocking , Therefore, we should cfd Set to non blocking 
    while((n = Readline(cfd, buf, sizeof(buf))) > 0);

    // Judge whether the file exists 
    struct stat st;
    //stat(fileName, &st)  By file name fileName Get the file information and store it in st in 
    // If the file does not exist : 
    if(stat(pFile, &st) < 0)
    {
    
        printf("file not exist\n");

        // Organize reply messages :http The response message + Error page 
        send_header(cfd, "404", "NOT FOUND", get_mime_type(".html"), 0);

        send_file(cfd, "error.html");
    }    
    else// If the file exists :
    {
    
        // Ordinary documents 
        if(S_ISREG(st.st_mode))
        {
    
            printf("file exist\n");
            // Send status line and header information 
            send_header(cfd, "200", "OK", get_mime_type(pFile), st.st_size);
            // Send the body of the file 
            send_file(cfd, pFile);
        }
        else if(S_ISDIR(st.st_mode))// Directory file 
        {
    
            // The directory file uses html To present the content 
            printf(" Directory file \n");
            
            // Send a header message 
            send_header(cfd, "200", "OK", get_mime_type(".html"), 0);

            // send out html File header 
            send_file(cfd, "html/dir_header.html");

            // File list information 
            struct dirent **namelist;
            int num;
            char buffer[1024];
            num = scandir(pFile, &namelist, NULL, alphasort);
            if (num < 0) 
            {
    
                perror("scandir");
                close(cfd);
                // The event tree corresponding to the file descriptor 
                epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
                return -1;
            }
            else 
            {
    
                while (num--) 
                {
    
                    printf("%s\n", namelist[num]->d_name);
                    // Send directories one by one 
                    memset(buffer, 0x00,sizeof(buf));
                    if(namelist[num]->d_type == DT_DIR) // Catalog 
                    {
    
                        sprintf(buffer, "<li><a href=%s/> %s </a></li>", namelist[num]->d_name, namelist[num]->d_name);
                    }
                    else // Ordinary documents 
                    {
    
                        sprintf(buffer, "<li><a href=%s> %s </a></li>", namelist[num]->d_name, namelist[num]->d_name);
                    }
                    free(namelist[num]);
                    Write(cfd, buffer, strlen(buffer));
                }
                free(namelist);
            }   

            //sleep(10);
            // send out html The tail 
            send_file(cfd, "html/dir_tail.html");
            
        }
            
    }         
}


//code: Status code 
//msg: Status code corresponding information ( Such as OK)
//fileType: file type ( Such as Content-Type:text/plain;char....)
//len: Data length (Content-Length)
int send_header(int cfd, char *code, char *msg, char *fileType, int len)
{
    
    char buf[1024] = {
    0};
    
    sprintf(buf, "HTTP/1.1 %s %s\r\n", code, msg);
    // Go to buf+strlen(buf) The memory area pointed to starts , Write in the following contents ,
    sprintf(buf + strlen(buf), "Content-Type:%s\r\n", fileType);
    if(len > 0)
    {
    
        sprintf(buf + strlen(buf), "Content-Length:%d\r\n", len);
    }
    strcat(buf, "\r\n");
    Write(cfd, buf, strlen(buf));
    return 0;
}

int send_file(int cfd, char *fileName)
{
    
    // Open file 
    int fd = open(fileName, O_RDONLY);
    if(fd < 0)
    {
    
        perror("open error");
        return -1;
    }

    // Loop through the file and send 
    char buf[1024];
    int n;
    while(1)
    {
    
        memset(buf, 0x00, sizeof(buf));
        n = read(fd, buf, sizeof(buf));

        if(n <= 0)
        {
    
            break;
        }
        else
        {
    
            Write(cfd, buf, n);
        }
    }
}

pub.c

#include "pub.h"
// Get the file type by the file name 
char *get_mime_type(char *name)
{
    
    char* dot;

    dot = strrchr(name, '.');	// Find from right to left ‘.’ character ; If it does not exist, return NULL
    /* *charset=iso-8859-1  Code for Western Europe , The code of the website is English ; *charset=gb2312  Description the code of the website is simplified Chinese ; *charset=utf-8  Represents the universal language code ; *  You can use Chinese 、 Korean 、 Japanese and other languages in the world  *charset=euc-kr  The code of the website is Korean ; *charset=big5  The code used in the website is traditional Chinese ; * * The following is based on the passed in file name , Use suffixes to determine what file type it is  * Set the corresponding file type as http The defined keywords are sent back  */
    if (dot == (char*)0)
        return "text/plain; charset=utf-8";
    if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
        return "text/html; charset=utf-8";
    if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
        return "image/jpeg";
    if (strcmp(dot, ".gif") == 0)
        return "image/gif";
    if (strcmp(dot, ".png") == 0)
        return "image/png";
    if (strcmp(dot, ".css") == 0)
        return "text/css";
    if (strcmp(dot, ".au") == 0)
        return "audio/basic";
    if (strcmp( dot, ".wav") == 0)
        return "audio/wav";
    if (strcmp(dot, ".avi") == 0)
        return "video/x-msvideo";
    if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
        return "video/quicktime";
    if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
        return "video/mpeg";
    if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
        return "model/vrml";
    if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
        return "audio/midi";
    if (strcmp(dot, ".mp3") == 0)
        return "audio/mpeg";
    if (strcmp(dot, ".ogg") == 0)
        return "application/ogg";
    if (strcmp(dot, ".pac") == 0)
        return "application/x-ns-proxy-autoconfig";

    return "text/plain; charset=utf-8";
}
/**********************************************************************/
/* Get a line from a socket, whether the line ends in a newline, * carriage return, or a CRLF combination. Terminates the string read * with a null character. If no newline indicator is found before the * end of the buffer, the string is terminated with a null. If any of * the above three line terminators is read, the last character of the * string will be a linefeed and the string will be terminated with a * null character. * Parameters: the socket descriptor * the buffer to save the data in * the size of the buffer * Returns: the number of bytes stored (excluding null) */
/**********************************************************************/
// Get a row of data , Every line is marked with \r\n As the closing tag 
int get_line(int sock, char *buf, int size)
{
    
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n'))
    {
    
        n = recv(sock, &c, 1, 0);
        /* DEBUG printf("%02X\n", c); */
        if (n > 0)
        {
    
            if (c == '\r')
            {
    
                n = recv(sock, &c, 1, MSG_PEEK);//MSG_PEEK  Read data from buffer , But the data is not purged from the buffer 
                /* DEBUG printf("%02X\n", c); */
                if ((n > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);
                else
                    c = '\n';
            }
            buf[i] = c;
            i++;
        }
        else
            c = '\n';
    }
    buf[i] = '\0';

    return(i);
}

// The following function will be used the next day 
/* *  The content here is to deal with %20 Things like that ! yes " decode " The process . * %20 URL In coding ‘ ’(space) * %21 '!' %22 '"' %23 '#' %24 '$' * %25 '%' %26 '&' %27 ''' %28 '('...... *  Related knowledge html Medium ‘ ’(space) yes &nbsp */
void strdecode(char *to, char *from)
{
    
    for ( ; *from != '\0'; ++to, ++from) {
    

        if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) {
     // Judge in turn from in  %20  Three characters 

            *to = hexit(from[1])*16 + hexit(from[2]);// character string E8 Become a real 16 It's binary E8
            from += 2;                      // Move past the two characters that have been processed (%21 Pointer to 1), expression 3 Of ++from One more character will be moved back 
        } else
            *to = *from;
    }
    *to = '\0';
}

//16 The base number is converted to 10 Base number , return 0 There will be no 
int hexit(char c)
{
    
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    if (c >= 'A' && c <= 'F')
        return c - 'A' + 10;

    return 0;
}

//" code ", When used as a write back browser , Divide by alphanumeric and /_.-~ Write back after escaping characters other than .
//strencode(encoded_name, sizeof(encoded_name), name);
void strencode(char* to, size_t tosize, const char* from)
{
    
    int tolen;

    for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from) {
    
        if (isalnum(*from) || strchr("/_.-~", *from) != (char*)0) {
    
            *to = *from;
            ++to;
            ++tolen;
        } else {
    
            sprintf(to, "%%%02x", (int) *from & 0xff);
            to += 3;
            tolen += 3;
        }
    }
    *to = '\0';
}

pub.h

#ifndef _PUB_H
#define _PUB_H
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <ctype.h>
char *get_mime_type(char *name);
int get_line(int sock, char *buf, int size);
int hexit(char c);//16 Turn into the system 10 Base number 
void strencode(char* to, size_t tosize, const char* from);// code 
void strdecode(char *to, char *from);// decode 
#endif


wrap.h

#ifndef __WRAP_H_
#define __WRAP_H_
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>

void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);
int tcp4bind(short port,const char *IP);
#endif

wrap.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
// Binding error display and exit 
void perr_exit(const char *s)
{
    
	perror(s);
	exit(-1);
}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
    
	int n;

again:
	if ((n = accept(fd, sa, salenptr)) < 0) {
    
		if ((errno == ECONNABORTED) || (errno == EINTR))//ECONNABORTED  Represents a connection failure  ETINTR  Represents being interrupted by a signal 
			goto again;
		else
			perr_exit("accept error");
	}
	return n;
}

int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    
    int n;

	if ((n = bind(fd, sa, salen)) < 0)
		perr_exit("bind error");

    return n;
}

int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
    
    int n;

	if ((n = connect(fd, sa, salen)) < 0)
		perr_exit("connect error");

    return n;
}

int Listen(int fd, int backlog)
{
    
    int n;

	if ((n = listen(fd, backlog)) < 0)
		perr_exit("listen error");

    return n;
}

int Socket(int family, int type, int protocol)
{
    
	int n;

	if ((n = socket(family, type, protocol)) < 0)
		perr_exit("socket error");

	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)// If you are interrupted by a signal, you should continue reading 
			goto again;
		else
			return -1;
	}
	return n;
}

ssize_t Write(int fd, const void *ptr, size_t nbytes)
{
    
	ssize_t n;

again:
	if ( (n = write(fd, ptr, nbytes)) == -1) {
    
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

int Close(int fd)
{
    
    int n;
	if ((n = close(fd)) == -1)
		perr_exit("close error");

    return n;
}

/* Shen San :  Number of bytes that should be read */
ssize_t Readn(int fd, void *vptr, size_t n)
{
    
	size_t  nleft;              //usigned int  Number of bytes left unread 
	ssize_t nread;              //int  The number of bytes actually read 
	char   *ptr;

	ptr = vptr;
	nleft = n;

	while (nleft > 0) {
    
		if ((nread = read(fd, ptr, nleft)) < 0) {
    
			if (errno == EINTR)
				nread = 0;
			else
				return -1;
		} else if (nread == 0)
			break;

		nleft -= nread;// To prevent data from not being read at one time 
		ptr += nread;// The pointer needs to move back 
	}
	return n - nleft;
}

ssize_t Writen(int fd, const void *vptr, size_t n)
{
    
	size_t nleft;
	ssize_t nwritten;
	const char *ptr;

	ptr = vptr;
	nleft = n;
	while (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];// Defined 100 The buffer 

	if (read_cnt <= 0) {
    
again:
        // Using a buffer can avoid reading data from the underlying buffer multiple times -- In order to improve efficiency 
		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++;// Fetch data from buffer 

	return 1;
}
// Read a line 
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')// Represents the completion of the task 
				break;
		} else if (rc == 0) {
    // End to end shutdown 
			*ptr = 0;//0 = '\0'
			return n - 1;
		} else
			return -1;
	}
	*ptr  = 0;

	return n;
}

int tcp4bind(short port,const char *IP)
{
    
    struct sockaddr_in serv_addr;
    int lfd = Socket(AF_INET,SOCK_STREAM,0);
    bzero(&serv_addr,sizeof(serv_addr));// Empty serv_addr Address   contrast  memset()
    if(IP == NULL){
    
        // If used in this way  0.0.0.0, arbitrarily ip Will be able to connect 
        serv_addr.sin_addr.s_addr = INADDR_ANY;
    }else{
    
        if(inet_pton(AF_INET,IP,&serv_addr.sin_addr.s_addr) <= 0){
    
            perror(IP);// switch views 
            exit(1);
        }
    }
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port   = htons(port);
    int opt = 1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    Bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    return lfd;
}


原网站

版权声明
本文为[qq_ forty-two million one hundred and twenty thousand eight hun]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/162/202206111426450571.html