2009年12月9日 星期三

freopen()

當我們想要redirect我們的標準輸出(stdout)或標準輸入(stdin)到某一個檔案,可以使用freopen()來達成。
FILE *freopen(const char *path, const char *mode, FILE *stream);

以下是簡單小範例

#include <iostream>
int main() {
freopen("test.txt", "w", stdout);
std::cout << "Hello freopen()" << std::endl;
return 0;
}


執行時,便不會看到任何輸出,而是會出現在test.txt檔案裡。

2009年11月29日 星期日

簡單的COM範例

簡單的COM範例

環境:Visual C++ 2008

1. 新增一個ATL專案,取名叫SimpleATL,伺服程式類型選擇DLL。
2. 在類別檢視下,可以看見SimpleATL類別,按右鍵加入類別,選擇ATL簡單物件,簡短名稱就設為MyATL。
3. 接著就是在IMyATL interface加上method。在IMyATL按右鍵加入method,方法名稱為Add,參數屬性為:
[in] LONG lNum1, [in] LONG lNum2, [out] LONG * plResult
4. 實作Add Method
STDMETHODIMP CMyATL::Add(LONG lNum1, LONG lNum2, LONG * plResult)
{
*plResult = lNum1 + lNum2;
return S_OK;
}

5. 建置專案即完成。


接著我們就用VC++來寫程式來使用這個COM
建立一個新的Win32 Console Application,選擇主控台應用程式和空專案選項。
先將SimpleATL_i.h及SimpleATL_i.c複製並加入到這個專案資料夾。
新增TestATL.cpp
#include "SimpleATL_i.h"
#include <iostream>

using namespace std;

void main(void)
{
HRESULT hr;
IMyATL *pIMyATL;

hr = CoInitialize(NULL);

if(SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_MyATL, NULL, CLSCTX_INPROC_SERVER, IID_IMyATL, (void**) &pIMyATL);

if(SUCCEEDED(hr))
{
long lResult;

hr = pIMyATL->Add(5, 10, &lResult);
cout << "5 + 10 = " << lResult << endl;
hr = pIMyATL->Release();
}
else
cout << "CoCreateInstance Failed." << endl;
}
CoUninitialize();
}

2009年11月28日 星期六

SO_REUSEADDR

當一個Socket server如果重新啟動時,在bind的過程就會出現:Address already in use的錯誤訊息,必須等待TCP TIME_WAIT state的時間(2 * MSL)才能使用這個Address。為了避免這個問題發生,我們可以透過修改socket參數來讓socket可以馬上bind之前的Address。

sock = socket(AF_INET, SOCK_STREAM, 0);

int on = 1;
int status = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
if (status == -1)
perror("setsockopt() error");

簡單的DLL範例

簡單的DLL範例

環境:Visual C++ 2008

建立一個新的,選擇Win32 Console Application
最後選擇DLL和Empty Porject選項

建立AddInt.h及AddInt.cpp

AddInt.h
#ifndef _ADD_INT_H_
#define _ADD_INT_H_

#define DLLExport __declspec( dllexport )

extern "C"
{
DLLExport int Add(int a, int b);
}

#endif


function必須export才能被使用,有兩種方法可以使用:
使用 __declspec這個關鍵字
建立一個Module-Definition File (.DEF)

至於為什麼要加入extern "C"
因為C++ compiler在編譯時會將function name作一些修飾也就是改名字的動作(mangle)
加入extern "C"則可保留原名

AddInt.cpp
#include "AddInt.h"

int Add(int a, int b) {
return a + b;
}


Build Project即完成,我們會在資料夾找到AddInt.dll及AddInt.lib


接著我們就寫一個程式來使用這個DLL

可以用兩種方式來使用DLL

Implicit Linking - 程式需link到AddInt.lib,並將AddInt.dll放在與程式同目錄下
#include <iostream>
#include "AddInt.h"

int main()
{
std::cout << Add(5, 10) << std::endl;

return 0;
}


