1. 程式人生 > >串列埠通訊模組3:串列埠通訊程式設計基礎(讀寫、關閉)

串列埠通訊模組3:串列埠通訊程式設計基礎(讀寫、關閉)

上一節總結了如何開啟串列埠並討論瞭如何配置串列埠,本節是在上一節的基礎上,進一步討論串列埠程式設計的基礎——如何進行檔案讀寫?如何關閉串列埠?

1. 讀寫串列埠

串列埠的讀寫操作和檔案的讀寫操作是一樣的,也是通過ReadFile()及WriteFile()函式來實現的。這兩個函式的原型分別如下:

寫檔案函式原型及說明

BOOL WriteFile(
	HANDLE hFile,
	LPCVOID lpBuffer,
	DWORD nNumberOfBytesToWrite,
	LPDWORD lpNumberOfBytesWritten,
	LPOVERLAPPED lpOverlapped,
);
該函式包含5各引數,其具體意義為:

hFile:開啟串列埠時返回的控制代碼。

lpBuffer:寫入的資料儲存的地址,即以該指標得值為首地址的nNumberOfBytesToWrite個位元組的資料,將要寫入串列埠的傳送資料緩衝區。

nNumberOfBytesToWrite:要寫入的資料的位元組數

lpNumberOfByteWritten:指向一個DWORD數值,該數值返回實際寫入的位元組數。

lpOverlapped:重疊操作時,該引數指向一個OVERLAPPED結構,同步操作時,該引數為NULL.

讀檔案函式原型及說明

BOOL ReadFile(
	HANDLE hFile,
	LPCVOID lpBuffer,
	DWORD nNumberOfBytesToRead,
	LPDWORD lpNumberOfBytesRead,
	LPOVERLAPPED lpOverlapped,
);
該函式的引數解釋與上文相類似。

再用ReadFile()和WriteFile()函式讀寫串列埠時,即可以同步執行也可以重疊執行。再同步執行時,函式直到操作完成後才返回。這意味著同步執行時執行緒會被阻塞,從而導致效率下降。在重疊操作時,即時操作還未完成,這兩個函式也會立即返回,費事的I/O操作在後臺執行。

而在呼叫CreateFile()函式開啟串列埠時,就已經決定了ReadFile()和WriteFile()函式對串列埠的操作是同步還是非同步。如果在呼叫CreateFile()函式建立開啟串列埠時指定了FILE_FLAG_OVERLAPPED標誌,那麼呼叫ReadFile()和WriteFile()函式對該開啟串列埠時返回的控制代碼進行的操作就是重疊的。如果沒有指定重疊標誌,那麼讀寫操作就應該是同步的。這裡強調的是:ReadFile()和WriteFile()函式的同步或非同步選擇應該和CreateFile()函式相一致

說明:ReadFile()函式只要在串列埠輸入緩衝區中讀入指定數量的字元,就算完成了工作。然而,WriteFile()函式不但要把指定數量的字元複製到輸出緩衝區,而且要等這些字元從創航口送出去才算完成操作。

由於同步讀寫串列埠的實現很簡單,先研究同步串列埠讀寫的程式碼:

//同步讀串列埠程式碼
bool ReadCom(char * str, int len)
{
	DWORD wCount;  //讀取的位元組數
	BOOL bReadStat;
	bReadStat = ReadFile( hCom, str, len, &wCount, NULL);
	if (!bReadStat)
	{
		AfxMessageBox(" 讀串列埠失敗! ");
		return FALSE;
	}
	return TRUE;
}
//同步寫串列埠程式碼
bool WriteCom(char * str, int len)
{
	DWORD dwBytesWriten = 0;
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	BOOL bWriteStat;
	ClearCommError(hCom, &dwErrorFlags, &ComStat);
	bWriteStat = WriteFile(hCom, str, len, &dwBytesWriten, NULL);
	if (!bWriteStat)
	{
		AfxMessageBox(" 寫串列埠失敗");
		return FALSE;
	}
	PurgeComm(hCom, //清理讀寫快取區
		  PURGE_TXABORT| PURGE_RXABORT| PURGE_TXCLEAR| PURGE_RXCLEAR);
}

