本文是《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