Explicit Linking - 不需要AddInt.h及AddInt.Lib,僅需要AddInt.dll,但使用較為複雜。
#include <iostream>
#include <windows.h>

typedef int (*pfAddIntFunction)(int,int);

int main()
{
pfAddIntFunction pfAddInt ;

HINSTANCE hLibrary = LoadLibrary(L"AddInt.dll");

if(hLibrary)
{
pfAddInt = (pfAddIntFunction)GetProcAddress(hLibrary, "Add");

if(pfAddInt)
{
std::cout << pfAddInt(5, 10) << std::endl;
}

FreeLibrary(hLibrary);
}
else
{
std::cout << "Failed To Load Library" << std::endl;
}

return 0;
}

2009年11月21日 星期六

NAT型態

NAT的類型大致可分為四種:
  • Full cone NAT
  • (Address) Restricted cone NAT
  • Port-Restricted cone NAT
  • Symmetric NAT

假設在NAT下的主機A為(iIP, iPort),實際對外為(eIP, ePort)

Full cone NAT: 任何主機皆可透過(eIP, ePort)送封包給A。

(Address) Restricted cone NAT:只有之前收過A送來封包的外部主機(hIP, any),才可透過(eIP, ePort)送封包給A,不限定原來溝通的Port。

Port-Restricted cone NAT :同上,但外部主機只能用之前用的Port才能送封包給A。

Symmetric NAT:同上,但A對每一個不同的(hIP, hPort),也就是IP或Port的任何一值改變,其(eIP, ePort)就會不同。

2009年11月17日 星期二

Dump Schema

若要匯出MySQL下某個資料庫的Schema,可以用mysqldump來達成:

# mysqldump -d -u 使用者名稱 -p 欲備份的資料庫 >> FILENAME.sql

例:
# mysqldump -d -u howard -p account > account.sql


匯入到新的MySQL資料庫

# mysql -u 使用者名稱 -p 資料庫 < account.sql

2009年10月27日 星期二

CMake簡單範例

CMake是一個軟體建構工具,可以幫我們產生MakeFile。以下就用簡單的範例來呈現:
我們有三個檔案main.cpp hello.cpp hello.h和三個CMakeLists.txt
分別位於

|-- CMakeLists.txt
|-- lib
| |-- CMakeLists.txt
| |-- hello.cpp
| |-- hello.h
|-- src
|-- CMakeLists.txt
|-- main.cpp

hello.h
void hello();
hello.cpp
#include "hello.h"
#include <iostream>
void hello() {
std::cout << "Hello CMake!" << std::endl;
}

main.cpp
#include "hello.h"
int main() {
hello();
return 0;
}

最上層的CMakeLists.txt
PROJECT (HELLO)
# 加入兩個子資料夾(src後面bin的意思是將src的輸出放到bin資料夾)
ADD_SUBDIRECTORY (src bin)
ADD_SUBDIRECTORY (lib)

lib/CMakeLists.txt
# 將hello.cpp編譯成static library
ADD_LIBRARY (HelloLibrary hello.cpp)

src/CMakeLists.txt
INCLUDE_DIRECTORIES (${HELLO_SOURCE_DIR}/lib)
SET (HELLO_SRCS main.cpp)
ADD_EXECUTABLE (HelloCMake HELLO_SRCS)
TARGET_LINK_LIBRARIES (HelloCMake HelloLibrary)

在執行cmake前,我們可以在最上層先建立一個目錄(例如:build),然後到build目錄下執行cmake,如此cmake後產生的資料皆會在build資料夾裡。
# mkdir build
# cd build
# cmake ../
# make

若順利的話,便可在bin找到HelloCMake執行檔,在lib可找到libHelloLibrary.a

2009年10月24日 星期六

FastCGI

FastCGI是一個網頁伺服器端應用程式介面,CGI有一個很大的缺點就是CGI程式每次執行就會產生一個新的process,當同時很多人上線時,會對伺服器造成很大負擔;而FastCGI程式是常駐型的CGI程式,不會因為Http_Request結束而終止。

