幾種伺服器端IO模型的簡單介紹及實現
一些概念:
同步和非同步
同步和非同步是針對應用程式和核心的互動而言的,同步指的是使用者程序觸發I/O操作並等待或者輪詢的去檢視I/O操作是否就緒,而非同步是指使用者程序觸發I/O操作以後便開始做自己的事情,而當I/O操作已經完成的時候會得到I/O完成的通知。
阻塞和非阻塞
阻塞和非阻塞是針對於程序在訪問資料的時候,根據I/O操作的就緒狀態來採取的不同方式,說白了是一種讀取或者寫入操作函式的實現方式,阻塞方式下讀取或者寫入函式將一直等待,而非阻塞方式下,讀取或者寫入函式會立即返回一個狀態值。
伺服器端幾種模型:
1、阻塞式模型(blocking IO)
我們第一次接觸到的網路程式設計都是從 listen()、accpet()、send()、recv() 等介面開始的。使用這些介面可以很方便的構建C/S的模型。這裡大部分的 socket 介面都是阻塞型的。所謂阻塞型介面是指系統呼叫(一般是 IO 介面)不返回呼叫結果並讓當前執行緒一直阻塞,只有當該系統呼叫獲得結果或者超時出錯時才返回。
如下面一個簡單的Server端實現:
#include <Winsock2.h> #include <cstdio> #include <iostream> #include <string> using namespace std; #pragma comment(lib,"ws2_32.lib") int init_win_socket() { WSADATA wsaData; if(WSAStartup(MAKEWORD(2,2) , &wsaData ) != 0) {View Codereturn -1; } return 0; } #define Server_Port 10286 void handle_client(int newfd) { while(1) { char buff[1024]; memset(buff,0,1024); int result = recv(newfd,buff,1024,0); if(result <= 0) { break; } else{ printf("Receive Data %s, Size: %d \n",buff,result); int ret = send(newfd,buff,result,0); if(ret>0) { printf("Send Data %s, Size: %d \n",buff,ret); } else { break; } } } closesocket(newfd); return; } int run() { int listener; struct sockaddr_in addr_server; listener = socket(AF_INET, SOCK_STREAM, 0); //addr_server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); addr_server.sin_addr.S_un.S_addr = ADDR_ANY; addr_server.sin_family = AF_INET; addr_server.sin_port = htons(Server_Port); if(bind(listener,(const sockaddr *)&addr_server,sizeof(addr_server)) < 0) { perror("bind error"); return -1; } if (listen(listener, 10)<0) { perror("listen error"); return -1; } printf("Server is listening ... \n"); bool runing = true; while(runing) { sockaddr_in addr_client; int clientlen = sizeof(addr_client); int client_sock; if ((client_sock = accept(listener, (struct sockaddr *) &addr_client, &clientlen)) < 0) { printf("Failed to accept client connection \n"); } fprintf(stdout, "Client connected: %s \n", inet_ntoa(addr_client.sin_addr)); /*Handle this connect */ handle_client(client_sock); } closesocket(listener); return 0; } int main(int c, char **v) { #ifdef WIN32 init_win_socket(); #endif run(); getchar(); return 0; }
示意圖如下:
這裡的socket的介面是阻塞的(blocking),線上程被阻塞期間,執行緒將無法執行任何運算或響應任何的網路請求,這給多客戶機、多業務邏輯的網路程式設計帶來了挑戰。
2、多執行緒的伺服器模型(Multi-Thread)
應對多客戶機的網路應用,最簡單的解決方式是在伺服器端使用多執行緒(或多程序)。多執行緒(或多程序)的目的是讓每個連線都擁有獨立的執行緒(或程序),這樣任何一個連線的阻塞都不會影響其他的連線。
多執行緒Server端的實現:
#include <Winsock2.h> #include <cstdio> #include <iostream> #include <string> using namespace std; #pragma comment(lib,"ws2_32.lib") int init_win_socket() { WSADATA wsaData; if(WSAStartup(MAKEWORD(2,2) , &wsaData ) != 0) { return -1; } return 0; } #define Server_Port 10286 DWORD WINAPI handle_client(LPVOID lppara) { int *newfd = (int *)lppara; while(1) { char buff[1024]; memset(buff,0,1024); int result = recv(*newfd,buff,1024,0); if(result <= 0) { break; } else { printf("Receive Data %s, Size: %d \n",buff,result); int ret = send(*newfd,buff,result,0); if(ret>0) { printf("Send Data %s, Size: %d \n",buff,ret); } else { break; } } Sleep(10); } closesocket(*newfd); return 0; } int run() { int listener; struct sockaddr_in addr_server; int sock_clients[1024]; //max number for accept client connection; listener = socket(AF_INET, SOCK_STREAM, 0); //addr_server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); addr_server.sin_addr.S_un.S_addr = ADDR_ANY; addr_server.sin_family = AF_INET; addr_server.sin_port = htons(Server_Port); if(bind(listener,(const sockaddr *)&addr_server,sizeof(addr_server)) < 0) { perror("bind error"); return -1; } if (listen(listener, 10)<0) { perror("listen error"); return -1; } printf("Server is listening ... \n"); int fd_count = 0; bool runing = true; while(runing) { sockaddr_in addr_client; int clientlen = sizeof(addr_client); int client_sock; if ((client_sock = accept(listener, (struct sockaddr *) &addr_client, &clientlen)) < 0) { printf("Failed to accept client connection \n"); } fprintf(stdout, "Client connected: socket fd %d , %s \n", client_sock,inet_ntoa(addr_client.sin_addr)); /*Handle this connect */ if(fd_count<1024) { sock_clients[fd_count] = client_sock; if(CreateThread(NULL,0,handle_client,&sock_clients[fd_count],0,NULL)==NULL) return -1; ++ fd_count; } Sleep(10); } closesocket(listener); return 0; } int main(int c, char **v) { #ifdef WIN32 init_win_socket(); #endif run(); getchar(); return 0; }View Code
上述多執行緒的伺服器模型可以解決一些連線量不大的多客戶端連線請求,但是如果要同時響應成千上萬路的連線請求,則無論多執行緒還是多程序都會嚴重佔據系統資源,降低系統對外界響應效率。
在多執行緒的基礎上,可以考慮使用“執行緒池”或“連線池”,“執行緒池”旨在減少建立和銷燬執行緒的頻率,其維持一定合理數量的執行緒,並讓空閒的執行緒重新承擔新的執行任務。“連線池”維持連線的快取池,儘量重用已有的連線、減少建立和關閉連線的頻率。這兩種技術都可以很好的降低系統開銷,都被廣泛應用很多大型系統。
3、非阻塞式模型(Non-blocking IO)
非阻塞的介面相比於阻塞型介面的顯著差異在於,在被呼叫之後立即返回。
非阻塞型IO的示意圖如下:
從應用程式的角度來說,blocking read 呼叫會延續很長時間。在核心執行讀操作和其他工作時,應用程式會被阻塞。
非阻塞的IO可能並不會立即滿足,需要應用程式呼叫許多次來等待操作完成。這可能效率不高,因為在很多情況下,當核心執行這個命令時,應用程式必須要進行忙碌等待,直到資料可用為止。
另一個問題,在迴圈呼叫非阻塞IO的時候,將大幅度佔用CPU,所以一般使用select等來檢測”是否可以操作“。
4、多路複用IO
支援I/O複用的系統呼叫有select、poll、epoll、kqueue等,
這裡以Select函式為例,select函式用於探測多個檔案控制代碼的狀態變化,以下為一個使用了使用了Select函式的Server實現:
#include <Winsock2.h> #include <cstdio> #include <cstdlib> #include <cassert> #include <iostream> #include <string> using namespace std; #pragma comment(lib,"ws2_32.lib") int init_win_socket() { WSADATA wsaData; if(WSAStartup(MAKEWORD(2,2) , &wsaData ) != 0) { return -1; } return 0; } #define Server_Port 10286 #define MAX_LINE 16384 #define FD_SETSIZE 1024 struct fd_state { char buffer[MAX_LINE]; size_t buffer_used; int writing; size_t n_written; size_t write_upto; }; struct fd_state * alloc_fd_state(void) { struct fd_state *state = (struct fd_state *)malloc(sizeof(struct fd_state)); if (!state) return NULL; state->buffer_used = state->n_written = state->writing = state->write_upto = 0; memset(state->buffer,0,MAX_LINE); return state; } void free_fd_state(struct fd_state *state) { free(state); } int set_socket_nonblocking(int fd) { unsigned long mode = 1; int result = ioctlsocket(fd, FIONBIO, &mode); if (result != 0) { return -1; printf("ioctlsocket failed with error: %ld\n", result); } return 0; } int do_read(int fd, struct fd_state *state) { char buf[1024]; int i; int result; while (1) { memset(buf,0,1024); result = recv(fd, buf, sizeof(buf), 0); if (result <= 0) break; for (i=0; i < result; ++i) { if (state->buffer_used < sizeof(state->buffer)) state->buffer[state->buffer_used++] = buf[i]; } } state->writing = 1; state->write_upto = state->buffer_used; printf("Receive data: %s size: %d\n",state->buffer+state->n_written,state->write_upto-state->n_written); if (result == 0) { return 1; } else if (result < 0) { #ifdef WIN32 if (result == -1 && WSAGetLastError()==WSAEWOULDBLOCK) return 0; #else if (errno == EAGAIN) return 0; #endif return -1; } return 0; } int do_write(int fd, struct fd_state *state) { while (state->n_written < state->write_upto) { int result = send(fd, state->buffer + state->n_written, state->write_upto - state->n_written, 0); if (result < 0) { #ifdef WIN32 if (result == -1 && WSAGetLastError()==WSAEWOULDBLOCK) return 0; #else if (errno == EAGAIN) return 0; #endif return -1; } assert(result != 0); printf("Send data: %s \n",state->buffer+ state->n_written); state->n_written += result; } if (state->n_written == state->buffer_used) state->n_written = state->write_upto = state->buffer_used = 0; state->writing = 0; return 0; } void run() { int listener; struct fd_state *state[FD_SETSIZE]; struct sockaddr_in sin; int i, maxfd; fd_set readset, writeset, exset; sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; sin.sin_port = htons(Server_Port); for (i = 0; i < FD_SETSIZE; ++i) state[i] = NULL; listener = socket(AF_INET, SOCK_STREAM, 0); set_socket_nonblocking(listener); int one = 1; setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,(const char *)&one, sizeof(one)); if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) { perror("bind"); return; } if (listen(listener, 16)<0) { perror("listen"); return; } printf("Server is listening ... \n"); FD_ZERO(&readset); FD_ZERO(&writeset); FD_ZERO(&exset); while (1) { maxfd = listener; FD_ZERO(&readset); FD_ZERO(&writeset); FD_ZERO(&exset); FD_SET(listener, &readset); for (i=0; i < FD_SETSIZE; ++i) { if (state[i]) { if (i > maxfd) maxfd = i; FD_SET(i, &readset); if (state[i]->writing) { FD_SET(i, &writeset); } } } if (select(maxfd+1, &readset, &writeset, &exset, NULL) < 0) { perror("select"); return; } //check if listener can accept if (FD_ISSET(listener, &readset)) { struct sockaddr_in ss; int slen = sizeof(ss); int fd = accept(listener, (struct sockaddr*)&ss, &slen); if (fd < 0) { perror("accept"); } else if(fd > FD_SETSIZE) { closesocket(fd); } else { printf("Accept socket %d, address %s \n",fd,inet_ntoa(ss.sin_addr)); set_socket_nonblocking(fd); state[fd] = alloc_fd_state(); assert(state[fd]); } } //process read and write socket for (i=0; i < maxfd+1; ++i) { int r = 0; if (i == listener) continue; if (FD_ISSET(i, &readset)) { r = do_read(i, state[i]); } if (r == 0 && FD_ISSET(i, &writeset)) { r = do_write(i, state[i]); } if (r) { free_fd_state(state[i]); state[i] = NULL; closesocket(i); } } } } int main(int c, char **v) { #ifdef WIN32 init_win_socket(); #endif run(); return 0; }View Code
示意圖如下:
這裡Select監聽的socket都是Non-blocking的,所以在do_read() do_write()中對返回為EAGAIN/WSAEWOULDBLOCK都做了處理。
從程式碼中可以看出使用Select返回後,仍然需要輪訓再檢測每個socket的狀態(讀、寫),這樣的輪訓檢測在大量連線下也是效率不高的。因為當需要探測的控制代碼值較大時,select () 介面本身需要消耗大量時間去輪詢各個控制代碼。
很多作業系統提供了更為高效的介面,如 linux 提供 了 epoll,BSD 提供了 kqueue,Solaris 提供了 /dev/poll …。如果需要實現更高效的伺服器程式,類似 epoll 這樣的介面更被推薦。遺憾的是不同的作業系統特供的 epoll 介面有很大差異,所以使用類似於 epoll 的介面實現具有較好跨平臺能力的伺服器會比較困難。
5、使用事件驅動庫libevent的伺服器模型
Libevent 是一種高效能事件迴圈/事件驅動庫。
為了實際處理每個請求,libevent 庫提供一種事件機制,它作為底層網路後端的包裝器。事件系統讓為連線新增處理函式變得非常簡便,同時降低了底層IO複雜性。這是 libevent 系統的核心。
建立 libevent 伺服器的基本方法是,註冊當發生某一操作(比如接受來自客戶端的連線)時應該執行的函式,然後呼叫主事件迴圈 event_dispatch()。執行過程的控制現在由 libevent 系統處理。註冊事件和將呼叫的函式之後,事件系統開始自治;在應用程式執行時,可以在事件佇列中新增(註冊)或 刪除(取消註冊)事件。事件註冊非常方便,可以通過它新增新事件以處理新開啟的連線,從而構建靈活的網路處理系統。
使用Libevent實現的一個回顯伺服器如下:
#include <event2/event.h> #include <assert.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #define MAX_LINE 16384 void do_read(evutil_socket_t fd, short events, void *arg); void do_write(evutil_socket_t fd, short events, void *arg); struct fd_state { char buffer[MAX_LINE]; size_t buffer_used; size_t n_written; size_t write_upto; struct event *read_event; struct event *write_event; }; struct fd_state * alloc_fd_state(struct event_base *base, evutil_socket_t fd) { struct fd_state *state = (struct fd_state *)malloc(sizeof(struct fd_state)); if (!state) { return NULL; } state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state); if (!state->read_event) { free(state); return NULL; } state->write_event = event_new(base, fd, EV_WRITE, do_write, state); if (!state->write_event) { event_free(state->read_event); free(state); return NULL; } memset(state->buffer,0,MAX_LINE); state->buffer_used = state->n_written = state->write_upto = 0; return state; } void free_fd_state(struct fd_state *state) { event_free(state->read_event); event_free(state->write_event); free(state); } void do_read(evutil_socket_t fd, short events, void *arg) { struct fd_state *state = (struct fd_state *) arg; char buf[1024]; int i; int result; assert(state->write_event); while(1) { memset(buf,0,1024); result = recv(fd, buf, sizeof(buf), 0); if (result <= 0) { break; } else { for (i=0; i < result; ++i) { if (state->buffer_used < sizeof(state->buffer)) state->buffer[state->buffer_used++] = buf[i]; } } } printf("receive data: %s size: %d\n",state->buffer+state->n_written,state->write_upto-state->n_written); assert(state->write_event); event_add(state->write_event, NULL); state->write_upto = state->buffer_used; if (result == 0) { printf("connect closed \n"); free_fd_state(state); } else if (result < 0) { #ifdef WIN32 if (result == -1 && WSAGetLastError()==WSAEWOULDBLOCK) return; #else if (errno == EAGAIN) return; #endif perror("recv"); free_fd_state(state); } } void do_write(evutil_socket_t fd, short events, void *arg) { struct fd_state *state = (struct fd_state *)arg; while (state->n_written < state->write_upto) { int result = send(fd, state->buffer + state->n_written, state->write_upto - state->n_written, 0); if (result < 0) { #ifdef WIN32 if (result == -1 && WSAGetLastError()==WSAEWOULDBLOCK) return; #else if (errno == EAGAIN) return; #endif free_fd_state(state); return; } assert(result != 0); printf("send data: %s \n",state->buffer+ state->n_written); state->n_written += result; } //buffer is full if (state->n_written == state->buffer_used) { state->n_written = state->write_upto = state->buffer_used = 0; memset(state->buffer,0,MAX_LINE); } } void do_accept(evutil_socket_t listener, short event, void *arg) { struct event_base *base = (struct event_base *)arg; struct sockaddr_in ss; int slen = sizeof(ss); int fd = accept(listener, (struct sockaddr*)&ss, &slen); if (fd > 0) { printf("accept socket %d, address %s \n",fd,inet_ntoa(ss.sin_addr)); struct fd_state *state; evutil_make_socket_nonblocking(fd); state = alloc_fd_state(base, fd); assert(state); assert(state->read_event); event_add(state->read_event, NULL); } } void run() { int listener; struct sockaddr_in addr_server; struct event_base *base; struct event *listener_event; base = event_base_new(); if (!base) { perror("event_base_new error"); return; } addr_server.sin_addr.S_un.S_addr = ADDR_ANY; addr_server.sin_family = AF_INET; addr_server.sin_addr.s_addr = 0; addr_server.sin_port = htons(10286); listener = socket(AF_INET, SOCK_STREAM, 0); evutil_make_socket_nonblocking(listener); int one = 1; setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one)); if (bind(listener, (struct sockaddr*)&addr_server, sizeof(addr_server)) < 0) { perror("bind error"); return; } if (listen(listener, 10)<0) { perror("listen error"); return; } printf("server is listening ... \n"); listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base); event_add(listener_event, NULL); event_base_dispatch(base); } int init_win_socket() { WSADATA wsaData; if(WSAStartup(MAKEWORD(2,2) , &wsaData ) != 0) { return -1; } return 0; } int main(int c, char **v) { #ifdef WIN32 init_win_socket(); #endif run(); getchar(); return 0; }View Code
6、訊號驅動IO模型(Signal-driven IO)
使用訊號,讓核心在描述符就緒時傳送SIGIO訊號通知應用程式,稱這種模型為訊號驅動式I/O(signal-driven I/O)。
圖示如下:
首先開啟套接字的訊號驅動式I/O功能,並通過sigaction系統呼叫安裝一個訊號處理函式。該系統呼叫將立即返回,我們的程序繼續工作,也就是說程序沒有被阻塞。當資料報準備好讀取時,核心就為該程序產生一個SIGIO訊號。隨後就可以在訊號處理函式中呼叫recvfrom讀取資料報,並通知主迴圈資料已經準備好待處理,也可以立即通知主迴圈,讓它讀取資料報。
無論如何處理SIGIO訊號,這種模型的優勢在於等待資料報到達期間程序不被阻塞。主迴圈可以繼續執行 ,只要等到來自訊號處理函式的通知:既可以是資料已準備好被處理,也可以是資料報已準備好被讀取。
7、非同步IO模型(asynchronous IO)
非同步I/O(asynchronous I/O)由POSIX規範定義。演變成當前POSIX規範的各種早起標準所定義的實時函式中存在的差異已經取得一致。一般地說,這些函式的工作機制是:告知核心啟動某個操作,並讓核心在整個操作(包括將資料從核心複製到我們自己的緩衝區)完成後通知我們。這種模型與前一節介紹的訊號驅動模型的主要區別在於:訊號驅動式I/O是由核心通知我們何時可以啟動一個I/O操作,而非同步I/O模型是由核心通知我們I/O操作何時完成。
示意圖如下:
我們呼叫aio_read函式(POSIX非同步I/O函式以aio_或lio_開頭),給核心傳遞描述符、緩衝區指標、緩衝區大小(與read相同的三個引數)和檔案偏移(與lseek類似),並告訴核心當整個操作完成時如何通知我們。該系統呼叫立即返回,並且在等待I/O完成期間,我們的程序不被阻塞。本例子中我們假設要求核心在操作完成時產生某個訊號,該訊號直到資料已複製到應用程序緩衝區才產生,這一點不同於訊號驅動I/O模型。
參考:
《UNIX網路程式設計》
使用 libevent 和 libev 提高網路應用效能:http://www.ibm.com/developerworks/cn/aix/library/au-libev/
使用非同步 I/O 大大提高應用程式的效能:https://www.ibm.com/developerworks/cn/linux/l-async/
相關推薦
幾種伺服器端IO模型的簡單介紹及實現
一些概念: 同步和非同步 同步和非同步是針對應用程式和核心的互動而言的,同步指的是使用者程序觸發I/O操作並等待或者輪詢的去檢視I/O操作是否就緒,而非同步是指使用者程序觸發I/O操作以後便開始做自己的事情,而當I/O操作已經完成的時候會得到I/O完成的通知。 阻塞和非阻塞 阻塞和非阻塞是針對於程序在
]淺談幾種伺服器端模型——多執行緒併發式(執行緒池)
(如果不加以說明,我們都是考慮開發是基於GNU/Linux的)在Linux下建立一個執行緒的方式很簡單,pthread_create() 函式來建立執行緒,其中的一個引數的回撥函式,也就是執行緒本身的執行體函式。 ? 1 void *thread_e
netty學習(一)--linux下的網絡io模型簡單介紹
網絡協議 過程 content 結構體 了解 簡單 文件路徑 技術 連接 linux的內核將全部的外部設備都看作一個文件來操作,對一個文件的讀寫操作會調用內核提供的系統命令 ,返回一個file descriptor(fd。文件描寫敘述符)。而
網路 IO 模型簡單介紹
## 一、同步阻塞 IO(BIO) ![](https://img2020.cnblogs.com/blog/1153954/202012/1153954-20201216153138831-1730679968.png) 當用戶執行緒呼叫了 read 系統呼叫,核心(kernel)就開始了 IO 的第一個
KVM虛擬化的四種簡單網絡模型介紹及實現(一)
_for only 應該 code eth tun x86_64 信息 dock KVM中的四種簡單網絡模型,分別如下:1、隔離模型:虛擬機之間組建網絡,該模式無法與宿主機通信,無法與其他網絡通信,相當於虛擬機只是連接到一臺交換機上。2、路由模型:相當於虛擬機連接到一臺路由
KVM虛擬化的四種簡單網絡模型介紹及實現(二)
str drive 51cto -c water -a return dfa 模型 接上篇,介紹NAT網絡模型和橋接模型。 三、NAT模型 NAT模型其實就是SNAT的實現,路由中虛擬機能將報文發送給外部主機,但是外部主機因找不到通往虛擬機的路由因而無法回應請求。但是外部
微信公眾號 幾種移動端UI框架介紹
微信公眾號開發,主要是移動端網頁的頁面開發,在這裡推薦3個移動端UI框架:WeUI、SUI和Mint UI。1. WeUI1.1 WeUIWeUI是微信官方設計團隊為微信 Web 開發量身設計,可以令使用者在HTML5應用中的使用感知更加統一。元件包含button、cell、dialog、 progress,
幾個常用規則引擎的簡單介紹和演示
規則引擎 drools ilog odm Ilog JRules 是最有名的商用BRMS;Drools 是最活躍的開源規則引擎;Jess 是Clips的java實現,就如JRuby之於Ruby,是AI系的代表;Visual Rules(旗正規則引擎)國內商業規則引擎品牌。今天對比了一下這四個頗
Java並發編程:淺析幾種線程安全模型 [轉]
condition 神器 ans temp 簡單 nts 替換 line get() 多線程編程一直是老生常談的問題,在Java中,隨著JDK的逐漸發展,JDK提供給我們的並發模型也越來越多,本文摘取三例使用不同原理的模型,分析其大致原理。目錄如下: 1.COW之Copy
幾種實用型Ruby Web開發框架介紹
大家在通過對Ruby的學習後,都知道,Ruby on Rails是一款效能非常優越的Ruby Web開發框架。但是其他的Ruby Web開發框架又有多少人知道一二呢? 迅速掌握Ruby檔案鎖概念 Ruby操作檔案許可權技巧分享 Ruby will_paginate知識堂
TCP/IP四層模型簡單介紹
OSI七層參考模型 OSI(Open System Interconnection 開放系統互聯)參考模型,是國際標準化組織(ISO)制定的一個用於計算機或通訊系統間互聯的標準體系。 OSI分為七層 應用層:為使用者提供服務和協議 表示層:裝置固有資料格式和網路標準資料格式的轉換 會話層
Redis簡單介紹及5種資料型別的使用
Redis簡介 關於關係型資料庫和nosql(not only sql)資料庫(泛指非關係型資料庫) 關係型資料庫是基於關係表的資料庫,最終會將資料持久化到磁碟上,而nosql(不僅僅是資料庫)資料庫是基於特殊的結構,並將資料儲存到記憶體的資料庫。從效能上而言,nosql資料庫要
設計模式之3種工廠模式對比和簡單介紹
一、簡單工廠模式 模式定義 簡單工廠模式又稱靜態工廠模式。可以根據引數的不同返回不同類的例項。定義一個類來建立其它類的例項。 模式角色 簡單工廠模式包含如下角色 Factory:工廠角色 Product:抽象產品角色 ConcreteProdu
【Three.js:3D模型】各種模型簡單介紹
轉載:3D模型彙總----骨骼模型 3D模型的格式有很多,每個公司或軟體都可以自己定義自己的格式,或公開或不公開。如max,obj, x(微軟),fbx(被Autodesk收購),dae,md2(Quake2),ms3d(MilkShap3D),mdl(魔獸3
對幾種常見卷積的簡單理解
看了一些關於CNN中常見卷積方法的介紹,簡單做一些筆記,詳細介紹可參考文末的連結。 深度可分離卷積(Depthwise separable convolution) 思想:先逐通道地(depthwise)對輸入資料體空間域做卷積,再沿著通道方向做1*1卷積進行通道融合 好處:
伺服器端WSAAsyncSelect模型程式設計
WSAAsyncSelect是伺服器端的六種I/O模型之一,他的主要思想是運用了windows視窗的訊息機制,用函式WSAAsyncSelect()將監聽埠感興趣的網路訊息註冊到視窗,然後在視窗的訊息過程中處理,該模型只提供非同步通知,並不提供非同步資料傳送,只適用於系統開
幾種常見軟體過程模型的比較
瀑布模型 瀑布模型(經典生命週期)提出了軟體開發的系統化的、順序的方法。其流 程從使用者需求規格說明開始,通過策劃、建模、構建和部署的過程,最終提供一 個完整的軟體並提供持續的技術支援。 優點: 1. 強調開發的階段性,各階段具有順序性和依賴性 2. 強調早期調
java使用UDP來進行客戶端和伺服器端通訊的簡單例子
1:客戶端程式 package com.pb.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.Unk
James郵件伺服器簡單介紹及配置傳送外網郵件
因為微貿網使用者已經超過3萬個使用者,需要每天給使用者推薦訂閱了相關關鍵詞的詢盤。剛開是使用了第三方的郵箱進行傳送,有限制而且不能那個多發,所以就想到了要搭建自己的郵箱伺服器。摸索了兩天終於搞定了。廢話不多說,進入正題。 一、簡介Apache James(Java Ap
實現Android客戶端與Eclipse伺服器端基於Okthhp簡單通訊
最近在重溫知識,所以藉此機會也想把自己寫的一些心得寫出來供大家分享,寫的有誤或者不好的地方望大家見諒,好了,廢話少說,直接進入正題,下面給大家介紹的就是基於目前主流網路通訊框架的okhttp實現的Android與Eclipse通訊。首先說明:我用的Android客戶端是And