博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C实现WEB服务器端的通信接收
阅读量:6176 次
发布时间:2019-06-21

本文共 6471 字,大约阅读时间需要 21 分钟。

  hot3.png

本文是《Mina 实现自定义协议的通信》服务器端的解释和扩充。

服务器端代码在不使用第三方开源或者商业的代码库前提下,通过C语言自定义实现了WEB服务器端的数据接收和通信。可以简单的理解为TOMCAT的简单版本。

通过在服务器端SOCKET的监听绑定,监听了服务器的REQUEST,见代码如下:

struct sockaddr_in server_addr;	bzero(&server_addr, sizeof(server_addr));	server_addr.sin_family = AF_INET;	server_addr.sin_addr.s_addr = htons(INADDR_ANY);	server_addr.sin_port = htons(SEVERPORT);	int server_socket = socket(PF_INET, SOCK_STREAM, 0);	if (server_socket < 0){		printf("create socket failed!\n");		exit(1);	}/*	int opt = 1;	setsocketopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));*/	if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))){		printf("server bind port: %d failed!\n", SEVERPORT);		exit(1);	}	if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)){		printf("server listen failed!\n");		exit(1);	}
然后通过一个简单的while(1)死循环不断的访问监听服务器端的端口,代码如下:

while(1){		int i;		struct sockaddr_in client_addr;		socklen_t length = sizeof(client_addr);		int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);		if (new_server_socket < 0){			printf("server accept failed!\n");			break;		}		char buffer[BUFFER_SIZE];		bzero(buffer, BUFFER_SIZE);		length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);		if (length < 0){			printf("server receive data failed!\n");		}		printf("----package begin----");		printf("client INFO:%s ", inet_ntoa(client_addr.sin_addr));		time_t t = time(NULL);		struct tm *local = localtime(&t);		printf("hour:%d minute:%d second:%d\n", local->tm_hour, local->tm_min, local->tm_sec);/*		for (i = 0; i < BUFFER_SIZE; i++){			printf("%d ", (unsigned char)buffer[i]);		}*/		printf("----package end----\n");//		handler_request(buffer, new_server_socket);		struct comdata cd;		int k;		for(k = 0; k < BUFFER_SIZE; k++)			cd.buffer[k] = buffer[k];		cd.socket_server = new_server_socket;		pool_add_worker(handler_request, &cd);//		close(new_server_socket);	}

其中当数据包被监听到的时候,就会调用handler_request()函数进行数据包的解析和操作反馈。为了能够试程序的效率运行的更好,对于并发的处理能够更好,作者使用了线程池的方法来处理对于数据包处理函数的调用。代码可以见如下:

pool_add_worker(handler_request, &cd);
其中线程池的初始化代码如下:

pool_init(POOL_MAX_NUMBER);
线程池的详细代码和解释放在下一篇文章中,或者直接参照源码。

继续回到正题。对于handler_request()函数的详细代码如下:

