MyTomCat服务器示例

博客围绕MyCatCat服务器展开,提供了相关示例,但具体内容未给出。推测可能涉及服务器的配置、使用等信息技术方面的关键信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/sendfile.h>
#include <signal.h>

#define MAX 1024
//根据TCP服务流程取得套接字与客户端的套接字与客户端的文件描述符
//根据客户端的文件描述符将客户端的响应报头取出来
//根据客户端的文件响应描述符将报文分离并取出报头
int getLine(int sock, char line[], int num)//获得报文中的一行,将第一行信息保存在line[]数组中
{
	char c = 'x';
	int i = 0;
	while(c != '\n' && i < num - 1){
		ssize_t s = recv(sock, &c, 1, 0);//recv函数只看一下此文件的一个字符并不将其拿出来,将看到的字符放入c中
		if(s > 0){
			//\r\n, \n, \r -> \n
			if(c == '\r'){ // \r\n, \r        //如果看到的是  \r 替换成\n  \n不变  \r\n
				recv(sock, &c, 1, MSG_PEEK);
				if(c == '\n'){
					recv(sock, &c, 1, 0);
				}
				else{
					c = '\n';
				}
			} //\n

			line[i++] = c;
		}
		else{
			break;
		}
	}
	line[i] = 0;
	return i;
}

void clearHeader(int sock)//明却报头   即:获得空行
{
	char line[MAX];
	do{
		getLine(sock, line, MAX);         
	}while(strcmp(line, "\n") != 0);
}

void echoError(int sock, int statusCode)
{
	switch(statusCode){
		case 404:
		//	show_404();
			break;
		default:
			break;
	}
}

int echoHtml(int sock, char path[], int size)
{
	//通过他的第一行就直接可以知道他要访问那个资源
	char line[MAX];
	clearHeader(sock);
	int fd = open(path, O_RDONLY);//open函数打开指定的文件,只读方式打开
	if(fd < 0){
		return 404;
	}

	char *stuff = path + strlen(path) - 1;
	while(*stuff != '.'){
		stuff--;
	}
	//一个响应报文发两次给TCP,其实TCP是面向字节流,所以不管你分几次,TCP都不管,只交给http来解释
	sprintf(line, "http/1.0 200 OK\r\n");
	send(sock, line, strlen(line), 0);//发送数据
	if(0 == strcmp(stuff, ".html")){
		sprintf(line, "Content-Type: text/html\r\n");
	}
	// else if(0 == strcmp(stuff, ".css")){
	// 	sprintf(line, "Content-Type: text/css\r\n");
	// }else if(0 == strcmp(stuff, ".js")){
	// 	sprintf(line, "Content-Type: application/x-javascript\r\n");
	// }else{
	// 	sprintf(line, "Content-Type: text/html\r\n");
	// }
	send(sock, line, strlen(line), 0);

	sprintf(line, "Content-Length: %d\r\n", size);//size获得文件大小
	send(sock, line, strlen(line), 0);
	sprintf(line, "\r\n");
	send(sock, line, strlen(line), 0);
	//打开一个文件,用while循环读多少写多少  可以  但是太搓了
	//用sendfile()函数,将文件的描述符给socket让socket自己来决定咋调咋读
	//从fd里将数据读出来然后发送给sock
	sendfile(sock, fd, NULL, size);

	close(fd);
}

int exeCgi(int sock, char *method, char *path, char *query_string)
{
	char line[MAX];
	int content_length = -1;
	if(strcmp(method, "GET") == 0){
		clearHeader(sock);
	}else{
		do{
			getLine(sock, line, MAX);
			if(strncmp(line, "Content-Length: ", 16) == 0){
				content_length = atoi(line + 16);
			}
		}while(strcmp(line, "\n") != 0);
		if(content_length == -1){
			return 404;
		}
	}

	///////////////////////////////////////
	
}