非同步讀寫串列埠的實現就比較靈活了,即可以實現非阻塞讀寫,也可以實現阻塞讀寫。有兩種方法可以等待操作完成而實現阻塞讀寫

# 應用像WaitForSingleObject()等待函式一樣來等待OVERLAPPED結構的hEvent成員。

# 呼叫GetOverlappedResult()函式等待
在OVERLAPPED結構中包含了重疊I/O的一些資訊,它的詳細定義如下:

typedef struct _OVERLAPPED
{
	DWORD Internal;    //作業系統保留,指出一個和系統相關的狀態
	DWORD InternalHigh;//指出傳送或接收的資料長度
	DWORD Offset;      //檔案傳送的開始位置
	DWORD OffsetHigh;  //檔案傳送位元組變一輛的高位元組
	HANDLE hEvent;     //指定一個I/O操作完成後觸發的事件
}OVERLAPPED;
在使用ReadFile()和WriteFile()函式進行非同步重疊操作時,執行緒需要建立OVERLAPPED結構以供這兩個函式使用。執行緒通過OVERLAPPED結構獲得當前的操作狀態,該結構的成員是hEvent。hEvent是讀寫事件,當串列埠使用非同步通訊時,函式返回時可能還沒有完成,程式可以通過檢查該事件的值是否讀寫完畢。當呼叫ReadFile()或WriteFile()函式的時候,該成員會自動被指為無訊號狀態;當重疊操作完成後,該成員變數會自動被置為有訊號狀態。

而GetOverlappedResult()函式返回重疊操作結束有點不同,他是通過判斷OVERLAPPED結構中的hEvent是否被置位,來判斷非同步操作是否完成,函式原型如下:

BOOL GetOverlappedResults(
	HANDLE hFile, //串列埠的控制代碼
	LPOVERLAPPED lpOverlapped,  //重疊操作開始時指定的OVERLAPPED結構
	LPWORD lpNumberOfBytesTransferred, //實際讀寫操作傳輸的位元組數
	BOOL bWait            //用於指定函式是否一直等到重疊操作結束
);
在非同步操作前應先使用ClearCommError()函式獲取尚未讀取的位元組數,函式原型如下:
BOOL ClearComError(
	HANDLE hFile,     //串列埠控制代碼
	LPDWORD lpErrors, //指向接受錯誤碼的變數
	LPCOMSTAT lpStat  //指向通訊狀態快取區
);
根據以上資訊可以編寫非同步讀串列埠的程式:
bool ReadCom(char* str, DWORD *len)
{
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	OVERLAPPED m_osRead;
	memset(&m_osRead,0,sizeof(OVERLAPPED));
	m_osRead.hEvent = CreateEveent(NULL, TRUE, FALSE, NULL);
	ClearCommError(hCom, &dwErrorFlags, &ComStat);
	dwBytesRead = min(len, (DWORD)ComStat.cbInQue);//獲取尚未讀取的位元組數
	if (!dwBytesRead)
		return FALSE;
	BOOL bReadStatus;
	bReadStatus = ReadFile(hCom, str, len, &len, &m_osRead);//讀取資料
	if (!bReadStatus)
	{//錯誤處理
		if (GetLastError == ERROR_IO_PENDING)
		{   //串列埠正在進行讀操作
		    //等到讀操作完成或延時已經達到2秒
			WaitForSingleObject(m_osRead.hEvent,2000);
			PurgeComm(hCom,
				PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
		}//清空串列埠緩衝
		return false;
	}//清空串列埠快取區
	PurgeComm(hCom,
		PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
	return true;
}

2. 關閉串列埠

利用API函式關閉串列埠是非常簡單的活兒,只需要使用CreateFile()函式返回的控制代碼作為引數呼叫CloseHandle()函式即可。CloseHandle()函式如下:

BOOL CloseHandle(
	HANDLE  hObject;
);
到此,串列埠程式設計的基礎就OK了,明天繼續研究自定義串列埠類,以及介面設計的幾點敲門,晚安!!