void *handler_request(void *arg){	char *request = (*(struct comdata *)arg).buffer;	int socket_server = (*(struct comdata *)arg).socket_server;//	splice_file_request(request, socket_server);	char command[BUFFER_SIZE];	char arguments[BUFFER_SIZE];	if (sscanf(request, "%s%s", command, arguments) != 2){		splice_file_request(request, socket_server);		close(socket_server);		return NULL;	}	printf("handler_cmd: %s\n", command);	printf("handler_path: %s\n", arguments);	if (strcmp(command, "GET") == 0 || strcmp(command, "POST") == 0)		right_request(request, command, arguments, socket_server);	else		wrong_request(socket_server);	close(socket_server);	return NULL;}
先看一段简单的HTTP访问数据包如下:

----package begin----client INFO:127.0.0.1 hour:11 minute:3 second:39GET /favicon.ico HTTP/1.1Host: 127.0.0.1:8082Connection: keep-aliveAccept: */*User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.4 (KHTML, like Gecko) Ubuntu/12.10 Chromium/22.0.1229.94 Chrome/22.0.1229.94 Safari/537.4Accept-Encoding: gzip,deflate,sdchAccept-Language: en-US,en;q=0.8Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3----package end----

这段数据包的request是想要GET 文件/favicon.ico。

一般HTTP的访问方式有GET POST 等。只需要简单的解析一下就可以作出对数据REQUEST的RESPONSE。其中对于GET和POST的response函数代码如下:

void right_request(char *request, char *command, char *arguments, int socket_server){	char *head = "HTTP/1.0 200 OK\r\n";	int len = strlen(head);	if (sendall(socket_server, head, &len) == -1){		printf("sending failed!\n");		return;	}	char *content_type = "Content-type: text/plain\r\n";	len = strlen(content_type);	if (sendall(socket_server, content_type, &len) == -1){		printf("sending failed!\n");		return;	}	char *end = "\r\n";	len = strlen(end);	if (sendall(socket_server, end, &len) == -1){		printf("sending failed!\n");		return;	}	char info[BUFFER_SIZE];	strcat(info, "return: ");	strcat(info, command);	strcat(info, arguments);	strcat(info, "\r\nhello world!\r\n");	len = strlen(info);	if (sendall(socket_server, info, &len) == -1){		printf("sending failed!\n");		return;	}}
其访问的最后效果为 浏览器访问127.0.0.1:8082/index.html 结果返回为:

return: GET/index.htmlhello world!

同时支持自定义数据包的接收,其数据的包格式见文章《Mina 实现自定义协议的通信》,其数据格式简单图如下(java版本 ):

package com.a2.desktop.example5.mina.potocol;import org.apache.mina.core.buffer.IoBuffer;/** * 通信协议 * @author Chen.Hui *  */public abstract class AbsMessage {		/**	 * 协议格式:	 * 	 * tag | header length | Filename | File length | offset | checksum | temps | data	 * 	 */	/** 请求或访问类型 请求Tag:0x00 返回Tag:0x01 共 8 bit */	public abstract byte getTag();	/** 头文件长度 共 2^16 可表示 65535 */	public abstract short getHeaderlen();	/** 根据UUID生成文件唯一标识,共 8*36=288 bit */	public abstract byte[] getFilename();//需要設計一個算法	/** 获取文件长度 2^32=4GB 共 32 bit */	public abstract int getFileLen();	/** 获取文件的偏移量offset 共 32 bit */	public abstract int getOffset();	/** 获取文件的MD5校验码 共 32 bit */	public abstract byte[] getChecksum();	/** 预留字段 长度不超过 128 bit */	public abstract byte[] getTmp();		/**data 方式传输内容 不超过1024bit*/	public abstract IoBuffer getData();}
其C端服务器数据包的解析代码如下:

void splice_file_request(char *request, int socket_server){	struct message_head tmp;	char data[DATA_SIZE];	int i;	bzero(&data, sizeof(data));	bzero(&tmp, sizeof(tmp));	tmp.tag = request[0];	tmp.head_length = (unsigned char)request[2]+(unsigned char)request[1]*256;	memcpy(&tmp.file_name, &(request[3]), 36);	tmp.file_name[36]='\0';	tmp.file_length = (unsigned char)request[39]*256*256*256+(unsigned char)request[40]*256*256+(unsigned char)request[41]*256+(unsigned char)request[42];	tmp.offset = (unsigned char)request[43]*256*256*256+(unsigned char)request[44]*256*256+(unsigned char)request[45]*256+(unsigned char)request[46];	memcpy(&tmp.checksum, &(request[47]), 4);	memcpy(&tmp.tmp, &(request[51]), 4);	memcpy(&data, &(request[tmp.head_length]), DATA_SIZE);	for(i = 0; i < DATA_SIZE; i++){//		printf("%2x ", (unsigned char)data[i]);		if (i != 0 && i % 20 == 0)			printf("\n");		printf("%2x ", (unsigned char)data[i]);	}	printf("\n--head--\n");	printf("tag:%d head_length:%d file_name:%s file_length:%d offset:%d checksum:%s\n", tmp.tag, tmp.head_length, tmp.file_name, tmp.file_length, tmp.offset, tmp.checksum);	printf("--head--\n");}

作者只是实现到data数据的获取和显示,然后作者会将数据进行整合和拼接。然后会处理虚拟文件的管理,以及分布式文件的处理,服务器的负载均衡等问题。

Comming soon.....

详细代码见附件。

http://dl.vmall.com/c0h39lv1hn

转载于:https://my.oschina.net/u/860079/blog/86236

你可能感兴趣的文章
spring mybatis 批量插入返回主键
查看>>
指针函数小用
查看>>
开源力量公开课第二十三期-从SVN到Git,次时代代码管理
查看>>
输入挂
查看>>
升级迁移前,存储过程统计各个用户下表的数据量,和迁移后的比对
查看>>
sql注入分类
查看>>
初识CSS选择器版本4
查看>>
[Hadoop in China 2011] 朱会灿:探析腾讯Typhoon云计算平台
查看>>
JavaScript之数组学习
查看>>
PHP 设置响应头来解决跨域问题
查看>>
CAS实现SSO单点登录原理
查看>>
博客园美化专用图片链接
查看>>
HDU_1969_二分
查看>>
高等代数葵花宝典—白皮书
查看>>
一种简单的图像修复方法
查看>>
基于DobboX的SOA服务集群搭建
查看>>
C#设计模式之装饰者
查看>>
[noip模拟20170921]模版题
查看>>
获取ip
查看>>
Spring Shell简单应用
查看>>