void *handlerRequest(void *arg)//真正对客户进行服务的函数接口,args是接收的客户端申请的文件描述符
{//http报文第一行   请求方法   请求的资源     请求的版本信息
	int sock = (int)arg;
	char line[MAX];//根据http报文形式来获得第一行信息
	char method[MAX/16];   //用来存储请求方法的字段
	char url[MAX];    //
	char path[MAX];   //
	int i = 0;
	int j = 0;
	int statusCode = 200;//状态码
	int cgi = 0;
	char *query_string = NULL;

	getLine(sock, line, MAX);//获得报文的第一行的信息    将请求方法分离出来

	while(i < sizeof(method)-1 && j < sizeof(line) && !isspace(line[j])){
		method[i] = line[j];           //获得第一个字段即:方法字段   将这个字段存储进method数组里
		i++, j++;
	}
	method[i] = '\0';              

	if(strcmp(method, "GET") == 0){       //如果是get方法  不处理
	}
	else if(strcmp(method, "POST") == 0){
		cgi = 1;
	}
	else{                          //如果不是POST也不是GET那么处理不了报错并结束处理
		clearHeader(sock);          //把头部清掉
		statusCode = 404;           //
		goto end;
	}

	while(j < sizeof(line) && isspace(line[j])){//让j指针获得URL字段
		j++;
	}

	i = 0;
	while(i < sizeof(url) - 1 && j < sizeof(line) && !isspace(line[j])){
		url[i] = line[j];                   //url字段保存在url数组中
		i++, j++;
	}
	url[i] = '\0';
//走到这里说明他就是GET方法或者POST方法
//上传数据   POST  GET   方法都能传参  参数位置不同   服务器要处理的数据cgi  GET方法传参需要cgi  POST方法都需要cgi
//去除掉url中的cji
	printf("method: %s, url: %s\n", method, url);//打印出此次方法与URL字段
//URL字段即是要访问的网址
	if(strcmp(method, "GET") == 0){
		query_string = url;
		while(*query_string != '\0'){
			if(*query_string == '?'){//获取到? 那么后面的就不是路径是带参的get方法的参数
				cgi = 1;
				*query_string = '\0'; // /a/b/c.html\0a=100&b=200
				query_string++;
				break;
			}
			query_string++;
		}
	}

	//method, url, query_string
	//根据URL将需要访问的目录加上web放在path数组中
	//从当前的web服务器目录下找
	sprintf(path, "web%s", url); //path表示从当前目录下的web目录下找
	if(path[strlen(path)-1] == '/'){//当别人只给一个斜杠,你需要把首页给人家
		strcat(path, "index.html");//在path里放入index.html
	}
	//将需要的资源目录已经准备好了
	//一个命令   stat  命令 可以获得文件的大小,文件名称
	//stat()   系统调用函数
	struct stat st;    
	//获得文件属性信息
	//stat接口用来判断
	if(stat(path, &st) < 0){
		//文件不存在
		clearHeader(sock);
		statusCode = 404;
		goto end;
	}
	else{
		//path结尾一定不带‘\’
		if(S_ISDIR(st.st_mode)){//判断它是不是目录
			strcat(path, "/index.html");//给它一个欢迎界面
		}

		//判断是否是cgi
		//如果是cgi
		if(cgi){
			statusCode = exeCgi(sock, method, path, query_string);
		}
		else{
			//如果不是cgi直接给他响应一个path资源
			statusCode = echoHtml(sock, path, st.st_size);
		}
	}

end:
	if(statusCode != 200){
		echoError(sock, statusCode);//如果是错误码那么就返回一个错误信息的页面
	}                               //因为sock是全双工的那么就需要从socket里面度,写也往socket里面写

	close(sock);
}

int startup(char *ip, int port)
{
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if(sock < 0){
		perror("socket");
		exit(2);
	}

	int opt = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	struct sockaddr_in local;
	local.sin_family = AF_INET;
	local.sin_addr.s_addr = inet_addr(ip);
	local.sin_port = htons(port);
    
    int len=sizeof(local)

	if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0){
		perror("bind");
		exit(3);
	}

	if(listen(sock, 5) < 0){
		perror("listen");
		exit(4);
	}

	return sock;
}

void usage(const char *proc)
{
	printf("Usage: %s [ip] [port]\n", proc);
}

// ./myTomcat ip 8080
int main(int argc, char *argv[])
{
	if(argc != 3){
		usage(argv[0]);
		return 1;
	}
	signal(SIGPIPE, SIG_IGN);
	int listen_sock = startup(argv[1], atoi(argv[2])); //ip, port//根据ip和udp来获得监听文件

	for(;;){
		struct sockaddr_in peer;                       //获得客户端的套接字
		socklen_t len = sizeof(peer);
		int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);//接收客户端申请
		
		if(sock < 0){
			continue;
		}
		printf("get a new link... create thread handler!\n");//获得一个新连接
		pthread_t tid;
		pthread_create(&tid, NULL, handlerRequest, (void *)sock);//用多线程来服务此次申请
		pthread_detach(tid);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值