windows下 Libevent +多執行緒 實現檔案傳輸
阿新 • • 發佈:2018-12-20
1、模式:來一個客戶端連線進來,服務端就開啟一個處理執行緒。
2、缺點:對大量的客戶端情況不適用。大量客戶端的情況需要加入執行緒管理機制。
// LibeventTest.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include "winsock2.h" #include <process.h> #include "event2/listener.h" #include "event2/bufferevent.h" #include "BufferManager.h" typedef struct PictureInfo { char szFileName[260]; long nFileSize; } PICTUREINFO; //讀緩衝去回撥函式 void read_cb(struct bufferevent *bev, void *arg) { BufferManager* bm = (BufferManager*)arg; if (bm->nFileSize == 0) { int nReceived = bufferevent_read(bev, bm->buf + bm->nReceiveTotal,10000); if (nReceived >= sizeof(PICTUREINFO)) { bm->nFileSize = ((PICTUREINFO*)bm->buf)->nFileSize; strcpy_s(bm->szImgName,sizeof(bm->szImgName),((PICTUREINFO*)bm->buf)->szFileName); } bm->nReceiveTotal += nReceived; if (bm->nFileSize == bm->nReceiveTotal) { bm->f = NULL; fopen_s(&bm->f,bm->szImgName,"wb"); if (bm->f) { if (fwrite(bm->buf+sizeof(PICTUREINFO),bm->nReceiveTotal - sizeof(PICTUREINFO),1,bm->f) < 1){ // write error } fclose(bm->f); bm->f = NULL; }else{ // open file error } bm->iniParam(); } } else if ((bm->nFileSize - bm->nReceiveTotal) >= 10000) { int nReceived = bufferevent_read(bev, bm->buf + bm->nReceiveTotal,10000); bm->nReceiveTotal += nReceived; if (bm->nFileSize == bm->nReceiveTotal) { bm->f = NULL; fopen_s(&bm->f,bm->szImgName,"wb"); if (bm->f) { if (fwrite(bm->buf+sizeof(PICTUREINFO),bm->nReceiveTotal - sizeof(PICTUREINFO),1,bm->f) < 1){ // write error } fclose(bm->f); bm->f = NULL; }else{ // open file error } bm->iniParam(); } } else if((bm->nFileSize - bm->nReceiveTotal) >= 0) { int nReceived = bufferevent_read(bev, bm->buf + bm->nReceiveTotal, bm->nFileSize-bm->nReceiveTotal); bm->nReceiveTotal += nReceived; if (bm->nFileSize == bm->nReceiveTotal) { bm->f = NULL; fopen_s(&bm->f,bm->szImgName,"wb"); if (bm->f) { if (fwrite(bm->buf+sizeof(PICTUREINFO),bm->nReceiveTotal - sizeof(PICTUREINFO),1,bm->f) < 1){ // write error } fclose(bm->f); bm->f = NULL; }else{ // open file error } bm->iniParam(); } } printf("收到的位元組數:%d",bm->nReceiveTotal); } //寫緩衝區回撥函式 void write_cb(struct bufferevent *bev, void *arg) { printf("成功寫資料給客戶端,寫緩衝區回撥函式被回撥.\n"); } //事件回撥函式 void event_cb(struct bufferevent *bev,short events, void *arg) { if (events & BEV_EVENT_EOF) { printf("connection close.\n"); } else if(events & BEV_EVENT_ERROR) { printf("some other error.\n"); } //登出事件導致事件迴圈退出,這樣子執行緒也將退出 bufferevent_free(bev); printf("bufferevent 資源已經被釋放.\n"); } unsigned __stdcall SecondThreadFunc( void* pArguments ) { printf( "in a new thread(%d)...\n",GetCurrentThreadId()); evutil_socket_t evsock = (evutil_socket_t)pArguments; BufferManager *bm = new BufferManager; //子執行緒使用自己的base struct event_base *base; base = event_base_new(); //每個子執行緒都有自己的事件 struct bufferevent* bev; bev = bufferevent_socket_new(base, evsock, BEV_OPT_CLOSE_ON_FREE); //給bufferevent緩衝區設定回撥 bufferevent_setcb(bev, read_cb, write_cb, event_cb, bm); //啟動bufferevent的讀緩衝區,讀緩衝區預設是disable的. bufferevent_enable(bev, EV_READ); //開啟子執行緒的事件迴圈 event_base_dispatch(base); //執行緒退出,記憶體銷燬 if (bm) { delete bm; bm = NULL; } printf("new thread end(%d).\n",GetCurrentThreadId()); _endthreadex(0); return 0; } //監聽器回撥函式 void cb_listener(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr *addr, int len, void *ptr) { printf("new client connect.\n"); HANDLE hThread; unsigned threadID; hThread = (HANDLE) _beginthreadex( NULL, 0, &SecondThreadFunc, (void*)fd, 0, &threadID ); CloseHandle( hThread ); } int _tmain(int argc, _TCHAR* argv[]) { //初始化網路庫 #ifdef WIN32 WSADATA wsa_data; WSAStartup(0x0201, &wsa_data); #endif //初始化伺服器地址結構 struct sockaddr_in sSerAddr; memset(&sSerAddr, 0, sizeof(sSerAddr)); sSerAddr.sin_family = AF_INET; sSerAddr.sin_addr.s_addr = htonl(INADDR_ANY); sSerAddr.sin_port = htons(8888); //建立event_base struct event_base *base; base = event_base_new(); //建立監聽器 struct evconnlistener *listener; listener = evconnlistener_new_bind(base, cb_listener, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, (struct sockaddr*)&sSerAddr, sizeof(sSerAddr)); //啟動迴圈監聽 event_base_dispatch(base); //因為監聽事件不登出,理論上下面的三條語句不會直行到 //釋放操作 evconnlistener_free(listener); event_base_free(base); WSACleanup(); return 0; }
BufferManager類:
#pragma once
class BufferManager
{
public:
BufferManager(void);
~BufferManager(void);
FILE *f;
char szImgName[260]; //圖片的名稱
char buf[1000000]; //用於接收影象資料
int nFileSize; //檔案總大小
int nReceiveTotal; //接收檔案大小
public:
void iniParam();
};
#include "StdAfx.h" #include "BufferManager.h" #include "Windows.h" BufferManager::BufferManager(void):f(NULL) { iniParam(); } BufferManager::~BufferManager(void) { } void BufferManager::iniParam() { memset(szImgName,0,sizeof(szImgName)); memset(buf,0,sizeof(buf)); nFileSize = 0; nReceiveTotal = 0; if(f) { fclose(f); f = NULL; } }
客戶端傳送檔案主要程式碼如下:
WIN32_FIND_DATA FileInfo; HANDLE hFind = INVALID_HANDLE_VALUE; DWORD FileSize = 0; //檔案大小 char buf[1000000] = {0}; char *pbuf = NULL; ZeroMemory(&FileInfo,sizeof(WIN32_FIND_DATA)); hFind = FindFirstFile("test.png",&FileInfo); if(hFind != INVALID_HANDLE_VALUE) { FileSize = FileInfo.nFileSizeLow ; } FindClose(hFind); FILE *f = NULL; fopen_s(&f,"test.png","rb"); fread(buf + sizeof(PICTUREINFO),FileSize,1,f); fclose(f); f = NULL; FileSize += sizeof(PICTUREINFO); strcpy_s(((PICTUREINFO*)buf)->szFileName,"test.png"); ((PICTUREINFO*)buf)->nFileSize = FileSize; pbuf = buf; while (FileSize >= 10000) { bufferevent_write(bev,pbuf,10000); FileSize -= 10000; pbuf += 10000; } if (FileSize > 0) { bufferevent_write(bev,pbuf,FileSize); FileSize -= FileSize; pbuf += FileSize; }
typedef struct PictureInfo
{
char szFileName[260];
long nFileSize;
} PICTUREINFO;