C实现WEB服务器端的通讯接收

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

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

经过在服务器端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()函数进行数据包的解析和操做反馈。为了可以试程序的效率运行的更好,对于并发的处理可以更好,做者使用了线程池的方法来处理对于数据包处理函数的调用。代码能够见以下: apache


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:39
GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8082
Connection: keep-alive
Accept: */*
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.4
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-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.html
hello 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..... socket


详细代码见附件。 分布式

http://dl.vmall.com/c0h39lv1hn

相关文章
相关标签/搜索