在Debian 5安裝Apache2 + FastCGI
# apt-get install apache2 libapache2-mod-fastcgi
# /etc/init.d/apache2 restart

接著用c寫一個helloFCGI的程式來驗証:
#include "fcgi_stdio.h"
#include <stdlib.h>

int main()
{
int count = 0;
while(FCGI_Accept() >= 0) {
printf("Content-type: text/html\r\n"
"\r\n"
"<title>Hello FastCGI</title>"
"<h1>Hello FastCGI</h1>"
"Request number %d running on host <i>%s</i>\n",
++count, getenv("SERVER_NAME"));
}
return 0;
}

編譯時要加上-lfcgi參數
# gcc -lfcgi -o helloFCGI.fcgi helloFCGI.c

將helloFCGI.fcgi放到所設定的FastCGI執行的資料夾內(Debian安裝好的預設資料夾在/usr/lib/cgi-bin),在網頁執行http://localhost/cgi-bin/helloFCGI.fcgi即可看到結果。

2009年10月7日 星期三

Windows Multithreaded Programming

要建立多執行緒程式,我們會用到<process.h>標頭檔定義的函式:

uintptr_t _beginthread(

void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist

);

_beginthread()需要三個參數,第一個是要執行緒所執行的函式名稱(注意這個函式的prototype);第二個參數為給這個執行緒的stack size,若設定0值則作業系統會自動設定此值;第三個參數可以用來傳遞資料給欲執行的函式。

#include <windows.h>
#include <process.h>
#include <iostream>

using namespace std;

void func1(void *);
void func2(void *);

int main()
{
_beginthread(func1, 0, NULL);
_beginthread(func2, 0, NULL);

Sleep(10000);
cout << "Main exit" << endl;
getchar();
return 0;
}

void func1(void *p)
{
for(int i = 1; i < 6; ++i)
cout << "func1 value " << i << endl;
}

void func2(void *p)
{
for(int i = 1; i < 6; ++i)
cout << "func2 value " << i << endl;
}

以下用簡單的範例說明:
執行結果如下:


這個結果可以發現兩個執行緒可以在任何時間點去插隊執行,若要避免這種情況發生,我們可以利用critical section來解決它。

2009年9月26日 星期六

update-alternatives

Debian提供了update-alternatives的工具,可以用來管理套件的版本。例如:系統有三套Java的套件GIJ、GCJ和Sun Java,如何能自由且快速的切換不同版本。

首先,先看一下java指令的路徑
#which java
/usr/bin/java

# ls -l /usr/bin/java
/usr/bin/java -> /etc/alternatives/java
# ls -l /etc/alternatives/java
/etc/alternatives/java -> /usr/lib/jvm/java-gcj/jre/bin/java

我們可以看到java的指令用一個symbolic link連到/etc/alternatives/java,而/etc/alternatives/java又是一個symbolic link連到/usr/lib/jvm/java-gcj/jre/bin/java。
所以update-alternatives就是將/etc/alternatives/java改掉。

# update-alternatives --display java
java - status is auto.
link currently points to /usr/lib/jvm/java-gcj/jre/bin/java
/usr/bin/gij-4.2 - priority 42
slave java.1.gz: /usr/share/man/man1/gij-4.2.1.gz
/usr/lib/jvm/java-gcj/jre/bin/java - priority 1042
/usr/lib/jvm/java-6-sun/jre/bin/java - priority 63
slave java.1.gz: /usr/lib/jvm/java-6-sun-1.6.0.00/jre/man/man1/java.1.gz
Current `best' version is /usr/lib/jvm/java-gcj/jre/bin/java.

可以看到目前系統有gcj和Sun Java,由於status是auto,而gcj的優先權最高,所以可以最下面顯示目前最佳的版本是gcj。若要改變成java-6-sun:

# update-alternatives --config java

即可選擇java-6-sun。