linux平臺基於文件鎖實現用戶數據鎖(基於文件鎖判斷用戶是否處於登錄狀態)

軟件多用戶管理,同一中端不允許重複登錄同一賬號。linux平臺基於flock實現該功能。首先,用戶登錄時,創建屬於該用戶的鎖文件並上鎖。當要重複登錄同一個賬號時,首先檢查該用戶的鎖文件是否存在,如果存在並且鎖文件處於鎖定狀態,那麼就判定用戶處於登錄狀態。廢話不多說,直接上測試代碼。

main.cpp(編譯命令g++ main.cpp -o test -lstdc++fs

#include <experimental/filesystem>
#include <string>
#include <string.h>
#include <sys/fcntl.h>
#include <unistd.h>

using namespace std;

namespace filesystem = std::experimental::filesystem;
// 用戶數據鎖類
class UserDataLock
{
public:
	UserDataLock(const string& lockFilename);
	~UserDataLock();

private:
	filesystem::path m_lockFilePath;// 用戶數據鎖文件路徑
	struct lockFileDeleter
	{
		void operator()(FILE* fileHandle) const
		{
			if (fileHandle)
			{
				fclose(fileHandle);
			}
		}
	};
	std::unique_ptr<FILE, lockFileDeleter> m_lockFileHandle;// 用戶數據鎖文件句柄
};

UserDataLock::UserDataLock(const string& lockFilename)
	: m_lockFilePath(lockFilename)
{
	if (filesystem::exists(m_lockFilePath))
	{
		throw std::runtime_error("a lock file exists");// 鎖文件存在
	}
	m_lockFileHandle.reset(fopen(m_lockFilePath.string().c_str(), "wb"));
	if (!m_lockFileHandle)
	{
		throw std::runtime_error("create lock file failed");// 創建鎖文件失敗
	}
	const char* content = "File Lock";
	fwrite(content, strlen(content), 1, m_lockFileHandle.get());
	fflush(m_lockFileHandle.get());// 向鎖文件寫入"File Lock"
	
	// 嘗試鎖住整個文件,給文件上鎖
	struct flock lock = {};
	lock.l_type = F_WRLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_pid = getpid();
	if (fcntl(fileno(m_lockFileHandle.get()), F_SETLK, &lock) == -1) {//上鎖失敗
		throw std::runtime_error("lock lock file failed");
	}
}
UserDataLock::~UserDataLock()
{
	try
	{
		// 釋放鎖
		struct flock lock = {};
		lock.l_type = F_UNLCK;
		lock.l_whence = SEEK_SET;
		lock.l_start = 0;
		lock.l_len = 0;
		lock.l_pid = getpid();
		fcntl(fileno(m_lockFileHandle.get()), F_SETLK, &lock);
		m_lockFileHandle.reset();
		filesystem::remove(m_lockFilePath);
	}
	catch (const filesystem::filesystem_error&)
	{
	}
}
//獲取用戶數據鎖
std::unique_ptr<UserDataLock> AcquireUserDataLock(const string& strlockFilename)
{
	filesystem::path lockFilename = strlockFilename;

	try
	{
		if (filesystem::exists(lockFilename))
		{
			int fd = open(lockFilename.string().c_str(), O_RDWR);
			if (fd == -1) {
				throw std::runtime_error("can not open lock file");
			}
			// 嘗試鎖住整個文件,linux平臺刪除已打開文件不會拋出異常,先嚐試給文件上鎖
			struct flock lock = {};
			lock.l_type = F_WRLCK;
			lock.l_whence = SEEK_SET;
			lock.l_start = 0;
			lock.l_len = 0;
			lock.l_pid = getpid();
			if (fcntl(fd, F_SETLK, &lock) == -1) {//上鎖失敗
				close(fd);
				throw std::runtime_error("a lock file exists");
			}
			// 釋放鎖
			lock.l_type = F_UNLCK;
			if (fcntl(fd, F_SETLK, &lock) == -1) {
				close(fd);
				throw std::runtime_error("lock file release lock fail");
			}
			close(fd);
		}

		auto pLock = std::unique_ptr<UserDataLock>(new UserDataLock(lockFilename.string()));
		return pLock;
	}
	catch (const filesystem::filesystem_error& e)
	{
		return nullptr;
	}
	catch (const std::runtime_error& e)
	{
		return nullptr;
	}
}

int main()
{
	std::string lockfilepath = "/tmp/a.lock";
	UserDataLock userlock(lockfilepath);//創建一個用戶鎖,此時處於登錄狀態
	
	auto pUserDataLock = AcquireUserDataLock(lockfilepath);
	if(pUserDataLock == nullptr)//獲取用戶數據鎖失敗,說明此時處於登錄狀態
	{
		printf("用戶數據鎖存在\n");
	}
	return 0;
}

執行結果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章