CCESocket: a general purpose TCP/UDP socket class for WinCE
2014-05-07 22:57
621 查看
http://www.codeproject.com/Articles/14585/CCESocket-a-general-purpose-TCP-UDP-socket-class-f
Introduction
Background
Using
the code
Creating
a socket
Making
a client socket
Making
a server socket
A
quick example
Disconnecting
Notifications
Reading
data
Sending
data
Other
functionalities
This class is designed to replace the buggy MFC
even on Win32 systems (tested on Win2K and WinXP).
It allows to create both TCP or UDP socket types operating in client or server configuration, integrates a dynamic buffer that stores all incoming data allowing delayed access, and provides async events notification.
Asynchronous notifications are triggered for:
Incoming data
Closed connections
Accepted clients (only for TCP)
To read incoming data, there are four possibilities:
Direct read: read data as they arrive. In this way, data is not stored in the internal buffer.
Binary read.
Text read: it's possible to read single strings.
Packet read: you can read single packets, exactly as they were received.
To send data, you can choose between:
Binary mode.
String mode.
String mode with auto EOL termination.
Other functions let you specify the string EOL format, to query socket state, to change UDP or TCP receiving buffer, and to query internal data buffer state.
The first time I wrote a network application for a PDA, I realized that the
useless. It not only lacks asynchronous notifications but it also consumes more resources than is really necessary, and must be wrapped in the
to use advanced load/store operations. So I decided to write a completely new socket wrapper for the WinCE OS that is small, efficient, general purpose, and with advanced read/send functions. I use it extensively in a human robot interface for the RoboCup
Robot@Home league.
Before explaining in detail how to use every single function of the class, I think you should known that the
two independent threads, one for receiving data, and another for accepting connections (if the socket is accepting). Both threads use blocking calls to Winsock API functions to minimize CPU usage. You should be aware of the existence of these threads because
they call events (virtual functions, see Notifications),
so when you receive an event, you're on a different thread than the main one.
To use
Collapse | Copy
Code
TCP, or
the default values will be used: 1024 bytes for TCP, and 2048 bytes for UDP.
Return value is
an error occurs. You can always query the last error code with the function:
Collapse | Copy
Code
Now, you can decide to make a client or a server socket.
To make a client socket, call:
Collapse | Copy
Code
name.
Return value is
an error occurs.
TCP socket: this function really connects the two computers. In this case, if you want to make a new connection, you have to disconnect the
socket first.
UDP socket:
establish a real connection, it only saves the passed address to the sent data. In this case, you can reconnect to another address without first disconnecting.
Now, you can use your socket to send and read data.
However, you should first learn how notifications (events)
work, otherwise you won't know when new data is available for reading or if your socket is disconnected for some reason.
To make a server socket, call:
Collapse | Copy
Code
Return value is
successful,
UDP socket: when a UDP packet is received from a client,
trigger a normal
After receiving this data, you can use the same socket to send data
to the remote client. UDP acts blindly, you never know if someone is really listening, and doesn't require service sockets like TCP does.
TCP socket: when a new connection is accepted,
trigger the
Collapse | Copy
Code
It provides a service socket that can be used to accept the connection.
You'll notice that this is a virtual function. All events are virtual functions. In fact, you cannot use
is, you must subclass it and redefine virtual functions for the events that you want to catch.
So, when
you can refuse the connection, returning
If you accept the connection, you must create a new (subclassed)
(you do not have to call the
Collapse | Copy
Code
Now, you can use this new socket to receive and send messages.
A quick example (for TCP)
This is a quick example to explain the
Collapse | Copy
Code
and
code is dirty and is intended only to show the necessary steps to set up a server):
Collapse | Copy
Code
and
and
However, I only defined the
any comment
To disconnect a socket, you can call:
Collapse | Copy
Code
After a disconnect, you must call
We have already seen the
To receive these events, you have to subclass
class.
As soon as you get new data, the following event is called (if you have redefined its virtual function):
Collapse | Copy
Code
This notification is called directly from the receiving thread to notify that there is new data in the queue. This is the first notification of this kind, and offers the possibility to get the data without buffering it. If you accept the packet, then return
In this case, you're responsible for deleting the buffer after its use. If you refuse the data, then return
In this case, the packet will be stored in the internal buffer and you'll receive a new notification:
Collapse | Copy
Code
This event only notifies you that there is data in the buffer. I'll show you later how to read data
from the buffer.
If a connection closes, for any reason, you receive:
Collapse | Copy
Code
TCP socket: you receive
the other peer disconnects. If you are a client and wants to make a new connection, call
If you are a server, then simply release (delete) the service socket.
UDP socket: you receive
the other peer "disconnects". This happens only if you try to send data to a disconnected peer (to my knowledge, this should not happen, however, this is what I experimented using the Winsock APIs). If you are a client and wants to make a new connection, call
If you are a server, you can use or ignore the event, it doesn't interfere with the server operation. Note that if you continue to send data to a disconnected peer,
ignore your trials, and send functions
will return 0 sent bytes.
If your socket is a server and, for some reason, the read (UDP) or accept (TCP) thread fails, both TCP or UDP sockets will return an
In this case, to respawn the server, you must call
Collapse | Copy
Code
As stated at the beginning, keep in mind that these functions are called from another thread, not from the main application thread. If you need to execute something in the window thread, you should send a message to it with
seen in the
This is required for MFC objects. They don't work if passed among threads, you should use these objects in the same thread where they are defined.
You already know one of the four ways to read data: direct read through the first
The remaining functions access data directly from the internal buffer. You'll typically call one of them after receiving the
Data remains available even after a socket disconnection.
You can read binary data using:
Collapse | Copy
Code
Return value is the number of bytes actually read.
To read a string, use the following function:
Collapse | Copy
Code
Return value is
there was no string to read. A string is an array of bytes that begins at the current buffer location and terminates with an EOL. This value can be set with the
(see later).
To read the head packet stored in the buffer, use:
Collapse | Copy
Code
buffer will be internally allocated to match the packet size. You have to delete it after its use.
Return value is
there was no packet to retrieve.
You have three functions to send data.
Binary send:
Collapse | Copy
Code
Return value is the number of bytes sent, or
TCP socket.
To send a string, use one of the following functions:
Collapse | Copy
Code
Both send the string
(see later).
Data query functions
Collapse | Copy
Code
Returns the total data size queued in the buffer.
Collapse | Copy
Code
Returns the number of packets queued in the buffer.
Collapse | Copy
Code
Returns the size of the head packet in the queue (next packet to to be retrieved).
Socket query functions
Collapse | Copy
Code
Returns the error code of the last error.
Collapse | Copy
Code
Returns
UDP sockets.
Collapse | Copy
Code
Returns one of the following states:
Other functions
Collapse | Copy
Code
Call this function to modify the buffer size. You can call it at any time, even if the socket is already connected. This is useful if you want to change the buffer size after a
Collapse | Copy
Code
Sets the new EOL format for
Contents
IntroductionBackground
Using
the code
Creating
a socket
Making
a client socket
Making
a server socket
A
quick example
Disconnecting
Notifications
Reading
data
Sending
data
Other
functionalities
Introduction
This class is designed to replace the buggy MFC CCeSocketclass for the WinCE (Pocket PC) platform. However, it works
even on Win32 systems (tested on Win2K and WinXP).
It allows to create both TCP or UDP socket types operating in client or server configuration, integrates a dynamic buffer that stores all incoming data allowing delayed access, and provides async events notification.
Asynchronous notifications are triggered for:
Incoming data
Closed connections
Accepted clients (only for TCP)
To read incoming data, there are four possibilities:
Direct read: read data as they arrive. In this way, data is not stored in the internal buffer.
Binary read.
Text read: it's possible to read single strings.
Packet read: you can read single packets, exactly as they were received.
To send data, you can choose between:
Binary mode.
String mode.
String mode with auto EOL termination.
Other functions let you specify the string EOL format, to query socket state, to change UDP or TCP receiving buffer, and to query internal data buffer state.
Background
The first time I wrote a network application for a PDA, I realized that the CCeSocket, that comes with MFC, was completely
useless. It not only lacks asynchronous notifications but it also consumes more resources than is really necessary, and must be wrapped in the
CSocketFile/
CArchiveframework
to use advanced load/store operations. So I decided to write a completely new socket wrapper for the WinCE OS that is small, efficient, general purpose, and with advanced read/send functions. I use it extensively in a human robot interface for the RoboCup
Robot@Home league.
Using the code
Before explaining in detail how to use every single function of the class, I think you should known that the CCESocketuses
two independent threads, one for receiving data, and another for accepting connections (if the socket is accepting). Both threads use blocking calls to Winsock API functions to minimize CPU usage. You should be aware of the existence of these threads because
they call events (virtual functions, see Notifications),
so when you receive an event, you're on a different thread than the main one.
Creating a socket
To use CCESocket, you first have to call the
Createfunction:
Collapse | Copy
Code
bool Create(int socketType, int bufferSize = 0); //Examples: mySocket->Create(SOCK_STREAM); mySocket->Create(SOCK_DGRAM); mySocket->Create(SOCK_STREAM, 4096);
socketType: can be
SOCK_STREAMfor
TCP, or
SOCK_DGRAMfor UDP.
bufferSize: you can specify the buffer size for incoming packets. Otherwise,
the default values will be used: 1024 bytes for TCP, and 2048 bytes for UDP.
Return value is
TRUEif the socket is created successfully,
FALSEif
an error occurs. You can always query the last error code with the function:
Collapse | Copy
Code
int GetLastError() //returned error is obtained from a WSAGetLastError() call
Now, you can decide to make a client or a server socket.
Making a client socket
To make a client socket, call:Collapse | Copy
Code
bool Connect(CString &addr, UINT remotePort); //Examples: mySocket->Connect("192.168.0.20", 3000); mySocket->Connect("www.someServer.com", 5003);
addr: is the remote host address. It can be an IP number or the host
name.
remotePort: is the remote host port to connect to.
Return value is
TRUEif the connection was successfully established,
FALSEif
an error occurs.
TCP socket: this function really connects the two computers. In this case, if you want to make a new connection, you have to disconnect the
socket first.
UDP socket:
CCESocketdoesn't
establish a real connection, it only saves the passed address to the sent data. In this case, you can reconnect to another address without first disconnecting.
Now, you can use your socket to send and read data.
However, you should first learn how notifications (events)
work, otherwise you won't know when new data is available for reading or if your socket is disconnected for some reason.
Making a server socket
To make a server socket, call:Collapse | Copy
Code
bool Accept(UINT localPort, int maxConn = SOMAXCONN); //Examples: mySocket->Accept(3000); mySocket->Accept(5003, 1);
localPort: is the local port to bind for incoming requests.
maxConn: is the maximum length of the queue for pending connections.
Return value is
TRUEif
Acceptis
successful,
FALSEif an error occurs. This function will bind the socket to a local port waiting for connections.
UDP socket: when a UDP packet is received from a client,
CCESocketwill
trigger a normal
OnReceiveevent (see Notifications).
After receiving this data, you can use the same socket to send data
to the remote client. UDP acts blindly, you never know if someone is really listening, and doesn't require service sockets like TCP does.
TCP socket: when a new connection is accepted,
CCESocketwill
trigger the
OnAcceptevent:
Collapse | Copy
Code
virtual bool OnAccept(SOCKET serviceSocket)
It provides a service socket that can be used to accept the connection.
You'll notice that this is a virtual function. All events are virtual functions. In fact, you cannot use
CCESocketas
is, you must subclass it and redefine virtual functions for the events that you want to catch.
So, when
CCESocketcalls your
OnAcceptfunction,
you can refuse the connection, returning
FALSE, or accept the connection, returning
TRUE.
If you accept the connection, you must create a new (subclassed)
CCESocketand pass the service socket to its
AcceptServiceSocketfunction
(you do not have to call the
Createfunction first):
Collapse | Copy
Code
void AcceptServiceSocket(SOCKET serviceSocket);
Now, you can use this new socket to receive and send messages.
A quick example (for TCP)
This is a quick example to explain the
OnAccept/
AcceptServiceSocketfunctions.
Collapse | Copy
Code
//This is a subclassed CCESocket class CMySocket : public CCESocket { public: CMySocket(); CMySocket(CWnd* parent); virtual ~CMySocket(); void SetParent(CWnd* parent) {m_parent = parent;} virtual bool OnAccept(SOCKET serviceSocket); virtual void OnReceive(); virtual void OnClose(int closeEvent); protected: CWnd* m_parent; }; CMySocket::CMySocket() : CCESocket() { m_parent = NULL; } CMySocket::CMySocket(CWnd* parent) : CCESocket() { m_parent = parent; } CMySocket::~CMySocket() { } bool CMySocket::OnAccept(SOCKET serviceSocket) { if(m_parent) { ::PostMessage(m_parent->m_hWnd, ON_ACCEPT, (WPARAM) serviceSocket, (LPARAM) this); return TRUE; } return FALSE; } void CMySocket::OnReceive() { if(m_parent) ::PostMessage(m_parent->m_hWnd, ON_RECEIVE, NULL, (LPARAM) this); } void CMySocket::OnClose(int closeEvent) { if(m_parent) ::PostMessage(m_parent->m_hWnd, ON_CLOSE, (WPARAM) closeEvent, (LPARAM) this); }
ON_ACCEPT,
ON_RECEIVE,
and
ON_CLOSEare window messages passed to the main application. The latter could be something like this (note: the
code is dirty and is intended only to show the necessary steps to set up a server):
Collapse | Copy
Code
class MyApp : public CDialog { public: ... afx_msg LRESULT OnAccept(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnReceiveData(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnDisconnected(WPARAM wParam, LPARAM lParam); ... protected: CMySocket *m_server; CMySocket *m_serviceSocket; ... }; BOOL MyApp::OnInitDialog() { bool serverStarted; ... m_server = new CMySocket(this); if(serverStarted = m_server->Create(SOCK_STREAM)) serverStarted = m_server->Accept(somePortNumber); if(!serverStarted) //Recovery code ... } LRESULT MyApp::OnAccept(WPARAM wParam, LPARAM lParam) { m_serviceSocket = new CMySocket(this); m_serviceSocket->AcceptServiceSocket((SOCKET) wParam); m_serviceSocket->SendLine("Hello!"); return 0; }
OnAccept,
OnReceiveData,
and
OnDisconnectare triggered by the
ON_ACCEPT,
ON_RECEIVE,
and
ON_CLOSEevents posted by
CMySocket.
However, I only defined the
OnAcceptfunction for this example. I think the code is so simple that it doesn't need
any comment
Disconnecting
To disconnect a socket, you can call:Collapse | Copy
Code
void Disconnect(); //Example: mySocket->Disconnect();
After a disconnect, you must call
Createagain if you want to use the socket again.
Notifications
We have already seen the OnAcceptevent. Let's now analyse
OnReceiveand
OnClose.
To receive these events, you have to subclass
CCESocketand provide new virtual functions, as already seen in the
CMySocketexample
class.
As soon as you get new data, the following event is called (if you have redefined its virtual function):
Collapse | Copy
Code
virtual bool OnReceive(char* buf, int len);
buf: is the received data packet.
len: it is the packet length.
This notification is called directly from the receiving thread to notify that there is new data in the queue. This is the first notification of this kind, and offers the possibility to get the data without buffering it. If you accept the packet, then return
TRUE.
In this case, you're responsible for deleting the buffer after its use. If you refuse the data, then return
FALSE.
In this case, the packet will be stored in the internal buffer and you'll receive a new notification:
Collapse | Copy
Code
virtual void OnReceive();
This event only notifies you that there is data in the buffer. I'll show you later how to read data
from the buffer.
If a connection closes, for any reason, you receive:
Collapse | Copy
Code
virtual void OnClose(int closeEvent);
closeEvent: it is an enum that can be:
CCESocket::EVN_CONNCLOSED(=0)
CCESocket::EVN_CONNLOST(=1)
CCESocket::EVN_SERVERDOWN(=2)
TCP socket: you receive
EVN_CONNCLOSEDif
the other peer disconnects. If you are a client and wants to make a new connection, call
Createfollowed by
Connect.
If you are a server, then simply release (delete) the service socket.
UDP socket: you receive
EVN_CONNLOSTif
the other peer "disconnects". This happens only if you try to send data to a disconnected peer (to my knowledge, this should not happen, however, this is what I experimented using the Winsock APIs). If you are a client and wants to make a new connection, call
Connect.
If you are a server, you can use or ignore the event, it doesn't interfere with the server operation. Note that if you continue to send data to a disconnected peer,
CCESocketwill
ignore your trials, and send functions
will return 0 sent bytes.
If your socket is a server and, for some reason, the read (UDP) or accept (TCP) thread fails, both TCP or UDP sockets will return an
EVN_SERVERDOWNevent.
In this case, to respawn the server, you must call
Create(only if TCP) followed by
Accept:
Collapse | Copy
Code
//Respawning a TCP server: (OnDisconnected //implementation of MyApp example class) LRESULT MyApp::OnDisconnected(WPARAM wParam, LPARAM lParam) { CCESocket *disconnectedSocket = (CCESocket*) lParam; if(disconnectedSocket == m_server) { Sleep(100); if(m_server->Create(SOCK_STREAM)) if(m_server->Accept(somePortNumber)) return 0; //Recovery code } return 0; }
As stated at the beginning, keep in mind that these functions are called from another thread, not from the main application thread. If you need to execute something in the window thread, you should send a message to it with
PostMessage(as
seen in the
CMySocketexample).
This is required for MFC objects. They don't work if passed among threads, you should use these objects in the same thread where they are defined.
Reading data
You already know one of the four ways to read data: direct read through the first OnReceiveevent.
The remaining functions access data directly from the internal buffer. You'll typically call one of them after receiving the
OnReceiveevent.
Data remains available even after a socket disconnection.
You can read binary data using:
Collapse | Copy
Code
int Read(char* buf, int len); //Example: char *buf = NULL; int count; int len = mySocket->GetDataSize(); if(len > 0) { buf = new char[len]; count = mySocket->Read(buf, len); }
buf: the buffer that will receive the data. It must be already allocated.
len: buffer size.
Return value is the number of bytes actually read.
To read a string, use the following function:
Collapse | Copy
Code
bool ReadString(CString &str); //Example: CString str; while(mySocket->GetDataSize() > 0 && mySocket->ReadString(str)) doSomethingWithTheString(str);
str: is the read sting.
Return value is
TRUEif the string was read,
FALSEif
there was no string to read. A string is an array of bytes that begins at the current buffer location and terminates with an EOL. This value can be set with the
SetEolFormatfunction
(see later).
To read the head packet stored in the buffer, use:
Collapse | Copy
Code
bool GetPacket(char*& buf, int* len); //Example: char *buf = NULL; int len; while(mySocket->GetNumPackets() > 0) { if(mySocket->GetPacket(buf, &len)) useTheData(buf, len); if(buf != NULL) delete[] buf; }
buf: a pointer to the buffer that will contain the packet data. The
buffer will be internally allocated to match the packet size. You have to delete it after its use.
len: it will contain the buffer size.
Return value is
TRUEif the packet was successfully retrieved,
FALSEif
there was no packet to retrieve.
Sending data
You have three functions to send data.Binary send:
Collapse | Copy
Code
int Send(const char* buf, int len); //Example: int count; count = mySocket->Send(myBuffer, myBufferSize);
buf: the buffer that contains the data to send.
len: buffer size.
Return value is the number of bytes sent, or
SOCKET_ERRORif an error occurs. Note: you cannot send data with a listening
TCP socket.
To send a string, use one of the following functions:
Collapse | Copy
Code
int Send(CString& str); int SendLine(CString &str); //Examples: CString msg = "My beautiful string"; mySocket->Send(msg); mySocket->Send("Hello!"); mySocket->SendLine(msg);
Both send the string
str. However, the latter adds an EOL to the string. The EOL is set with the
SetEolFormatfunction
(see later).
Other functionalities
Data query functionsCollapse | Copy
Code
int GetDataSize();
Returns the total data size queued in the buffer.
Collapse | Copy
Code
int GetNumPackets();
Returns the number of packets queued in the buffer.
Collapse | Copy
Code
int GetPacketSize();
Returns the size of the head packet in the queue (next packet to to be retrieved).
Socket query functions
Collapse | Copy
Code
int GetLastError(); //returned error is obtained from a WSAGetLastError() call
Returns the error code of the last error.
Collapse | Copy
Code
int GetSocketType();
Returns
SOCK_STREAMfor TCP sockets,
SOCK_DGRAMfor
UDP sockets.
Collapse | Copy
Code
int GetSocketState();
Returns one of the following states:
CCESocket::NONE(=0)
CCESocket::DISCONNECTED(=1)
CCESocket::CREATED(=2)
CCESocket::CONNECTED(=3)
CCESocket::ACCEPTING(=4)
Other functions
Collapse | Copy
Code
void SetBufferSize(int bufSize);
bufSize: new buffer size.
Call this function to modify the buffer size. You can call it at any time, even if the socket is already connected. This is useful if you want to change the buffer size after a
AcceptServiceSocketcall.
Collapse | Copy
Code
void SetEolFormat(eolFormat eol);
eolFormat: may be one the following:
CCESocket::EOL_NULL(=0)
CCESocket::EOL_LFCR(=1)
CCESocket::EOL_CR(=2)
Sets the new EOL format for
SendLineand
ReadStringfunctions.
相关文章推荐
- 【Java TCP/IP Socket】UDP Socket(含代码)
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- TCP UDP Socket
- TCP、UDP、HTTP、Socket、HttpUrlConnection、HttpClient、Volley、OkHttp之间关系
- TCP、UDP、HTTP、SOCKET之间的区别
- 进程通信之 Socket (顺便回顾 TCP UDP)
- SOCKET : TCP和UDP区别的体现??
- HTTP,FTP,TCP,UDP及SOCKET
- 基于Socket的UDP和TCP编程介绍
- TCP/IP、Http、Socket以及UDP
- Socket编程 (异步通讯) (Tcp,Udp) - Part2
- TCP 和 UDP 在socket编程中的区别
- unix编程之socket编程系列之 简单的tcp和udp编程(二)
- [网络] SOCKET, TCP/UDP, HTTP, FTP
- 基于Socket的UDP和TCP编程介绍
- 基于Socket的UDP和TCP编程介绍
- socket UDP TCP 的一些用法
- Android中Socket通信之TCP与UDP传输原理
- 一篇文章看明白 TCP/IP,TCP,UDP,IP,Socket 之间的关系
- TCP和UDP Socket