HTTP(0)-并发服务器

1.客户端请求+服务器响应协议说明及流程
2.服务器接收http请求
3.服务器解析http请求
4.服务器响应http请求
5.并发处理


1.客户端请求+服务器响应

流程如下:


本质上就是读取字节流,解析字节流的信息,得到文件名,在服务器上找到文件,然后再按格式发出去。

注意:字段名: 后面是有空格的。请求头部结束是有回车换行的。

1.主函数

#include<winsock.h>  //sockaddr_in
#include<socketapi.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>

#define SERVER_PORT 80
typedef int socklen_t;

static int debug = 0;
int get_line(int sock,char *buf,int size);
void* do_http_request(void* arg);
void do_http_response(int client_sock,const char *path);
void headers(int client_sock, FILE * resource);
void cat(int client_sock, FILE* resource);

//异常处理
void bad_request(int client_sock);//400
void unimplemented(int client_sock);//501
void not_found(int client_sock);




int httpmain(void)
{
	//1.创建连接
	int sock;
	struct sockaddr_in server_addr;
	sock = socket(AF_INET,SOCK_STREAM,0);
	//bzero(&server_addr, sizeof(server_addr));//清空标签
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(SERVER_PORT);
	bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
	listen(sock, 128);

	int done = 1;

	//2.解析http
	while (done)
	{
		struct sockaddr_in client;
		int client_sock, len, i;
		char client_ip[64];
		char buf[256];
		socklen_t client_addr_len;

		sock = socket(AF_INET, SOCK_STREAM, 0);
		//bzero(&server_addr, sizeof(server_addr));//清空标签
		server_addr.sin_family = AF_INET;
		server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		server_addr.sin_port = htons(SERVER_PORT);

		//打印客户端IP地址和端口号
		/*printf("client ip:%s\t port:%d\n",
			inet_ntop(AF_INET,&client.sin_addr,client_ip,sizeof(client_ip)),
			ntohs(client.sin_port));
		//读取客户端发送的数据
		len = read(client_sock, buf, sizeof(buf) - 1);
		buf[len] = '\0';

		//转换成大写
		for (i = 0; i < len; i++)
		{
			buf[i] = toupper(buf[i]);
		}
		len = write(client_sock, buf, len);
		printf("finished len:%d\n", len);
		close(client_sock);*/

		int* pclient_sock = NULL;
		//启动线程处理http请求
		pclient_sock = (int*)malloc(sizeof(int));
		*pclient_sock = client_sock;
		pthread_create(&id, NULL, do_http_request, pclient_sock);
		close(client_sock);

	 }
	close(sock);
	return 0;


}

3.服务器接收http请求
  3.1读取每一行  int get_line(int sock, char* buf, int size)
 

//读取每一行:返回值:-1:读取出错;0:读空行;>0:成功读取
int get_line(int sock, char* buf, int size) {
	int count = 0;
	char ch = '\0';
	int len = 0;
	while ((count<size-1)&&ch!='\n')
	{
		len = read(sock, &ch, 1);
		if (len == 1)//如果读取正常
		{
			if (ch == '\r')//如果是回车
			{
				continue;
			}
			else if (ch == '\n')//如果是换行
			{
				break;
			}
			buf[count] = ch;
			count++;
		}
		else if(len==-1)//如果读取异常
		{
			count = -1;
			break;
		}
	}
	if (count >= 0)buf[count] = '\0';
	return count;
}


3.2//解析请求:GET url 文件  void* do_http_request(void* arg);
如果碰到2个连续的回车换行即意味着请求头部结束了。




