1. 程式人生 > >windows下 Libevent +多執行緒 實現檔案傳輸

windows下 Libevent +多執行緒 實現檔案傳輸

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;