当前位置:网站首页>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
List of articles
- Simple use C Language version implemented webserver
- Abstract
- Technical points
- BS Mode diagram ( function )
- The execution flow chart of the program
- Concrete realization
- establish socket And bind the port number
- Set listening
- establish epoll Trees
- Will listen for file descriptors lfd On epoll Trees
- Call in loop `epoll_wait` Loop waiting for the event to occur
- Processing requests from browsers http message (httprequest function )
- Pre knowledge :http message
- Thought analysis
- Read the request data and parse
- Cycle through the remaining data
- Send response message status line and header
- Send response message body
- Judge whether the file exists
- Handle when the file does not exist
- When a file exists and is a normal file
- The file exists and is a directory file
- miscellaneous
- matters needing attention
- appendix
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
- multiple IO Multiplexing technology epoll
- http Request and answer protocol
- TCP agreement : Three handshakes, four waves , After the connection is established, carry out data transmission
- web The server : Parse the request data sent by the browser , Get the request file name
- Directory access function
- The problem of accessing the Chinese Directory
BS Mode diagram ( function )

The execution flow chart of the program

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

- http Response message

Thought analysis
Processing the request message sent by the browser can be divided into the following steps :
- Read request line data , Resolve the requested resource file name
- Cycle through the remaining data
- Judge whether the file exists
- non-existent : return http The response message + Error page content
- There is :
- Ordinary documents
- 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 .

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);
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 .txtwhen , The file name parsed from the request message we received is % spaced utf8 Encoded string form :

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

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 :

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
- 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   */
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;
}
边栏推荐
- In depth research and analysis report on global and Chinese content audit market
- 2021 年度 Go 开发者调查
- What is excess product power? Find the secret key of the second generation cs75plus in the year of the tiger
- Telecommuting with cpolar (1)
- Repository Manager之Nexus
- Anaconda delete virtual environment
- 数字化转型项目做了多年,主架构师都绝望了:当初就不应该用外包!
- After many years of digital transformation projects, the main architects are desperate: outsourcing should not have been used at the beginning!
- 2022-2028 near infrared (NIR) analyzer Market Status and future development trend in the world and China
- 化“被动”为“主动”,如何构建安全合规的智能产品 | Q推荐
猜你喜欢

HMS core shows the latest open capabilities in mwc2022, helping developers build high-quality applications

非常值得学习的调度开源库推荐

MySQL advanced statement

Uniapp settings page Jump effect - navigateto switching effect - Global animationtype animation

Leetcode 1962. 移除石子使总数最小(应该是向上取整)

【SystemVerilog 之 过程块和方法】~ 域、always过程块、initial过程块、函数 function、任务 task、生命周期

Online "comment explicit" function, TME's wave point music cultivates music "private plots"

非常值得學習的調度開源庫推薦

2022年全国最新消防设施操作员(初级消防设施操作员)题库及答案

What is excess product power? Find the secret key of the second generation cs75plus in the year of the tiger
随机推荐
2022年全国最新消防设施操作员(初级消防设施操作员)题库及答案
In depth research and analysis report on global and Chinese high purity molybdenum market
英伟达研发主管:AI 是如何改进芯片设计的?
非常值得学习的调度开源库推荐
Current situation and future development trend of global and Chinese metalworking fluid market from 2022 to 2028
In depth research and analysis report on global and Chinese smart lamp Market
【公开课预告】:MXPlayer OTT音视频转码实践和优化
深度剖析「圈组」关系系统设计 | 「圈组」技术系列文章
2021 年度 Go 开发者调查
Extracting storage is the best memory method
MySQL create table error 1067 - invalid default value for 'update_ time‘
Precision alignment adjustment platform
非常值得學習的調度開源庫推薦
Cartoon: interesting "cake cutting" problem
Taking log4j as an example, how to evaluate and classify security risks
Hashicopy之nomad应用编排方案01
Repository Manager之Nexus
Global and China dynamic light scattering nano laser particle sizer market depth research and Analysis Report
[team learning] task06:for, if, and while
[verification of SystemVerilog] ~ test platform, hardware design description, excitation generator, monitor and comparator