void* do_http_request(void* arg) {
	int len = 0;
	char buf[256];
	char method[64];
	char url[256];
	char path[256];
	struct stat st;
	int client_sock = *(int*)arg;                              //为了被线程调用,必须要改成指针
     
	len = get_line(client_sock, buf, sizeof(buf));              //1.读取请求行
	if (len > 0)
	{
		int i = 0, j = 0;
		while (!isspace(buf[j]) && (i < sizeof(method) - 1))
		{
			method[i] = buf[j];
			i++;
			j++;
		}
		method[i] = '\0';
		if (debug) printf("request method:%s\n", method);
		if (strncasecmp(method,"GET",i)==0)                     //2.GET方法      strncasecmp:比较参数s1和s2字符串前n个字符
		{
			if (debug) printf("method=GET\n");
			while (isspace(buf[j++]));                                         // isspace跳过白空格
			i = 0;

			while (!isspace(buf[j]) && (i < sizeof(url) - 1))   //3.得到URL
			{
				url[i] = buf[j];
				i++;
				j++;
			}
			url[i]= '\0';
			if (debug) printf("url:%s\n", url);          
			
			char* pos = strchr(url, "?");                    //4.获取文件   4.1截取 ?前的路径
			if (pos)
			{
				*pos = '\0';
				printf("real url: %s\n",url);                    //index.html ->./html_docs/index.html
			}
			sprintf(path, "./html_doc%s", url);                 //定位 只能截取一个/    path就是主目录下的全文件路径
			if (path[strlen(path)-1]=='/')                      //如果只有/那么就显示index.html
			{
				strcat(path, "index.html");
			}
			if (debug) printf("path:%s\n", path);

			do {                                              //5.不断的读
				len = get_line(client_sock, buf, sizeof(buf));
			} while (len > 0);

			if (stat(path, $st) == -1)                         //6.判断文件是否存在
			{
				not_found(client_sock);
			}
			else
			{
				if (S_ISDOR(st.st_mode))                    //  文件存在          S_ISDOR
				{
					strcat(path, "/index.html");
				}

				do_http_response(client_sock, path);            //7.响应
			}
		}
		else {
			bad_request(client_sock);                          //8.出错处理
		}
		if (arg) free(arg);
		close(client_sock);
		return NULL;
	}




}

 

   

4.服务器响应http请求

  

void do_http_response(int client_sock,const char *path);
void headers(int client_sock, FILE * resource);
void cat(int client_sock, FILE* resource);

//响应:把文件发出去
void do_http_response(int client_sock, const char* path){
	FILE* resource = NULL;
	resource = fopen(path, "r");
	if (resource == NULL) {
		not_found(client_sock);
	}
	else
	{
		headers(client_sock, resource);
		cat(client_sock, resource);
	}
	fclose(resource);
	sleep(20);
}
void headers(int client_sock, FILE* resource){
	char buf[1024];
	char tmp[64];
	int fileid = 0;
	struct stat st;

	strcpy(buf,"HTTP/1.0 200 OK\r\n");
	strcpy(buf, "Server: Martin Server\r\n");
	strcpy(buf, "Content-Type text/html\r\n");
	strcpy(buf, "Connection:Close\r\n");

	fileid = fileno(resource);
	if (fstat(fileid, &st) == -1) {
		inner_error(client_sock);
	}
	snprintf(tmp,64,"Content-Length: %ld\r\n\r\n",st.st_size);
	strcat(buf, tmp);
	if (send(client_sock, buf, strlen(buf), 0) < 0)
	{
		fprintf(stderr,"send failed %s, %s\n",buf,strerror(errno));
	}


}

void cat(int client_sock, FILE* resource){
	char buf[1024];
	fgets(buf, sizeof(buf), resource);
	while (!feof(resource))
	{
		int len = write(client_sock, buf, strlen(buf));
		if (len > 0)printf("%s", buf);
		fgets(buf, sizeof(buf), resource);
	}
}

发的内容就是字符串  header+body.

  截取?之前的字符,得到文件名。

   
判断文件是否存在于服务器上 ,用struct stat st;

 如果不存在 



 
内部错误

  

 5.并发
就是用线程处理http的请求以下为注意点:

1.添加线程指针       int *pclient_sock=NULL;
2.函数返回值改成指针   void * do_http_request(void * pclient_sock)
3.函数传递的参数改成指针
void * do_http_request(void * pclient_sock)
{
int client_sock=*(int *)pclient_sock;
close(client_sock);
if(pclient_sock) free(pclient_sock);//释放动态分配的内存
return NULL;

}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值