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

相關文章
相關標籤/搜索