实验四 web服务器2
实验四 Web服务器2
基于华为鲲鹏云服务器CentOS中(或Ubuntu),使用Linux Socket实现:
- 1. Web服务器的客户端服务器,提交程序运行截图
- 2. 实现GET即可,请求,响应要符合HTTP协议规范
- 3. 服务器部署到华为云服务器,浏览器用本机的
- 4. 把服务器部署到试验箱。(加分项)
服务器webserver.c代码:
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h> // socket
#include <sys/types.h> // 基本数据类型
#include <unistd.h> // read write
#include <string.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <fcntl.h> // open close
#include <sys/shm.h>
#include <signal.h>
#define PORT 8000
#define SERV "81.70.18.119"
#define QUEUE 20
#define BUFF_SIZE 1024
typedef struct doc_type{
char *key;
char *value;
}HTTP_CONTENT_TYPE;
HTTP_CONTENT_TYPE http_content_type[] = {
{ "html","text/html" },
{ "gif" ,"image/gif" },
{ "jpeg","image/jpeg" }
};
int sockfd;
char *http_res_tmpl = "HTTP/1.1 200 OK\r\n"
"Server: Cleey's Server V1.0\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"Content-Type: %s\r\n\r\n";
void handle_signal(int sign); // 退出信号处理
void http_send(int sock,char *content); // http 发送相应报文
int main(){
signal(SIGINT,handle_signal);
int count = 0; // 计数
// 定义 socket
sockfd = socket(AF_INET,SOCK_STREAM,0);
// 定义 sockaddr_in
struct sockaddr_in skaddr;
skaddr.sin_family = AF_INET; // ipv4
skaddr.sin_port = htons(PORT);
//skaddr.sin_addr.s_addr = inet_addr(SERV);
skaddr.sin_addr.s_addr = htonl ( INADDR_ANY );
// bind,绑定 socket 和 sockaddr_in
if( bind(sockfd,(struct sockaddr *)&skaddr,sizeof(skaddr)) == -1 ){
perror("bind error");
exit(1);
}
// listen,开始添加端口
if( listen(sockfd,QUEUE) == -1 ){
perror("listen error");
exit(1);
}
// 客户端信息
char buff[BUFF_SIZE];
struct sockaddr_in claddr;
socklen_t length = sizeof(claddr);
while(1){
int sock_client = accept(sockfd,(struct sockaddr *)&claddr, &length);
printf("%d\n",++count);
if( sock_client <0 ){
perror("accept error");
exit(1);
}
memset(buff,0,sizeof(buff));
int len = recv(sock_client,buff,sizeof(buff),0);
fputs(buff,stdout);
//send(sock_client,buff,len,0);
http_send(sock_client,"Hello World!");
close(sock_client);
}
fputs("Bye Cleey",stdout);
close(sockfd);
return 0;
}
void http_send(int sock_client,char *content){
char HTTP_HEADER[BUFF_SIZE],HTTP_INFO[BUFF_SIZE];
int len = strlen(content);
sprintf(HTTP_HEADER,http_res_tmpl,len,"text/html");
len = sprintf(HTTP_INFO,"%s%s",HTTP_HEADER,content);
send(sock_client,HTTP_INFO,len,0);
}
void handle_signal(int sign){
fputs("\nSIGNAL INTERRUPT \nBye Cleey! \nSAFE EXIT\n",stdout);
close(sockfd);
exit(0);
}
gettime.c
/*
* file:get_time.c
*/
#include <time.h>
#include <stdio.h>
#include <string.h>
#include "get_time.h"
/* get the time on server,
return: the ascii string of time , NULL on error
argument: time_buf the buffer to store time_string
*/
char *get_time_str(char *time_buf)
{
time_t now_sec;
struct tm *time_now;
if( time(&now_sec) == -1)
{
perror("time() in get_time.c");
return NULL;
}
if((time_now = gmtime(&now_sec)) == NULL)
{
perror("localtime in get_time.c");
return NULL;
}
char *str_ptr = NULL;
if((str_ptr = asctime(time_now)) == NULL)
{
perror("asctime in get_time.c");
return NULL;
}
strcat(time_buf, str_ptr);
return time_buf;
}
httpsession.c
/*
* file:http_session.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/in.h>
#include "http_session.h"
#include "get_time.h"
int http_session(int *connect_fd, struct sockaddr_in *client_addr)
{
char recv_buf[RECV_BUFFER_SIZE + 1]; /* server socket receive buffer */
unsigned char send_buf[SEND_BUFFER_SIZE + 1]; /* server socket send bufrer */
unsigned char file_buf[FILE_MAX_SIZE + 1];
memset(recv_buf, '\0', sizeof(recv_buf));
memset(send_buf, '\0', sizeof(send_buf));
memset(file_buf, '\0', sizeof(file_buf));
char uri_buf[URI_SIZE + 1]; /* store the the uri request from client */
memset(uri_buf, '\0', sizeof(uri_buf));
int maxfd = *connect_fd + 1;
fd_set read_set;
FD_ZERO(&read_set);
struct timeval timeout;
timeout.tv_sec = TIME_OUT_SEC;
timeout.tv_usec = TIME_OUT_USEC;
int flag = 1;
int res = 0;
int read_bytes = 0;
int send_bytes = 0;
int file_size = 0;
char *mime_type;
int uri_status;
FD_SET(*connect_fd, &read_set);
while(flag)
{
res = select(maxfd, &read_set, NULL, NULL, &timeout);
switch(res)
{
case -1:
perror("select() error. in http_sesseion.c");
close(*connect_fd);
return -1;
break;
case 0: /* time out, continue to select */
continue;
break;
default: /* there are some file-descriptor's status changed */
if(FD_ISSET(*connect_fd, &read_set))
{
memset(recv_buf, '\0', sizeof(recv_buf));
if((read_bytes = recv(*connect_fd, recv_buf, RECV_BUFFER_SIZE, 0)) == 0)
{
/* client close the connection */
return 0;
}
else if(read_bytes > 0) /* there are some data from client */
{
if(is_http_protocol(recv_buf) == 0) /* check is it HTTP protocol */
{
fprintf(stderr, "Not http protocol.\n");
close(*connect_fd);
return -1;
}
else /* http protocol */
{
memset(uri_buf, '\0', sizeof(uri_buf));
if(get_uri(recv_buf, uri_buf) == NULL) /* get the uri from http request head */
{
uri_status = URI_TOO_LONG;
}
else
{
printf("URI:%s\n", uri_buf);
uri_status = get_uri_status(uri_buf);
switch(uri_status)
{
case FILE_OK:
printf("file ok\n");
mime_type = get_mime_type(uri_buf);
printf("mime type: %s\n", mime_type);
file_size = get_file_disk(uri_buf, file_buf);
send_bytes = reply_normal_information(send_buf, file_buf, file_size, mime_type);
// send(*connect_fd, send_buf, send_bytes, 0);
break;
case FILE_NOT_FOUND: /* file not found on server */
printf("in switch on case FILE_NOT_FOUND\n");
send_bytes = set_error_information(send_buf, FILE_NOT_FOUND);
break;
case FILE_FORBIDEN: /* server have no permission to read the request file */
break;
case URI_TOO_LONG: /* the request uri is too long */
break;
default:
break;
}
send(*connect_fd, send_buf, send_bytes, 0);
}
}
}
}
}
}
return 0;
}
int is_http_protocol(char *msg_from_client)
{
/* just for test */
return 1;
int index = 0;
while(msg_from_client[index] != '\0' && msg_from_client[index] != '\n')
{
index++;
printf("%d%c",index - 1, msg_from_client[index - 1]);
}
if(strncmp(msg_from_client + index - 10, "HTTP/", 5) == 0) /* HTTP Request firt line like this 'GET /index.html HTTP/1.1' , so last 10 byte are HTTP/1.1\r\n*/
{
return 1;
}
return 0;
}
char *get_uri(char *req_header, char *uri_buf)
{
int index = 0;
while( (req_header[index] != '/') && (req_header[index] != '\0') )
{
index++;
}
int base = index;
while( ((index - base) < URI_SIZE) && (req_header[index] != ' ') && (req_header[index] != '\0') )
{
index++;
}
if( (index - base) >= URI_SIZE)
{
fprintf(stderr, "error: too long of uri request.\n");
return NULL;
}
if((req_header[index - 1] == '/') && (req_header[index] == ' '))
{
strcpy(uri_buf, "index.html");
return uri_buf;
}
strncpy(uri_buf, req_header + base + 1, index - base - 1);
return uri_buf;
}
int get_uri_status(char *uri)
{
if(access(uri, F_OK) == -1)
{
fprintf(stderr, "File: %s not found.\n", uri);
return FILE_NOT_FOUND;
}
if(access(uri, R_OK) == -1)
{
fprintf(stderr, "File: %s can not read.\n", uri);
return FILE_FORBIDEN;
}
return FILE_OK;
}
char *get_mime_type(char *uri)
{
int len = strlen(uri);
int dot = len - 1;
while( dot >= 0 && uri[dot] != '.')
{
dot--;
}
if(dot == 0) /* if the uri begain with a dot and the dot is the last one, then it is a bad uri request,so return NULL */
{
return NULL;
}
if(dot < 0) /* the uri is '/',so default type text/html returns */
{
return "text/html";
}
dot++;
int type_len = len - dot;
char *type_off = uri + dot;
switch(type_len)
{
case 4:
if(!strcmp(type_off, "html") || !strcmp(type_off, "HTML"))
{
return "text/html";
}
if(!strcmp(type_off, "jpeg") || !strcmp(type_off, "JPEG"))
{
return "image/jpeg";
}
break;
case 3:
if(!strcmp(type_off, "htm") || !strcmp(type_off, "HTM"))
{
return "text/html";
}
if(!strcmp(type_off, "css") || !strcmp(type_off, "CSS"))
{
return "text/css";
}
if(!strcmp(type_off, "png") || !strcmp(type_off, "PNG"))
{
return "image/png";
}
if(!strcmp(type_off, "jpg") || !strcmp(type_off, "JPG"))
{
return "image/jpeg";
}
if(!strcmp(type_off, "gif") || !strcmp(type_off, "GIF"))
{
return "image/gif";
}
if(!strcmp(type_off, "txt") || !strcmp(type_off, "TXT"))
{
return "text/plain";
}
break;
case 2:
if(!strcmp(type_off, "js") || !strcmp(type_off, "JS"))
{
return "text/javascript";
}
break;
default: /* unknown mime type or server do not support type now*/
return "NULL";
break;
}
return NULL;
}
int get_file_disk(char *uri, unsigned char *file_buf)
{
int read_count = 0;
int fd = open(uri, O_RDONLY);
if(fd == -1)
{
perror("open() in get_file_disk http_session.c");
return -1;
}
unsigned long st_size;
struct stat st;
if(fstat(fd, &st) == -1)
{
perror("stat() in get_file_disk http_session.c");
return -1;
}
st_size = st.st_size;
if(st_size > FILE_MAX_SIZE)
{
fprintf(stderr, "the file %s is too large.\n", uri);
return -1;
}
if((read_count = read(fd, file_buf, FILE_MAX_SIZE)) == -1)
{
perror("read() in get_file_disk http_session.c");
return -1;
}
printf("file %s size : %lu , read %d\n", uri, st_size, read_count);
return read_count;
}
int set_error_information(unsigned char *send_buf, int errorno)
{
register int index = 0;
register int len = 0;
char *str = NULL;
switch(errorno)
{
case FILE_NOT_FOUND:
printf("In set_error_information FILE_NOT_FOUND case\n");
str = "HTTP/1.1 404 File Not Found\r\n";
len = strlen(str);
memcpy(send_buf + index, str, len);
index += len;
len = strlen(SERVER);
memcpy(send_buf + index, SERVER, len);
index += len;
memcpy(send_buf + index, "\r\nDate:", 7);
index += 7;
char time_buf[TIME_BUFFER_SIZE];
memset(time_buf, '\0', sizeof(time_buf));
get_time_str(time_buf);
len = strlen(time_buf);
memcpy(send_buf + index, time_buf, len);
index += len;
str = "\r\nContent-Type:text/html\r\nContent-Length:";
len = strlen(str);
memcpy(send_buf + index, str, len);
index += len;
str = "\r\n\r\n404 File not found Please check your url,and try it again! ";
len = strlen(str);
int htmllen = len;
char num_len[5];
memset(num_len, '\0', sizeof(num_len));
sprintf(num_len, "%d", len);
len = strlen(num_len);
memcpy(send + index, num_len, len);
index += len;
memcpy(send_buf + index, str, htmllen);
index += htmllen;
break;
default:
break;
}
return index;
}
int reply_normal_information(unsigned char *send_buf, unsigned char *file_buf, int file_size, char *mime_type)
{
char *str = "HTTP/1.1 200 OK\r\nServer:Mutu/Linux(0.1)\r\nDate:";
register int index = strlen(str);
memcpy(send_buf, str, index);
char time_buf[TIME_BUFFER_SIZE];
memset(time_buf, '\0', sizeof(time_buf));
str = get_time_str(time_buf);
int len = strlen(time_buf);
memcpy(send_buf + index, time_buf, len);
index += len;
len = strlen(ALLOW);
memcpy(send_buf + index, ALLOW, len);
index += len;
memcpy(send_buf + index, "\r\nContent-Type:", 15);
index += 15;
len = strlen(mime_type);
memcpy(send_buf + index, mime_type, len);
index += strlen(mime_type);
memcpy(send_buf + index, "\r\nContent-Length:", 17);
index += 17;
char num_len[8];
memset(num_len, '\0', sizeof(num_len));
sprintf(num_len, "%d", file_size);
len = strlen(num_len);
memcpy(send_buf + index, num_len, len);
index += len;
memcpy(send_buf + index, "\r\n\r\n", 4);
index += 4;
memcpy(send_buf + index, file_buf, file_size);
index += file_size;
return index;
}
initsocket.c
/*
* file:init_socket.c
*/
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "init_socket.h"
int init_socket(int *listen_fd, struct sockaddr_in *server_addr)
{
if((*listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket() error. in init_socket.c");
return -1;
}
/* set reuse the port on server machine */
int opt = SO_REUSEADDR;
if(setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
{
perror("setsockopt() error. in init_socket.c");
return -1;
}
server_addr->sin_family = AF_INET;
server_addr->sin_port = htons(PORT);
server_addr->sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(*listen_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr_in)) == -1)
{
perror("bind() error. in init_socket.c");
return -1;
}
if(listen(*listen_fd, BACKLOG) == -1)
{
perror("listen() error. in init_socket.c");
return -1;
}
return 0;
}
webserver.c
/*
* file:webserver.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "get_time.h"
#include "init_socket.h"
#include "http_session.h"
int main(int argc, char *argv[])
{
int listen_fd;
int connect_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
bzero(&server_addr, sizeof(struct sockaddr_in));
bzero(&client_addr, sizeof(struct sockaddr_in));
if(init_socket(&listen_fd, &server_addr) == -1)
{
perror("init_socket() error. in webserver.c");
exit(EXIT_FAILURE);
}
socklen_t addrlen = sizeof(struct sockaddr_in);
pid_t pid;
while(1)
{
if((connect_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addrlen)) == -1)
{
perror("accept() error. in webserver.c");
continue;
}
if( (pid = fork()) > 0)
{
close(connect_fd);
continue;
}
else if(pid == 0)
{
close(listen_fd);
printf("pid %d process http session from %s : %d\n", getpid(), inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));
if(http_session(&connect_fd, &client_addr) == -1)
{
perror("http_session() error. in webserver.c");
shutdown(connect_fd, SHUT_RDWR);
printf("pid %d loss connection to %s\n", getpid(), inet_ntoa(client_addr.sin_addr));
exit(EXIT_FAILURE); /* exit from child process, stop this http session */
}
printf("pid %d close connection to %s\n", getpid(), inet_ntoa(client_addr.sin_addr));
shutdown(connect_fd, SHUT_RDWR);
exit(EXIT_SUCCESS);
}
else
{
perror("fork() error. in webserver.c");
exit(EXIT_FAILURE);
}
}
shutdown(listen_fd, SHUT_RDWR);
return 0;
}
实践截图:
server服务器请求截图:
这里我采用的是之前web课的实验作业作为这次实验的测试网页
可以多个浏览器同时浏览网页
网页跳转
将服务器部署到实验箱
因为暂时没有实验箱,所以这里使用树莓派进行代替,因为都是arm64架构,使用scp指令将文件传到树莓派中进行编译即可。
浏览器使用本机:
网页跳转: