討論區快速選單
知識庫快速選單
程式設計俱樂部Facebook粉絲團 軟體開發過程中有哪些資安漏洞?
[ 回上頁 ] [ 討論區發言規則 ]
[教學]socket通訊
更改我的閱讀文章字型大小
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 上午 06:57:54
我先開這個主題, 未來我會針對socket通訊方面做一些簡單的說明. 由於我講求的是portable, 因此winsock許多功能我並不會特別的說明 (特別是非同步通訊), 但我列出的程式, 一定是Windows/Linux (包括Solaris/AIX等等Unix系統), 一體適用.

socket運用方面有很多, 這邊我只專就於tcp/ip, block方式的通訊, 其他通訊的方式, 請自行研究 (全寫的話就太多了).

有英文閱讀能力的人, 請先看看底下這篇IBM寫的文章:

http://www-128.ibm.com/developerworks/linux/library/l-hisock.html?ca=dgr-lnxw01BoostSocket

其中一部份, 我會在後面提及, 但不是全部. (英文不好, 不要怪別人).

這篇先寫到這裡, 因為我還在趕工中. 全篇應該會在2週內寫完, 請靜等.
作者 : windblown(windblown) VC++優秀好手C++ Builder曠世奇才C++卓越專家貼文超過1000則
[ 貼文 1105 | 人氣 890 | 評價 9400 | 評價/貼文 8.51 | 送出評價 200 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 上午 11:43:46
感謝青衫的分享,尤其是跨平台的程式設計,真的是很少接觸;期待可以偷師 ^_^
作者 : kevin20060520(kevin) 貼文超過500則人氣指數超過100000點
[ 貼文 718 | 人氣 132860 | 評價 60 | 評價/貼文 0.08 | 送出評價 60 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 02:13:03
期待中........
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 05:13:52
期待 呵呵
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 06:10:48
** 平台的特性

//CPU型式
//#define INTEL
//#define SUN
//#define AIX

//作業系統
//#define WINDOWS
//#define SOLARIS
//#define LINUX
//#define AIX

#ifdef SUN
#define HIGH_BYTE_FIRST
#define NEED_DATA_ALIGN
#endif

#ifdef AIX
#define HIGH_BYTE_FIRST
#endif

這些是我曾porting過的cpu與作業系統. 作業系統部份, 主要區分為Windows與Unix, 其中的差異在於呼叫函數不太一樣. cpu方面比較麻煩. 這裡我定義了兩個參數:

HIGH_BYTE_FIRST: 就是記憶體中是高位元組在前. 一般常見的INTEL CPU是低位元組在前, 例如一個長整數0x12345678, 在記憶體中的排列是0x78, 0x56, 0x34, 0x12, 但若是高位元組在前, 則是0x12, 0x34, 0x56, 0x78. 由於socket傳送的資料, 是以byte為觀念, 如果要傳送一個長整數, 便要考慮到記憶體中排列的格式.

NEED_DATA_ALIGN: 也就是資料存取時, 必須配合記憶體Align的地方. 這部份舉例比較快些:

如果要將資料放入緩衝區裡, 如果是先放一個char資料 (1 byte), 再放一個long資料 (4 byte), 那麼普通寫法是:

char data1;
long data2;
char* buffer = new char[...];
buffer[0] = data1;
*((long*)(buffer+1)) = data2;

這樣的寫法在NEED_DATA_ALIGN的CPU中會引起Exception, 因為buffer+1的位址不是在4 byte align之處, 此時去存取long就會出問題. 解決方法就是利用memcpy, 直接將資料拷入, 讀取時也是要用memcpy方式讀出 (比較麻煩些).
作者 : windblown(windblown) VC++優秀好手C++ Builder曠世奇才C++卓越專家貼文超過1000則
[ 貼文 1105 | 人氣 890 | 評價 9400 | 評價/貼文 8.51 | 送出評價 200 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 06:25:04

>** 平台的特性

請問除了 CPU 與 OS 之外,是否要考量 compiler 廠商(ex: MS, Borland, GNU, ...)的特性呢?
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 08:03:49

>請問除了 CPU 與 OS 之外,是否要考量 compiler 廠商(ex: MS, Borland, GNU, ...)的特性呢?

當然, 如果是寫open source給別人引用, 那麼就還要考慮compiler. 如果是自用, 各作業系統只要挑自己在使用的便可以了. Windows我是挑VC, Unix我是挑GNU.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 08:16:26
** 基本參數的定義與應include的header file:

#ifdef WINDOWS
    #include <winsock2.h>
#else
#include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <errno.h>
    typedef int SOCKET;
    #define SOCKET_ERROR -1
    #define INVALID_SOCKET -1
#endif

這些定義, 主要是方便程式的相容性 (for Windows and Unix). 以下是我寫的Socket通訊物件, 後面會再說明各函數的用途與寫法.

class WgSocket
{
private:
SOCKET m_Socket;
public:
static bool Initialize(void);
static void Terminate(void);
static bool IsLocalHost(const char* hostname);
static bool GetHostIP(const char* hostname, int &ip1, int &ip2, int &ip3, int &ip4);
public:
WgSocket(void);
~WgSocket();
bool IsOpened(void) const;
void SetSocket(SOCKET socket);
bool Open(const char* hostname, int port);
void Close(void);
bool WaitInputData(int seconds);
bool Read(void* buffer, wglong len, wglong &ret_len);
bool Write(const void* buffer, wglong len);
bool Listen(int port);
bool Accept(SOCKET &socket);
bool SetNoDelay(void);
};

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 08:28:05
*** socket的初始化

Windows的socket是需要初始化的, Unix則不用. 由於Windows的socket初始化, 每次約需費時20ms, 因此不要每次都初始化. 這部份可以在使用WgSocket函數時, 自動呼叫處理.

#ifdef WINDOWS
   static bool m_Init_Flag=false;
#endif

bool WgSocket::Initialize(void)
//說明:初始化Socket物件
//傳回:失敗傳回false
{
#ifdef WINDOWS
   if (!m_Init_Flag)
   {
     WSAData wsa_data;
     try
     {
     if (WSAStartup(0x202,&wsa_data) != 0) return false; // 初始化失敗 //
     }
     catch(...)
     {
     return false;
     }
     m_Init_Flag = true;
   }
#endif
   return true;
}

void WgSocket::Terminate(void)
//說明:結束Socket物件
{
#ifdef WINDOWS
   if (m_Init_Flag)
   {
     try
     {
     WSACleanup();
     }
     catch(...)
     {
     }
     m_Init_Flag = false;
   }
#endif
}

各位應注意到, 我呼叫外界函數時, 都會加上try/catch, 這是為了避免外界函數出問題時, 引發自己程式當掉.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 08:40:45
在說明WgSocket各函數之前, 先說明一下socket的使用流程 (Block模式):

cient端:

1.取得socket並連線 (Open)
2.傳接資料 (WaitInputData, Read, Write)
3.斷線 (Close)

server端:

1.取得socket, bind到某個通訊埠, 然後listen (Listen)
2.接收client端的連線 (Accept, SetSocket)
3.傳接資料並加以處理 (WaitInputData, Read, Write) -> 視情況要用多重執行緒
4.終止與client端的連線 (Close)
5.回到步驟3, 直到應結束
6.斷線 (Close)

其實socket通訊的函數, 基本上並不難, 比較難的是傳接雙方的通訊協定如何去定義, 這部份等談完socket通訊函數後, 再來說明.
作者 : windblown(windblown) VC++優秀好手C++ Builder曠世奇才C++卓越專家貼文超過1000則
[ 貼文 1105 | 人氣 890 | 評價 9400 | 評價/貼文 8.51 | 送出評價 200 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/2 下午 08:50:52


> 如果是自用, 各作業系統只要挑自己在使用的便可以了. Windows我是挑VC, Unix我是挑GNU.

原來如此,我知道自己的問題了,謝謝
作者 : ku72(ku72) 人氣指數超過10000點
[ 貼文 188 | 人氣 17428 | 評價 420 | 評價/貼文 2.23 | 送出評價 30 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 上午 02:01:22
青杉大大 什麼時候來一篇藍芽教學....
我最近被它搞昏頭了ˊˋ....

時能連時不能連 我~~~好~~~可~~~憐~~~阿~~!!!!!
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 02:47:03
>青杉大大 什麼時候來一篇藍芽教學....
>我最近被它搞昏頭了ˊˋ....

我不是這行業的, 因此也沒經驗, 請其他有經驗的人補一篇吧.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 03:18:24
*** 連線Server端

WgSocket::WgSocket(void)
//說明:建構Socket物件
{
   m_Socket = INVALID_SOCKET;
}

WgSocket::~WgSocket()
//說明:解構Socket物件
{
   Close();
}

bool WgSocket::IsLocalHost(const char* hostname)
//說明:檢查是否為localhost呼叫
//輸入:hostname = Server位址
//傳回:是否為localhost呼叫
{
   if (hostname == NULL) return true;
   if (*hostname == 0) return true;
   if (stricmp(hostname,"localhost") == 0) return true;
   if (strcmp(hostname,"127.0.0.1") == 0) return true;
   return false;
}

bool WgSocket::IsOpened(void) const
//說明:檢測Socket是否已開啟
//傳回:檢測結果
{
   if (m_Socket == INVALID_SOCKET) return false;
   return true;
}

bool WgSocket::Open(const char* hostname, int port)
//說明:開啟與Server的連線
//輸入:hostname,port = Server位址與通訊埠
//傳回:失敗傳回false
{
   Close();
   if (!Initialize()) return false;
   struct sockaddr_in sock_addr;
   // 解出socket address //
   if (IsLocalHost(hostname)) hostname = "127.0.0.1";
   sock_addr.sin_family = AF_INET;
   sock_addr.sin_port = htons(port);
   struct hostent *hostinfo = gethostbyname(hostname);
   if (hostinfo == NULL) return false;
   sock_addr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
   // 建立socket //
   try
   {
     m_Socket = socket(AF_INET,SOCK_STREAM,0);
   }
   catch(...)
   {
     m_Socket = INVALID_SOCKET;
     return false;
   }
   if (m_Socket == INVALID_SOCKET) return false;
   // 開始連線 //
   try
   {
     if (connect(m_Socket,(struct sockaddr*)&sock_addr,sizeof(sock_addr)) >= 0) return true;
   }
   catch(...)
   {
   }
   // 此處可以加入一些錯誤處理... //
   Close();
   return false;
}

對Server連線的方式, 便是解開Server Hostname的位址, 取得socket, 然後連線. 這邊我將一些錯誤處理的部份都會掉了, 需要的人請自行加入. Windows上是由WSAGetLastError函數取得錯誤碼, Unix上則是由errno公用變數取得. 以下簡單說明一下Windows上常見的錯誤:

WSAECONNREFUSED (10061): 無法連線
WSAEADDRINUSE (10048): 沒有可用的port
WSAETIMEDOUT (10060): 連線逾時

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 03:28:36
通常當Server端沒有開機時, 會傳回連線逾時, 大約需等數十秒才會傳回, 如何縮短這個逾時時間, 我並沒有深入去研究, 主要是很少碰到. 如果Server端有開機, 但Server通訊程式沒執行, 則會立即傳回無法連線. 由於每次連線需要約20-30ms, 因此對於講求效率的通訊程式而言, 一般會採用connect-alive方式處理, 這部份後面再來說明. 至於WSAEADDRINUSE的實際意義, 就是所有可用的port都處理TIME_WAIT狀態, 無法再拿來使用. 關於port的狀態值, 下面是相關的基礎知識.

1.連線與關閉的狀態與過程

當socket port尚未使用時,它的狀態是CLOSED。Server端必須先listen、bind和accept,以準備接收Client端的連線,此時的狀態進入LISTEN。

當Client端要連線到Server端時,首先會送一個SYN封包到Server端,同時進入SYN_SENT狀態。Server端收到後,便回送SYN-ACK封包,並進入SYN_RCVD狀態。Client端收到這個封包之後,便進入ESTABLISHED狀態,並回送ACK封包,表示連線成功,否則在過一段時間後,直接回到CLOSED狀態,並傳回錯誤。Server端收到Client端送來的ACK封包後,進入ESTABLISHED狀態,表示連線成功,由Accept函數返回。

Socket的關閉有兩種方式,一種是主動關閉,一種是被動關閉。以下為其過程:

(1)主動關閉(active socket closure)

當程式呼叫closesocket或shutdown(SD_SEND)時,首先送出FIN封包給對方,同時狀態變成FIN_WAIT_1。接著等待對方的回應封包:

a. 如果回應的是FIN封包,表示對方也正好要關閉連線,此時送出ACK封包給對方,狀態也進入CLOSING,此時必須等對方回應ACK封包後,進入TIME_WAIT狀態。
b. 如果回應的是ACK封包,表示對方尚未關閉連線,此時便進入FIN_WAIT_2狀態,並等待對方送來FIN封包。收到後送出ACK封包給對方,即進入TIME_WAIT狀態。
c. 如果同時收到FIN與ACK封包,表示對方已進入等待連線關閉的狀態,送出ACK封包給對方後,便進入TIME_WAIT狀態。

(2)被動關閉(passive closure)

當socket port收到對方送來FIN封包時,表示對方要關閉連線,此時送回ACK封包後,進入CLOSE_WAITING狀態,等待程式呼叫closesocket。當程式呼叫closesocket後,送出FIN封包給對方,並進入LAST_ACK狀態,等待對方回送ACK封包後,便進入TIME_WAIT狀態。

無論是何種連線關閉的過程,都會進入TIME_WAIT狀態。當進入這種狀態時,socket port是無法再被使用的,必須等待一段時間後,才能再被使用。這個等待時間的目的,是為了讓網路上殘留該ip的所有封包能夠因timeout而被全數捨棄所設,因此至少應該>=2MSL。MSL即Maximum Segment Lifetime,也就是網路上各Router所設定的封包最大殘留時間。

由於socket port進入TIME_WAIT狀態時, 必須等待一段時間後才能再度被使用, 因此如果有大量的連線/斷線, 往往會導致所有的socket port都被用光了. 這時便需要修改Windows的設定, 增加可使用的socket port數量, 或縮短TIME_WAIT的等待時間. 更改的方式是設定register key:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Paremeters

裡面有兩個參數:

MaxUserPort: 最大可用的socket port數目, 將之改成65534
TcpTimedWaitDelay: TIME_WAIT的等待時間, 將之改成30 (秒)

Unix方面還沒遇過有類似的問題.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 03:42:24
*** 關閉連線

void WgSocket::Close(void)
//說明:關閉與Server的連線
{
   if (!IsOpened()) return;
   try
   {
#ifdef WINDOWS
     shutdown(m_Socket,SD_SEND);
#else
shutdown(m_Socket,SHUT_WR);
#endif
   }
   catch(...)
   {
   }
   try
   {
#ifdef WINDOWS
     closesocket(m_Socket);
#else
close(m_Socket);
#endif
   }
   catch(...)
   {
   }
   m_Socket = INVALID_SOCKET;
}

關於斷線的流程, 可以參考MSDN裡"Graceful shutdown"這篇. 這裡我並沒有去讀取殘留的已接收資料, 而是直接close socket將之捨棄 (因為實測結果有時會發生卡死的狀況). 注意Windows與Unix所使用的函數名稱並不太一樣, 但作用是相同的.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 04:04:52
*** Server端的接聽過程

Server端的接聽過程, 主要是bind, listen (以上只需一次), 然後利用accept來與Client
端建立連線 (以上可以多次).

bool WgSocket::Listen(int port)
//說明:接聽某個Port
//輸入:port = 接聽Port
//傳回:失敗傳回false
{
   Close();
   if (!Initialize()) return false;
   struct sockaddr_in sock_addr;
   sock_addr.sin_family = AF_INET;
   sock_addr.sin_addr.s_addr = INADDR_ANY;
   sock_addr.sin_port = htons(port);
   // 建立socket //
   try
   {
     m_Socket = socket(AF_INET,SOCK_STREAM,0);
   }
   catch(...)
   {
     m_Socket = INVALID_SOCKET;
     return false;
   }
   if (m_Socket == INVALID_SOCKET) return false;
   // Bind socket //
   int on = 1;
   setsockopt(m_Socket,SOL_SOCKET,SO_REUSEADDR,(char*)&on,sizeof(on));
   int rc;
   try
   {
     rc = bind(m_Socket,(struct sockaddr*)&sock_addr,sizeof(sock_addr));
   }
   catch(...)
   {
    rc = SOCKET_ERROR;
   }
   if (rc == SOCKET_ERROR)
   {
     Close();
     return false;
   }
   // Listen socket //
   try
   {
     rc = listen(m_Socket,SOMAXCONN);
   }
   catch(...)
   {
    rc = SOCKET_ERROR;
   }
   if (rc == SOCKET_ERROR)
   {
     Close();
     return false;
   }
   return true;
}

注意這裡我用setsockopt將SO_REUSEADDR設立起來, 這樣的話, 無論該socket port是處於什麼狀態, 都會被拿來重複使用. 因此在呼叫前要確定Server通訊程式沒有重複執行. 為什麼要設定這個屬性呢? 因為在Unix裡, 如果程式因特殊狀況跳出, 或是用kill方式停掉Server通訊程式, 由於socket port沒有正常close, 必須停上一段時間後才能再度使用該port, 如此一來, Server通訊程式便無法立即再度啟用了.

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 04:05:31
bool WgSocket::Accept(SOCKET &socket)
//說明:等待接收連線
//輸出:連線socket
//傳回:失敗傳回false
{
   socket = INVALID_SOCKET;
   if (!IsOpened()) return false;
   struct sockaddr_in from;
#ifdef WINDOWS
    int fromlen = sizeof(from);
#else
socklen_t fromlen = sizeof(from);
#endif
   try
   {
     socket = accept(m_Socket,(struct sockaddr*)&from,&fromlen);
   }
   catch(...)
   {
     socket = INVALID_SOCKET;
     return false;
   }
   return true;
}

void WgSocket::SetSocket(SOCKET socket)
//說明:設定連線的socket
//輸入:socket = 連線的socket
{
   Close();
   m_Socket = socket;
}

如果要得知Client端的ip位址, 可以從accept傳回的from結構裡取得傳出. accept一但呼叫, 必須要等到Client端有連線過來時才會返回. 如果Server程式想要結束, 可以用LocalHost的方式對自己開啟一個連線, 即可由accept返回, 然後再進行其他處理.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 04:28:29
*** 資料的傳接

int WgSocket::WaitInputData(int seconds)
//說明:等待對方送來資料
//輸入:seconds = 等待秒數
//傳回:沒有資料傳回false
{
   if (!IsOpened()) return false;
   // 設定descriptor sets //
   fd_set socket_set;
   FD_ZERO(&socket_set);
   FD_SET((unsigned int)m_Socket,&socket_set);
   // 設定timeout時間 //
   struct timeval timeout;
   timeout.tv_sec = seconds;
   timeout.tv_usec = 0;
   // 偵測是否有資料 //
   try
   {
     if (select(FD_SETSIZE,&socket_set,NULL,NULL,&timeout) <= 0) return false;
   }
   catch(...)
   {
     return false;
   }
   return true;
}

由於read函數必須等到對方有送來資料時才會返回, 因此在呼叫read前最好先偵測是否有資料進來, 以便進行timeout處理. 這裡我只以"秒"為timeout的基準, 需要更細微的時間, 請自行修改.

bool WgSocket::Read(void* data, wglong len, wglong &ret_len)
//說明:讀取資料
//輸入:data, len = 資料緩衝區與大小
//輸出:data = 讀取的資料, ret_len = 實際讀取的資料大小,0表對方已斷線
//傳回:失敗傳回false
//備註:本函數會一直等到有讀取資料或結束連線時才傳回
{
   ret_len = 0;
   if (!IsOpened()) return true;
   try
   {
#ifndef WINDOWS
     signal(SIGPIPE,SIG_IGN); // 避免SIGPIPE訊號終止程式 //
#endif
     ret_len = recv(m_Socket,(char*)data,len,0);
   }
   catch(...)
   {
     ret_len = SOCKET_ERROR;
   }
   if (ret_len < 0)
   {
    ret_len = 0;
    return false;
   }
   return true;
}

bool WgSocket::Write(const void* data, wglong len)
//說明:送出資料
//輸入:data, len = 資料緩衝區與大小
//傳回:失敗傳回false
{
   if (!IsOpened()) return false;
   if (len <= 0) return true;
   int write_len;
   try
   {
#ifndef WINDOWS
     signal(SIGPIPE,SIG_IGN); // 避免SIGPIPE訊號終止程式 //
#endif
     write_len = send(m_Socket,(const char*)data,len,0);
   }
   catch(...)
   {
    write_len = SOCKET_ERROR;
   }
   if (write_len != len) return false;
   return true;
}

注意這裡我加了一個signal將SIGPIPE訊號停掉. 在Unix上, 若對方已斷線, 而己方還
送出資料時, 便會引發一個SIGPIPE訊號, 這個訊號的內定處理方式便是終止程式. 如果不加上這行, 程式便會常常無預警地跳出結束. 原則上, 接收時並不會引發這個訊號, 但為保險起見, 我還是加上去.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 04:38:43
*** 取得本機的ip

bool WgSocket::GetHostIP(const char* hostname, int &ip1, int &ip2, int &ip3, int &ip4)
//說明:取得指定host的ip
//輸入:hostname = host位址
//輸出:ip1-4 = ip位址
//傳回:失敗傳回false
{
if (IsLocalHost(hostname))
{
// 先取出實際的hostname //
struct hostent *hostinfo = gethostbyname("localhost");
if (hostinfo == NULL) return false;
hostname = hostinfo->h_name;
}
struct hostent* hostinfo = gethostbyname(hostname);
if (hostinfo == NULL) return false;
char* addr = hostinfo->h_addr_list[0];
ip1 = (unsigned char) addr[0];
ip2 = (unsigned char) addr[1];
ip3 = (unsigned char) addr[2];
ip4 = (unsigned char) addr[3];
return true;
}

有時我們需要取得目前所在本機的ip位址,這個函數便可以做到這一點. 當要取localhost的ip時,要先取得本機實際的hostname (否則取到的ip一律都是127.0.0.1),然後再展開該hostname以取到實際的ip.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 05:00:37
*** Nagle Algorithm

bool WgSocket::SetNoDelay(void)
//說明:設定不延遲傳送 (停掉Nagle Algorithm)
//傳回:設定失敗傳回false
{
   if (!IsOpen()) return false;
   int on = 1;
   if (setsockopt(m_Socket,IPPROTO_TCP,TCP_NODELAY,(char*)&on,sizeof(on)) != 0) return false;
   return true;
}

Nagle Algorithm的詳細說明, 請參考MSDN "Nagle Algorithm"一文, 這個演算法主要是避免過多零散的送出資料, 將之收集後再一次送出. 對於非講究效率且非一次性資料送出的通訊程式而言 (例如TTY, telnet等), 這個演算法可以大量降低網路的資料傳輸量. 但若是已設計好一次性封包的通訊軟體而言, 這個演算法反而會嚴重影響效率. 在

http://www-128.ibm.com/developerworks/linux/library/l-hisock.html?ca=dgr-lnxw01BoostSocket

這篇所提到4種增加Linux通訊效率的方法中, 其中前2種也是適用於Windows系統的:

1.將所有要送出的資料集結在一起, 然後一次透過send函數送出. MSDN裡也有提到, 儘可能不要採用Send-Send-Receive的資料傳接方式.
2.停掉Nagle Algorithm, 這也是本函數的主要目的.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 05:04:04
基礎知識大抵講完, 通訊協定的部份後面再慢慢補, 趕工去...
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 05:11:53
忘了說明一點, 這些程式是從我的程式抽離出來的, 錯誤處理部份都拿掉了, 有些可能沒有修得很好, compile時可能會有錯誤, 請儘可能自己修一下, 或將問題po出來, 我再說明如何改.

剛剛看了一下, wglong這個形態忘記修正好, 這是我自訂的"4 byte long"形態, 直接改成long便可以了.

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 05:24:26
以下是一個很簡單的測試程式 (我現在暫時沒時間去測, 有心的人先試用看看吧):

client端:

int main(int argc, char* argv[])
{
   WgSocket socket;
   if (!socket.Open(NULL,12345)) return 1; // 連到本機的12345這個port //
   const char* s = "this is a test.";
   if (!socket.Send(s,strlen(s))) return 1;
   return 0; // WgSocket解構時會自動斷線 //
}

server端:

void process_client(SOCKET socket)
{
   WgSocket client_socket;
   client_socket.SetSocket(socket);
   // 每次讀取1 byte然後顯示出來 //
   for (;;)
   {
     if (client_socket.WaitInputData(10)) break; // 10秒沒回應便斷線 //
     char data;
     long ret_len;
     if (!client_socket.Read(&data,1,ret_len)) break;
     if (ret_len == 0) break; // 對方已斷線 //
     printf("%c",data);
   }
   client_socket.Close();
}

int main(int argc, char* argv[])
{
   WgSocket server_socket;
   if (!server_socket.Listen(12345)) return false;
   for (;;) // 永久執行 //
   {
     SOCKET socket;
     if (!server_socket.Accept(socket)) continue;
     process_client(socket);
   }
}

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/3 下午 05:28:40
> if (client_socket.WaitInputData(10)) break; // 10秒沒回應便斷線 //

這裡少了not... 打太快了.

if (!client_socket.WaitInputData(10)) break; // 10秒沒回應便斷線 //
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/5 上午 07:05:13
原本工作告一段落, 想多寫一些, 但回覆其他的發問太久了, 反而忘了這邊, 現在差不多該睡了. 不過, 有在看的人至少推一下吧, 不然沒動力 (只需要推到前100我看到即可). 原本這篇已經掉到99了, 是我寫了這篇才又推上來. 如果大家都覺得沒什麼, 那麼就讓它掉下去吧, 我也不必再多寫什麼了... (我只看最近的100篇, 其餘的一概不管, 如果這篇掉下去, 我就不繼續寫下去了...)


作者 : yohji(阿泰) C++ Builder卓越專家貼文超過500則
[ 貼文 566 | 人氣 3153 | 評價 3150 | 評價/貼文 5.57 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/5 上午 09:17:46
感謝前輩的付出,受益良多
作者 : arex_huang(小小孩) 貼文超過200則
[ 貼文 339 | 人氣 5660 | 評價 1210 | 評價/貼文 3.57 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/5 上午 09:35:29
推推推.....

感謝先進對後輩的不吝賜教...
希望這是拋磚引玉...
作者 : theboy(小猴子) VC++優秀好手貼文超過200則
[ 貼文 350 | 人氣 8375 | 評價 960 | 評價/貼文 2.74 | 送出評價 17 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/5 上午 11:41:15
感謝青杉大大的教學...
受用無窮...
期待更多的此類文章...
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/5 下午 06:04:39
*** 通訊協定與封包

一般而言, 非connect-alive的通訊協定比較簡單, 且容易除錯, 但以效率性而言, connect-alive方式的效率會比較好 (可以省下每次連線的20-30ms時間). 以下是非connect-alive的通訊方式:

client端:

連線
送出封包
等待並接收回傳封包
斷線

server端 (accept到client端的連線後):

接收封包
處理服務
送出處理結果的封包
斷線

client端也可以加入重送機制 (因為網路通訊是不穩定的, 可能偶爾斷線), 也就是前述的client端的傳接過程有問題, 便再呼叫一次. 至於server端accept後的通訊服務處理, 通常都是另開執行緒來處理, 這部份後面再來說明.

connect-alive通訊比較麻煩些:

client端:

for (int i=0; i<最大重試次數; i++)
{
if 還沒連線 then 連線
送出封包
等待ACK
if 沒收到ACK then 斷線, 繼續重試;
else 等待並接收回傳封包, 返回
}

server端 (accept到client端的連線後):

for (;;)
{
  等待並接收封包, 若逾時或對方斷線, 便斷線返回
   送出ACK
   處理服務
   送出處理結果的封包
}

由於connect-alive通訊方式, 可能會受到雜訊的干擾, 因此封包中需要有leading code或ending code, 來確認這是封包的起頭或結尾. ACK可有可無, 有的話, 比較容易確認封包資料是否完整送達. 以下是一些封包的例子:

HTTP: 以"\r\n\r\n"做為request header或response header結束.
FTP: 以"\r\n"做為服務封包的結束.
SMTP: 以"\r\n"做為服務封包的結束.

這些都是以ansi字串為命令的protocol, 基本上都是以"\r\n"做為封包的結束. 但一般的封包設計 (傳送複雜的binary資料時), 大抵不會採用這種方式, 而是:

leading code
資料大小
資料檢查碼
實際資料

Server端在接收服務封包時, 應先略過所有資料, 直到遇到leading code時, 才開始正式接收封包. leading code與ACK, 通常是4 byte long, 數值可以自己設計, 愈不跟其他資料重複的愈好. 資料檢查碼可有可無, 因為tcp封包本身就已有進行checksum的檢查 (參閱rfc793), 不過有的話, 比較能確認是正確的封包. 檢查碼一般都是採用CRC32.

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/5 下午 06:10:59
如果是跨平台傳接binary資料時, 應先定義好是low byte first還是high byte first格式. 例如採用low byte first格式時, 送出ACK應該是:

WgSocket socket;
...
long ack = 0x12345678;
ack = ReadInverseLong(ack);
socket.Write(&ack,sizeof(long));

接收ACK則是:

long ack;
if (!receive_data(&ack,sizeof(long))) return false;
ack = ReadInverseLong(ack);

bool receive_data(WgSocket &socket, int len, void* data, int timeout)
//說明:接收來自socket的資料
//輸入:len = 接收長度, timeout = 逾時時間
//輸出:data = 資料
//傳回:失敗傳回false
{
  while (len > 0)
  {
    if (!socket.WaitInputData(timeout)) return false;
    long ret_len;
    if (!socket.Read(data,len,ret_len)) return false;
    if (ret_len <= 0) return false;
    len -= ret_len;
    data = ((char*)data) + ret_len;
  }
  return true;
}

這邊的ReadInverseLong便是用來視CPU決定是否做low byte first轉high byte first的處理函數:

#ifdef HIGH_BYTE_FIRST

static inline long ReadInverseLong(const long* s)
//說明:從記憶體中讀出低位元組先,高位元組後的長整數
{
return ((long)((*((unsigned char*)s+3) << 24) | (*((unsigned char*)s+2) << 16) | (*((unsigned char*)s+1) << 8) | *((unsigned char*)s)));
}

#else

static inline wglong ReadInverseLong(const long* s)
{
return *s;
}

#endif

inline long ReadInverseLong(long value) { return ReadInverseLong(&value); }

除了ack外, leading code, 資料大小, 資料檢查碼, 甚至實際資料中若有類似的短整數, 長整數等等, 都應該做這樣的處理, 才能同一個程式適用於各種平台.
作者 : windblown(windblown) VC++優秀好手C++ Builder曠世奇才C++卓越專家貼文超過1000則
[ 貼文 1105 | 人氣 890 | 評價 9400 | 評價/貼文 8.51 | 送出評價 200 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/6 上午 10:22:38
請問青衫兄

>各位應注意到, 我呼叫外界函數時, 都會加上try/catch, 這是為了避免外界函數出問題時, 引發自己程式當掉.

在 windows 系統,我可以理解這種寫法,在 unix/linux 系統,這種寫法也有用嗎?

另外,對於外界函數,均需使用 try/catch, 還是只有特殊函數才需要呢?

感謝您的指導
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/6 上午 10:56:58
>在 windows 系統,我可以理解這種寫法,在 unix/linux 系統,這種寫法也有用嗎?
>另外,對於外界函數,均需使用 try/catch, 還是只有特殊函數才需要呢?

還是有用啊. 使用外部的lib, 個人的習慣是將它看成是"不安全"的, 因此都會加上try/catch. 像以前解pdf檔, 我是用xpdf, 大部份情況是可以解的, 但也有不少檔案會引起access violation, 不用try/catch, 程式就會被它弄掛. notes c++ api引發exception的情況更多. office元件也很常發生. 當然作業系統的函數一般都是安全的, 不過我還是加上去, 這只是個人的習慣性.
作者 : windblown(windblown) VC++優秀好手C++ Builder曠世奇才C++卓越專家貼文超過1000則
[ 貼文 1105 | 人氣 890 | 評價 9400 | 評價/貼文 8.51 | 送出評價 200 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/6 下午 01:20:20

>還是有用啊. 使用外部的lib, 個人的習慣是將它看成是'不安全'的, 因此都會加上try/catch. 當然作業系統的函數一般都是安全的, 不過我還是加上去, 這只是個人的習慣性.

原來如此,這部份倒是沒想過,值得列入考量的一部分!!
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/6 下午 11:30:14
由於傳送整數/浮點數資料時, 其格式會與平台有關, 因此絕大部份共通性的protocol都是採用ansi字串形態的資料封包, 像SMTP/POP3的MIME格式封包便是一例. 不過採用ansi字串做為封包的基礎, 雖然具有跨平台性, 但效率性就比較差一些 (封包會比較大). 如果要講求高效率, 最好自行設計binary資料封包 (tcp封包本身就是binary資料封包).

*** 多重執行緒的考量

前面有提到過, Server端在收到Client端的連線時, 應開啟另一條執行緒來處理與Client端的資料傳接和服務處理:

listen到某個port
for (;;)
{
   accept接收client端連線
   開啟執行緒處理此一client端連線
}

如果不開啟執行緒來處理client端連線, 那麼在處理過程中, 因為沒有立即再呼叫accept, 下個client端連線便必須等到前一個client端連線處理結束, 再次呼叫accept時, 才能連線, 這之間的時間裡都連進不來.

不過使用執行緒也有一些應注意的地方. 對於一個每次client連線都會很長一段時間才斷線的通訊而言 (例如ftp), 採用立即開啟執行緒處理, 斷線後執行緒即結束的方法是可以的. 但如果講求效率, 且連線不久即斷線的通訊而言 (例如http, 可能每秒50次甚至更多的連線), 這種方式便不太適合了. 為什麼呢? 主要因為執行緒的建立與結束都需要不少時間, 特別是執行緒的結束. 如果建立/結束執行緒過於頻繁, 那麼整個通訊效率很可能被它拖慢掉.

在Windows系統裡, 執行緒結束的優先權是很低的, 而且也很花時間, 當採用立即開啟執行緒的處理方式, Client端不斷的連線/斷線, 效率表面上看起來還不錯, 但執行久一點 (可能要十幾個小時以上), 便會開始產生資源不足的錯誤. 如果仔細去分析, 便會發現系統存在有極大量的未結束執行緒 (因為來不及結束).

比較好的做法是, 建出的執行緒並不結束, 而是保留下來做為處理下次Client端連線服務之用. 主accept的迴圈則是先找是否有空的執行緒, 有便直接拿來用, 沒有才建立新的. 當然, 你也可以在Server通訊程式一執行時, 便建立一定數量的執行緒, 當執行緒用完時, accept主迴圈便直接送回system busy之類的錯誤訊息.

至於accept主迴圈與執行緒之間的管理與溝通, 便不在這裡說明了, 請自行閱讀多重執行緒相關的文章.
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/6 下午 11:49:57
本篇暫時告一段落吧, 裡面比較有價值的, 應該是使用上的經驗部份, 基礎的知識比較沒什麼.

如果各位先進有其他可補充之處, 請不吝賜教.
作者 : justice640808(justice640808)
[ 貼文 54 | 人氣 2642 | 評價 150 | 評價/貼文 2.78 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/7 下午 05:14:00
謝謝你的分享
作者 : shyang2k(shyang)
[ 貼文 8 | 人氣 5 | 評價 40 | 評價/貼文 5 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/8 上午 09:30:35
感謝青衫大大的分享........^^
作者 : runangel(Q&A)
[ 貼文 12 | 人氣 1437 | 評價 0 | 評價/貼文 0 | 送出評價 4 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/8 上午 10:49:27
感謝青衫大大的SOCKET通訊教導受益良多!
作者 : runangel(Q&A)
[ 貼文 12 | 人氣 1437 | 評價 0 | 評價/貼文 0 | 送出評價 4 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/8 上午 10:49:33
感謝青衫大大的SOCKET通訊教導受益良多!
作者 : drray(ray)
[ 貼文 155 | 人氣 2252 | 評價 330 | 評價/貼文 2.13 | 送出評價 32 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/9 上午 09:19:58
辛苦了
真佩服能花這樣多的時間
來對我們詳細描述socket
謝謝
作者 : andy1201(阿聖)
[ 貼文 26 | 人氣 5 | 評價 350 | 評價/貼文 13.46 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/12 下午 03:52:07

感謝青衫大大的Socket通訊教學! 不僅更暸解Socket的運作方式,對C++的撰寫習慣也有所提升!

之前在寫Socket程式時,發現不論是Server or Client端先關閉,另一端都不會知道對方已經關閉了,後來我就在Socket關閉前加了通知對方的訊息(特殊字串)…

小弟想請問一下,是否有更好的方法,可以直接知道連線狀態?
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/12 下午 08:24:14
>小弟想請問一下,是否有更好的方法,可以直接知道連線狀態?

連線狀態是不容易知道的, 這有兩種情況:

1.對方確實斷線, 有送FIN封包過來, 這部份比較容易偵測得知.
2.因為對方電腦當掉/重開機/網路斷線等等因素, 造成對方沒有送FIN封包過來, 此時並無法確切得知對方是否斷線

我的解決方式, 是提供一個類似"hello"的命令封包, server端一收到, 便立即回應一個ACK. 如此當client端發出"hello"命令後, 一定時間內未收到ACK, 便視同斷線. 這個方法對於前述兩種情況的運作方式為:

1.對方已確實斷線時, 實際命令並不送出, 用select偵測是否有資料輸入時也會立即返回, 同時於read讀取資料時, 可得到長度為0, 在這個情況便能立即得知對方已斷線.
2.因其他因素造成通訊中斷時, 實際命令依然會送出, 但能否送達並不一定. 因此client端要用select做timeout處理, 當超出一定時間對方沒回應, 便視同斷線 (也可重送再試). 至於這段時間應該多長, 便要看程式通訊的性質為何了.
作者 : andy1201(阿聖)
[ 貼文 26 | 人氣 5 | 評價 350 | 評價/貼文 13.46 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/13 下午 01:29:11
感謝青衫大的指點,一解小弟多月以來的困惑!
作者 : daniel(冷眼)討論區板主 VC++優秀好手遊戲程式設計優秀好手DirectX優秀好手C++優秀好手貼文超過1000則人氣指數超過70000點
[ 貼文 1564 | 人氣 84169 | 評價 6990 | 評價/貼文 4.47 | 送出評價 15 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人chiuinan2註記此篇回應為很有道理 2006/6/13 下午 05:03:36
在cs 架構上還有很多應用...
可以看看sdlnet / ace 都是不錯的網路多平台架構
不過近年來在win os下伺服器開發大都建設使用iocp
一種io port 的socket 模式

在socket中..active packet ..可以用來主動測試svr client是否存活
而且可以用於 時間定位.封包解碼的key

封包訊息可以用 Queue 儲存..防止封包破裂的狀況發生

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/13 下午 06:24:51
>不過近年來在win os下伺服器開發大都建設使用iocp
>一種io port 的socket 模式

簡單說明一下. iocp, 全名為I/O completion Port, 可到MSDN由CreateIoCompletionPort函數查到相關的用法. 基本上它是屬於非同步通訊, 同時與處理執行緒相互結合, 效率會比前述我講的同步模式配上一堆執行緒要來得好 (過多的執行緒, 即使是holding狀態, 也要費去一些context switch的時間).

至於如何整合成可同時適用於Windows與Linux的共同架構, 哦... 還沒想到... @@
作者 : jasper(Jasper)討論區板主 程式設計甘苦談頂尖高手上班族的哈拉園地優秀好手貼文超過1000則人氣指數超過70000點
[ 貼文 1408 | 人氣 96053 | 評價 6990 | 評價/貼文 4.96 | 送出評價 42 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/6/24 上午 08:21:46
每看一次,有多一次的了解,
這主題不該落入第二頁。
作者 : brant(雁)
[ 貼文 22 | 人氣 3911 | 評價 170 | 評價/貼文 7.73 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/7/22 上午 08:26:27

>>不過近年來在win os下伺服器開發大都建設使用iocp
>>一種io port 的socket 模式
>
>簡單說明一下. iocp, 全名為I/O completion Port, 可到MSDN由CreateIoCompletionPort函數查到相關的用法. 基本上它是屬於非同步通訊, 同時與處理執行緒相互結合, 效率會比前述我講的同步模式配上一堆執行緒要來得好 (過多的執行緒, 即使是holding狀態, 也要費去一些context switch的時間).
>
>至於如何整合成可同時適用於Windows與Linux的共同架構, 哦... 還沒想到... @@
>
IOCP應該是無法用在Linux上,因為這個機制是建立在Windows OS的協助之下。
作者 : toadchang(庹德)
[ 貼文 7 | 人氣 490 | 評價 0 | 評價/貼文 0 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/7/26 上午 09:41:01
請問一下,
  如何讓server一直在接收資料狀態?
  我的client一丟訊息過去server, server收到後就無法再連上了
  必須再啟動一次才行.
  以下是我的程式片段:
UpdateData(true);
char REV_buffer[256];
WORD wsaVersion = MAKEWORD(2,0);
WSADATA wsaData;
WSAStartup(wsaVersion, &wsaData);
    

// set socket
SOCKET listen_socket, remote_socket;
listen_socket=socket(AF_INET, SOCK_STREAM, 0);

  // set network configuration
struct sockaddr_in Server;
Server.sin_family = AF_INET;
Server.sin_addr.s_addr = inet_addr("localhost");
Server.sin_port = htons(IPPORT_ECHO);

   
bind(listen_socket, (struct sockaddr *)&Server, sizeof(struct sockaddr));
listen(listen_socket, 1);
    
// wait for client
     remote_socket = accept(listen_socket, NULL, NULL);
    
// receive data
recv(remote_socket, REV_buffer, sizeof(REV_buffer), 0);
m_strMessage = REV_buffer;
UpdateData(false);

// terminate connection
closesocket(remote_socket);
closesocket(listen_socket);

WSACleanup();

麻煩指教一下, 謝謝
作者 : toadchang(庹德)
[ 貼文 7 | 人氣 490 | 評價 0 | 評價/貼文 0 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/7/26 上午 10:14:53
我發現在這裡用
for(;;){
> // wait for client
> remote_socket = accept(listen_socket, NULL, NULL);
>
> // receive data
> recv(remote_socket, REV_buffer, sizeof(REV_buffer), 0);
> m_strMessage = REV_buffer;
> UpdateData(false);
     }
     然後將顯示的方法以MessageBox(("%s",REV_buffer))的方法顯示就沒問題了
     是否REV_buffer的資料送到CString m_strMessage出現問題?
     不過不管什麼方式 在接收完資料後程式都無法正常結束
作者 : theboy(小猴子) VC++優秀好手貼文超過200則
[ 貼文 350 | 人氣 8375 | 評價 960 | 評價/貼文 2.74 | 送出評價 17 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/7/26 上午 11:21:46

>我發現在這裡用
>for(;;){
>> // wait for client
>> remote_socket = accept(listen_socket, NULL, NULL);
>>
>> // receive data
>> recv(remote_socket, REV_buffer, sizeof(REV_buffer), 0);
>> m_strMessage = REV_buffer;
>> UpdateData(false);
> }
> 然後將顯示的方法以MessageBox(('%s',REV_buffer))的方法顯示就沒問題了
> 是否REV_buffer的資料送到CString m_strMessage出現問題?
> 不過不管什麼方式 在接收完資料後程式都無法正常結束
>

樓上這位大大...你的問題請另外開新主題來討論會比較恰當..
這篇是青杉大大的教學文章..
你這樣混在裡面發表...
不太好....
作者 : toast(吐司)
[ 貼文 1 | 人氣 5 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2006/9/21 上午 12:54:59

>** 基本參數的定義與應include的header file:
>
>#ifdef WINDOWS
> #include <winsock2.h>
>#else
> #include <sys/socket.h>
> #include <netinet/in.h>
^^^^^^^^^^^^^^^^^^^^^^^
請問一下青杉大大,我在寫簡單的server-client的socket時
上面這兩行會找不到?可以忽略掉這兩行嗎?
忘了說,我執行的平台是.NET2005 VC,寫出來的是for WINDOWS

> #include <netdb.h>
> #include <errno.h>
> typedef int SOCKET;
> #define SOCKET_ERROR -1
> #define INVALID_SOCKET -1
>#endif
>
>這些定義, 主要是方便程式的相容性 (for Windows and Unix). 以下是我寫的Socket通訊物件, 後面會再說明各函數的用途與寫法.
>
>class WgSocket
>{
>private:
> SOCKET m_Socket;
>public:
> static bool Initialize(void);
> static void Terminate(void);
> static bool IsLocalHost(const char* hostname);
> static bool GetHostIP(const char* hostname, int &ip1, int &ip2, int &ip3, int &ip4);
>public:
> WgSocket(void);
> ~WgSocket();
> bool IsOpened(void) const;
> void SetSocket(SOCKET socket);
> bool Open(const char* hostname, int port);
> void Close(void);
> bool WaitInputData(int seconds);
> bool Read(void* buffer, wglong len, wglong &ret_len);
> bool Write(const void* buffer, wglong len);
> bool Listen(int port);
> bool Accept(SOCKET &socket);
> bool SetNoDelay(void);
>};
>
>
作者 : hfuman(王子麵)
[ 貼文 17 | 人氣 2829 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/1/10 上午 10:57:16
這篇還有人在看嗎

可能我參予的比較晚

想問都沒有人可以問了

有大大願意貼完整的程式碼嗎

真的很想學
作者 : theboy(小猴子) VC++優秀好手貼文超過200則
[ 貼文 350 | 人氣 8375 | 評價 960 | 評價/貼文 2.74 | 送出評價 17 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/6/5 下午 07:12:28
剛剛看到有人問要怎麼傳 struct
找了一下這篇....幫推一下....


作者 : seiyaku(seiyaku)
[ 貼文 13 | 人氣 3896 | 評價 0 | 評價/貼文 0 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/8/15 上午 01:38:10
看過 全華出版的 簡單易懂 網路程式設計 再看這篇文章 收穫很多^ ^"
作者 : lintaichung2005(光) 人氣指數超過30000點
[ 貼文 173 | 人氣 47573 | 評價 0 | 評價/貼文 0 | 送出評價 35 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/9/11 下午 05:28:13
感謝青杉大的指導,獲益良多!!

只是請問手機上也是這樣做嗎?

關於 ssl 的部份可以用得上嗎?

謝謝您!!
作者 : uu_optimal(optimal) 人氣指數超過10000點
[ 貼文 136 | 人氣 10990 | 評價 50 | 評價/貼文 0.37 | 送出評價 18 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/1/13 下午 09:06:03
非常感謝青衫的分享,我們才有這些學習機會。
作者 : ireullin(Ireul) 人氣指數超過10000點
[ 貼文 179 | 人氣 28144 | 評價 30 | 評價/貼文 0.17 | 送出評價 22 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/1/16 上午 09:15:43
非常有用的文章
感謝前輩不吝嗇的分享
加油喔~~~
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 450 | 人氣 211 | 評價 930 | 評價/貼文 2.07 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/1/24 上午 01:37:31

>通常當Server端沒有開機時, 會傳回連線逾時, 大約需等數十秒才會傳回, 如何縮短這個逾時時間, 我並沒有深入去研究, 主要是很少碰到. 如果Server端有開機, 但Server通訊程式沒執行, 則會立即傳回無法連線. 由於每次連線需要約20-30ms, 因此對於講求效率的通訊程式而言, 一般會採用connect-alive方式處理, 這部份後面再來說明. 至於WSAEADDRINUSE的實際意義, 就是所有可用的port都處理TIME_WAIT狀態, 無法再拿來使用. 關於port的狀態值, 下面是相關的基礎知識.

以下的方式可以控制 Connect 的逾時時間
BOOL CGXSocket::Connect( const SOCKADDR *lpSockAddr, int nSockAddrLen )
{
timeval timeTimeout;
fd_set writefds;
DWORD dwNoneBlock;
int iError;

if( m_hSocket == INVALID_SOCKET )
{
SetLastError( ERROR_INVALID_HANDLE );
Error( "Connect() = %s", GXGetLastErrorMessage() );
return false;
}
dwNoneBlock = 1;
// switch this socket to none blocking mode, so we can
// set the timeout value, otherwise it will block at connect()
if( !IOCtl( FIONBIO, &dwNoneBlock ) )
return false;
dwNoneBlock = 0;
if( connect( m_hSocket, lpSockAddr, nSockAddrLen ) == SOCKET_ERROR )
{
if( GetLastError() != WSAEWOULDBLOCK )
{
Error( "Connect() = %s", GXGetLastErrorMessage() );
IOCtl( FIONBIO, &dwNoneBlock );
return false;
}
}

FD_ZERO( &writefds );
FD_SET( m_hSocket, &writefds );
timeTimeout.tv_sec = m_nTimeout / 1000;
timeTimeout.tv_usec = m_nTimeout % 1000;

// check if we are really connected
iError = select( 0, NULL, &writefds, NULL, &timeTimeout );
if( iError == 0 )
{
SetLastError( WSAETIMEDOUT );
iError = SOCKET_ERROR;
}
if( iError == SOCKET_ERROR )
{
Error( "Connect() = %s", GXGetLastErrorMessage() );
IOCtl( FIONBIO, &dwNoneBlock );
return false;
}
// switch this socket to blocking mode
IOCtl( FIONBIO, &dwNoneBlock );
return true;
}

BOOL CGXSocket::IOCtl( long lCommand, DWORD *lpArgument )
{
if( m_hSocket == INVALID_SOCKET )
{
SetLastError( ERROR_INVALID_HANDLE );
Error( "IOCtl() = %s", GXGetLastErrorMessage() );
return false;
}
if( ioctlsocket( m_hSocket, lCommand, lpArgument ) == SOCKET_ERROR )
{
Error( "IOCtl() = %s", GXGetLastErrorMessage() );
return false;
}
return true;
}



>由於socket port進入TIME_WAIT狀態時, 必須等待一段時間後才能再度被使用, 因此如果有大量的連線/斷線, 往往會導致所有的socket port都被用光了. 這時便需要修改Windows的設定, 增加可使用的socket port數量, 或縮短TIME_WAIT的等待時間. 更改的方式是設定register key:
>
>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Paremeters
>
>裡面有兩個參數:
>
>MaxUserPort: 最大可用的socket port數目, 將之改成65534
>TcpTimedWaitDelay: TIME_WAIT的等待時間, 將之改成30 (秒)
>
>Unix方面還沒遇過有類似的問題.

除了改 Registry 外, 將 socket 設成 TCP_NODELAY 也可避免 socket port 被用光的問題


作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/1/24 上午 09:00:19
受教... 我再實驗看看. 謝啦.

ps. 站上給予評價或給全部回饋都怪怪的... 一給就全跳回首頁了... @@"
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/1/24 上午 09:09:50
仔細看了一下, TCP_NODELAY用來diable Nagle Algorithm, 因此你可能有誤解喔.

至於利用Nonblock方式來偵測block連線的timeout方式, 我是沒想過. 也許值得試看看. 最近比較忙, 希望有人幫忙驗證一下...

作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 450 | 人氣 211 | 評價 930 | 評價/貼文 2.07 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/1/25 上午 02:58:30
> 除了改 Registry 外, 將 socket 設成 TCP_NODELAY 也可避免 socket port 被用光的問題

>仔細看了一下, TCP_NODELAY用來diable Nagle Algorithm, 因此你可能有誤解喔.
>

或許是設成 TCP_NODELAY 後, TIME_WAIT 的等待時間變短了, socket close 後就可以 release 出來再被使用
以前試過這樣可以避免 socket port 被用光的情況
作者 : howdz(howdz)
[ 貼文 38 | 人氣 5177 | 評價 0 | 評價/貼文 0 | 送出評價 7 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/6/16 下午 02:01:15
很冒昧想請問一個關於 socket 共用的問題,
我有在本站的網路技術討論區提問,但沒有回應,
因為怕佔用版面、分類不符,所以在這邊就用連結的方式:
http://www.programmer-club.com.tw/ShowSameTitleN/nettech/2456.html

主要的問題是不同程式間的 socket 共享 (利用WSADuplicateSocket),
謝謝。
作者 : kenboyboy38(小劉)
[ 貼文 16 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 3 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/24 上午 11:16:10
>1.對方已確實斷線時, 實際命令並不送出, 用select偵測是否有資料輸入時也會立即返回, 同時於read讀取資料時, 可得到長度為0, 在這個情況便能立即得知對方已斷線.


我是linux c 的系統,我不太了解您的描述,可否提供一個範例程式? 感謝你!
作者 : kenboyboy38(小劉)
[ 貼文 16 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 3 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/24 上午 11:16:26
>1.對方已確實斷線時, 實際命令並不送出, 用select偵測是否有資料輸入時也會立即返回, 同時於read讀取資料時, 可得到長度為0, 在這個情況便能立即得知對方已斷線.


我是linux c 的系統,我不太了解您的描述,可否提供一個範例程式? 感謝你!
作者 : kenboyboy38(小劉)
[ 貼文 16 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 3 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/24 上午 11:20:52
> 1.對方已確實斷線時, 實際命令並不送出, 用select偵測是否有資料輸入時也會立即返回, 同時於read讀取資料時, 可得到長度為0, 在這個情況便能立即得知對方已斷線.


小弟我是linux系統,不太了解文章的描述,可否提供一段範例程式,感恩!
作者 : stevenbocheng(老乌;龟;)
[ 貼文 1 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/4/2 下午 04:07:27
乌龟是你吗?
我是老乌龟。
交大77级
是的话请与我联系!!!!
stevenbocheng@cathaylife.cn
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/4/5 上午 07:43:09
哇哩咧, 同班同學, 來這裡找人... @@||| (不是有校友會嗎...)

---------

為避免本篇愈來愈離題, 以下的"所有"留言, 一概不回覆與處理. 請另起一篇提問.
作者 : puremonkey2009(Benjamin)
[ 貼文 2 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/4/8 下午 04:50:38
感謝青衫大大的分享, 我有稍作整理. 有興趣的可以參考 :
http://puremonkey2010.blogspot.com/2011/04/c-socket.html
 板主 : 青衫 , Raymond
 > Visual C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - Visual C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
Visual C++
1 青衫 11070 
2 Raymond 10090 
3 Clier 7630 
4 小約翰 2500 
5 Cog 2030 
6 coco 1870 
7 aming 1410 
8 牧童哥 1400 
9 r2109 1380 
10 Akira 1350 
Visual C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
0.53125