Linux下用c语言实现一个简单的web服务器
程序员文章站
2022-05-08 23:51:55
...
github链接:c语言web服务器
这个服务器程序能够处理简单的GET请求,能够解析类似/%s或/./%s类的url
因为后期解析url时感觉c语言的原生字符串不太方便,所以自己简单封装了一个字符串库。
头文件:mystring.h
#ifndef MYSTRING_H
#define MYSTRING_H
typedef struct
{
char *data;
long length;
long ptr;
} String;
String initString();
void addChar(String *str, char ch);
void addString(String *dec, char *src, long len);
int isEqual(String src, char *dec);
long getStringSize(String str);
void freeString(String str);
char *toCString(String str);
#endif //MY_STRING
对应的mystring.c
#include <stdlib.h>
#include <string.h>
#include "mystring.h"
#include "stringarray.h"
#define STRINGLENGTH 30
String initString()
{
String string;
string.length = STRINGLENGTH;
string.ptr = 0;
string.data = (char *)malloc(sizeof(char) * string.length);
return string;
}
void addChar(String *str, char ch)
{
if (str->ptr >= str->length)
{
str->length *= 2;
str->data = (char *)realloc(str->data, str->length);
}
str->data[str->ptr++] = ch;
}
void addString(String *dec, char *src, long len)
{
while (len--)
{
addChar(dec, *src);
src++;
}
}
int isEqual(String src, char *dec)
{
for (long i = 0; i < src.ptr; i++)
{
if (src.data[i] != dec[i])
{
return 0;
}
}
return 1;
}
long getStringSize(String str)
{
return str.ptr;
}
void freeString(String str)
{
free(str.data);
}
char *toCString(String str)
{
str.data[str.ptr] = 0;
return str.data;
}
然后封装了对mystring类型的列表操作
头文件stringarray.h
#ifndef STRINGARRAY_H
#define STRINGARRAY_H
#include "mystring.h"
typedef struct
{
String *data;
long length;
long ptr;
} StringArray;
StringArray initArray();
void pushBack(StringArray *array, String str);
long getArraySize(StringArray array);
String getString(StringArray array, long ptr);
StringArray split(String str, const char *pat);
void freeArray(StringArray array);
#endif //STRINGARRAY_H
对应的stringarray.c
#include <stdlib.h>
#include <stdio.h>
#include "stringarray.h"
#define ARRAYLENGTH 30
StringArray initArray()
{
StringArray array;
array.length = ARRAYLENGTH;
array.data = (String *)malloc(sizeof(String) * array.length);
array.ptr = 0;
return array;
}
void pushBack(StringArray *array, String str)
{
if (array->ptr >= array->length)
{
array->length *= 2;
array->data = (String *)realloc(array->data, sizeof(String) * array->length);
}
array->data[array->ptr++] = str;
}
long getArraySize(StringArray array)
{
return array.ptr;
}
String getString(StringArray array, long ptr)
{
if (array.ptr <= ptr)
{
fprintf(stderr, "StringArray out of index\n");
exit(1);
}
return array.data[ptr];
}
StringArray split(String str, const char *pat)
{
StringArray array = initArray();
char *temp = str.data;
char *ptr;
long p = 0;
while ((ptr = strstr(temp, pat)) != NULL)
{
String x = initString();
while (temp != ptr)
{
addChar(&x, *temp);
temp++;
p++;
}
temp += strlen(pat);
p++;
pushBack(&array, x);
}
String x = initString();
while (p < str.ptr)
{
addChar(&x, *temp);
temp++;
p++;
}
pushBack(&array, x);
return array;
}
void freeArray(StringArray array)
{
free(array.data);
}
最后是main.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include "mystring.h"
#include "stringarray.h"
int server;
void interrupt(int x) //当输入CTRL-C时关闭服务器
{
if (x == SIGINT)
{
shutdown(server, SHUT_RDWR);
close(server);
exit(0);
}
}
String numToStr(long x) //获得将要传输的文件大小并转为字符串类型,以便于写入到http头中
{
String ans = initString();
int ptr = 0;
int data[1000];
while (x)
{
data[ptr++] = x % 10;
x /= 10;
}
for(ptr--; ptr >= 0; ptr--)
{
addChar(&ans, (char) (data[ptr] + 0x30));
}
return ans;
}
void sender(int client, char *fileName)
{
String str = initString();
FILE *fp = fopen(fileName, "rb");
if (fp) //如果打开文件成功,发送200文件头,否则说明要传输的文件不存在,发送404文件头和404页面
{
printf("200\n", fileName);
char *t = "HTTP/1.0 200 OK\r\n";
addString(&str, t, strlen(t));
addString(&str, "Content-Length: ", 16);
int x;
long len = 0;
String temp = initString();
while ((x = fgetc(fp)) != EOF) //将文件中读到的字符加到temp中并记录文件大小
{
len++;
addChar(&temp, (char) x);
}
String l = numToStr(len); //获得将要传输的文件大小并转为字符串类型,以便于写入到http头中
addString(&str, toCString(l), getStringSize(l)); //向http头中添加文件大小信息
addString(&str, "\r\n\r\n", 4);
addString(&str, toCString(temp), getStringSize(temp)); //将要传输的内容写在http头后面
freeString(l);
freeString(temp);
fclose(fp);
}
else
{
printf("404\n", fileName);
addString(&str, "HTTP/1.1 404 Not Found\r\n\r\n", 26);
fp = fopen("404.html", "rb");
int x;
while ((x = fgetc(fp)) != EOF)
{
addChar(&str, (char) x);
}
fclose(fp);
}
write(client, toCString(str), str.ptr);
freeString(str);
}
void *handle(void *arg) //处理请求
{
int client = *(int *)arg; //获得客户端socket
String str = initString();
char buffer[1024];
read(client, buffer, sizeof(buffer));
/* 开始解析url信息
在这里只解析GET信息
只判断url为/%s和/./%s时的情况,%s为请求的文件
*/
addString(&str, buffer, strlen(buffer));
StringArray array = split(str, "\r\n"); //将http请求头按行分割
for (long i = 0; i < getArraySize(array); i++)
{
if (strstr(toCString(getString(array, i)), "GET")) //请求GET
{
String url = getString(split(getString(array, i), " "), 1); //得到url信息
StringArray arr = split(url, "/");
printf("get %s ", toCString(url));
if (getArraySize(arr) == 2) //处理url为/%s的情况
{
String fileName = getString(arr, 1);
sender(client, toCString(fileName));
freeString(fileName);
}
else if (getArraySize(arr) == 3 && isEqual(getString(arr, 1), ".")) //处理url为/./%s的情况
{
String fileName = getString(arr, 2);
sender(client, toCString(fileName));
freeString(fileName);
}
freeString(url);
freeArray(arr);
}
}
freeArray(array);
freeString(str);
shutdown(client, SHUT_RDWR);
close(client);
return NULL;
}
int main()
{
//服务器地址为127.0.0.1:8080
char *addr = "127.0.0.1";
int port = 8080;
signal(SIGINT, interrupt); //接收CTRL-C信号
server = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
serverAddr.sin_addr.s_addr = inet_addr(addr);
int x = bind(server, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
if (x != -1)
{
printf("Listen at %s:%d\n", addr, port);
listen(server, 1000);
struct sockaddr_in clientAddr;
socklen_t len = sizeof(clientAddr);
while (1)
{
int client = accept(server, (struct sockaddr *) &clientAddr, &len);
printf("connected by %s\n", inet_ntoa(clientAddr.sin_addr)); //打印客户端连接
/* 多线程处理 */
pthread_t th;
pthread_create(&th, NULL, handle, &client);
}
}
else
{
printf("%s:%d cannot be used\n", addr, port);
}
close(server);
return 0;
}
上一篇: 一个简单的Node服务器