Your Ad Here

Saturday, January 2, 2010

OstroSoft Winsock

Retrieving web page source, using OstroSoft Winsock Component (oswinsck.dll)
Download project source code

Minimum requirements: Visual Basic 5, oswinsck.dll*
* If you don't have OstroSoft Winsock Component, see installation instructions

1. In Visual Basic create a new Standard EXE project
2. Add a Reference to oswinsck
3. Add controls to the form: txtSource (TextBox), cmdView (CommandButton), txtURL (TextBox), txtProxy (TextBox), chkProxy (Checkbox), Label1 (Label)
4. Enter the following code:

Option Explicit

Dim sServer As String
Dim sPage As String
Dim WithEvents oWinsock As OSWINSCK.Winsock

Private Sub cmdView_Click()
Dim nPort As Long

txtSource.Text = ""
nPort = 80
sServer = Trim(txtURL.Text)
If InStr(sServer, "://") > 0 Then_
sServer = Mid(sServer, InStr(sServer, "://") + 3)
If InStr(sServer, "/") > 1 Then
sPage = Mid(sServer, InStr(sServer, "/"))
sServer = Left(sServer, InStr(sServer, "/") - 1)
If InStr(sPage, "#") > 1 Then _
sPage = Left(sPage, InStr(sPage, "#") - 1)
Else
sPage = "/"
End If
If InStr(sServer, ":") > 1 Then
nPort = Mid(sServer, InStr(sServer, ":") + 1)
sServer = Left(sServer, InStr(sServer, ":") - 1)
End If

Set oWinsock = Nothing
Set oWinsock = CreateObject("OSWINSCK.Winsock")
If chkProxy.Value = 0 Then
oWinsock.Connect sServer, nPort
Else
sPage = "http://" & sServer & sPage
oWinsock.Connect txtProxy.Text, nPort
End If
End Sub

Private Sub oWinsock_OnClose()
oWinsock.CloseWinsock
Set oWinsock = Nothing
End Sub

Private Sub oWinsock_OnConnect()
oWinsock.SendData "GET " & sPage & " HTTP/1.0" & vbCrLf & vbCrLf
End Sub

Private Sub oWinsock_OnDataArrival(ByVal bytesTotal As Long)
Dim sBuffer As String
oWinsock.GetData sBuffer
txtSource.Text = txtSource.Text & sBuffer
End Sub

Private Sub oWinsock_OnError(ByVal Number As Integer, _
Description As String, ByVal Scode As Long, _
ByVal Source As String, ByVal HelpFile As String, _
ByVal HelpContext As Long, CancelDisplay As Boolean)
Debug.Print "Error " & Number & ": " & Description
End Sub

Winsock API

Introduction

Though Microsoft has done a great job wrapping Winsock API, developers are now facing a new challenge; thread-safety and asynchronous calls. Still the developer can get out without handling these issues, soon to realize that the application hangs or behaves unexpectedly due to the server asynchronously replying back, or that the UI will hang till the server responds. The goal of this project is to have a reliable socket library that will serve as a base for all TCP connections; covering most of the common mistakes where others left.
Ways of receiving data

You can receive data in three ways. First, you can block the application thread until a packet has been received, by calling the Receive method of a Socket object. Obviously, this is not a quite good solution for real-world scenarios. The second option is you can go into a loop (busy loop), polling the Socket object to determine if a packet has been received. This is an OK solution except that it will slow down your main application thread every time you tell the socket to poll and wait for a period of time. The third and last option is to receive data asynchronously by telling the socket to BeginReceive data into byte array, and once data has been received, call a special procedure that is set up to receive the data. Of course, by now you can tell what option I�ve chosen.
Working example

In my project, I�ve developed a component where you can just drag and drop into your application and whoo-ya, it's working. Let�s look into how and why it is done in this way.
Connection

The first thing you need to do in socket application is to connect to a specific port. There are three overloads of this function for ease of use:
Collapse

Public Sub Connect(ByVal hostNameOrAddress As String, ByVal port As Int32)
Public Sub Connect(ByVal serverAddress as IPAddress, ByVal port as Int32)
Public Sub Connect(ByVal endPoint As IPEndPoint)

The main function is as follows (error handling in the code snippet is omitted):
Collapse

Public Sub Connect(ByVal endPoint As IPEndPoint)

_mySocket = New Socket(AddressFamily.InterNetwork, _
SocketType.Stream, ProtocolType.Tcp)
_mySocket.Connect(endPoint)

If IsConnected() = True Then
RaiseEvent Connected(True)
End If

Dim bytes(_packetSize - 1) As Byte
try
_mySocket.BeginReceive(bytes, 0, bytes.Length, _
SocketFlags.None, AddressOf ReceiveCallBack, bytes)
Catch ex As Exception
Throw New Exception("Error receiving data", ex)
If IsConnected() = False Then
RaiseEvent Connected(False)
End If
End Try
End Sub

After establishing a connection, we raise a connection event which I will illustrate soon; once raised, we declare an array of bytes to receive the data. Now, the first trick is to use the BeginInReceive. This tells the socket to wait for a received data on another thread -a new one- from the thread pool, ha? Rings a bill? Yes. It is asynchronous, now I have my application running and not hanging to receive a packet. The function receives the bytes array which will be filled, its length, socket flag, an address of the callback function, and a state object. The callback function will be called when either an entire packet is received or the buffer is full. Again, if there is any error, a connection event is raised stating that the connection is lost and there were an error in receiving the data.
What about ReceiveCallback

ReceiveCallback function is the handler which will handle the packet when received. This is the core of the asynchronous operation. Take a look at the code and then I will explain.
Collapse

Private Sub ReceiveCallBack(ByVal ar As IAsyncResult)
Dim bytes() As Byte = ar.AsyncState
Dim numBytes As Int32 = _mySocket.EndReceive(ar)
If numBytes > 0 Then
ReDim Preserve bytes(numBytes - 1)
Dim received As String = _ascii.GetString(bytes)
'--Now we need to raise the received event.

' args() is used to pass an argument from this thread

' to the synchronized container's ui thread.

Dim args(0) As Object
Dim d As New RaiseReceiveEvent(AddressOf OnReceive)
args(0) = received
'--Invoke the private delegate from the thread.

_syncObject.Invoke(d, args)
End If
If IsConnected() = False Then
Dim args() As Object = {False}
Dim d As New RaiseConnectedEvent(AddressOf OnConnected)
_syncObject.Invoke(d, args)
Else
'--Yes, then resize bytes to packet size

ReDim bytes(PacketSize - 1)
'--Call BeginReceive again, catching any error

Try
_mySocket.BeginReceive(bytes, 0, bytes.Length, _
SocketFlags.None, AddressOf ReceiveCallBack, bytes)
Catch ex As Exception
'--Raise the exception event

Dim args() As Object = {ex}
Dim d As New RaiseExceptionEvent(AddressOf OnExcpetion)
_syncObject.Invoke(d, args)
'--If not connected, raise the connected event

If IsConnected() = False Then
args(0) = False
Dim dl As New RaiseConnectedEvent(AddressOf OnConnected)
_syncObject.Invoke(dl, args)
End If
End Try
End If
End Sub

Now that you�ve gone through the code, what do you think? Don�t worry, I will explain. First, we have a local variable to hold what I received from the server. Then I immediately issue EndReceive which will free any resources used by the BeginRecieve to create a new thread, and so forth. I also resize the array of bytes with the actual size. We, then, declare a local delegate object, and instead of modifying the main application (the user interface, for example) inside this component, it raises an event (Received) via OnReceive method. And since the event is raised on the main thread, it�s completely thread-safe. This is done by calling Invoke method from the private object _syncObject. But what is _syncObject? It is of type ISynchronizeInvoke. Ha? Well _syncObject is used to switch the context from the socket thread to the main thread (in this case, the UI component). This is achieved by having the SynchronizingObject property available to the main control. Here is the code for this property:
Collapse

Public Property SynchronizingObject() As _
System.ComponentModel.ISynchronizeInvoke
Get
If _syncObject Is Nothing And Me.DesignMode Then
Dim designer As IDesignerHost = Me.GetService(GetType(IDesignerHost))
If Not (designer Is Nothing) Then
_syncObject = designer.RootComponent
End If
End If
Return _syncObject
End Get
Set(ByVal Value As System.ComponentModel.ISynchronizeInvoke)
If Not Me.DesignMode Then
If Not (_syncObject Is Nothing) And Not (_syncObject Is Value) Then
Throw New Exception("Property ca not be set at run-time")
Else
_syncObject = Value
End If
End If
End Set
End Property

Events

There are three events and so three delegates:

* Connections: fires when a connection is opened or the connection is closed.
* Exception: fires whenever an exception happens.
* Received: fires whenever a packet is received.

password field, address bar

Introduction

The screen keyboard can write all the fields (password field, address bar, Notepad etc.). You can use Ctrl, Shift, Alt, Altgr, Esc, (Alt + f4) or any other keyboard events. You can also use the screen keyboard for opening Notepad, WordPad, Winamp, Outlook, Internet Explorer, Task Manager, CDROM tray, calculator, run window, search, media player and your Hotmail inbox. The program shows your MSN messenger contact list along with the names of all the songs you have listened to, simultaneously. The screen keyboard looks like Microsoft screen keyboard, but it has some extra properties.
Background

My project heavily uses Win32 API (Application Programming Interface) .The screen keyboard contains low level language properties. I have seen similar samples written in C++, but they cannot write every where. I wanted the screen keyboard to be written in VB.NET because I could not find any code here in CodeProject or any other site that fulfilled my requirements. Almost all other similar programs were written in C++, and none of them fully met my requirements. The API text viewer contains all the Windows API functions and constraints. The API text viewer also displays all your requirements when you use it.
Windows API references

* File, Memory, Process, Threading, Time, Console and Comm control(kernel32.dll)
* Windows handling, and Windows UI control(user32.dll)
* Graphics and Imaging(gdi32.dll)
* Audio, Video, and Joystick control (winmm.dll)
* Registry, Event Log, Authentication, and Services(advapi32.dll)
* Printing (winspoll.dll)
* Asian characters support(imm32.dll)
* Executing processes(shell32.dll)
* Winsock, Windows berkley socket support(wsock32.dll)
* WNet* Instrumentation (mpr.dll)
* Common Dialog control(comdlg32.dll)
* Windows Network support(netapi32.dll)
* Windows Compression(lz32.dll)
* Common Controls(comctl32.dll)
* Versioning support (version.dll)
* Object linking and embedding(ole32.dll)

Screen keyboard
API functions for screen keyboard
Collapse

Public Declare Function SetActiveWindow Lib "user32" Alias _
"SetActiveWindow"(ByVal hwnd As Long) As Long

Declare Function keybd_event Lib "user32" Alias "keybd_event" _
(ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, _
ByVal dwExtraInfo As Long) As Long

Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" _
(ByVal lpstrCommand As String, ByVal lpstrReturnString As String, _
ByVal uReturnLength As Integer, ByVal hwndCallback As Integer) _
As Integer

API constraints for screen keyboard
Example
Collapse

Const KEYEVENTF_KEYUP = &H2 ' Release key

Const VK_RETURN = &HD 'enter

Const VK_SHIFT = &H10 'Shift

Const VK_CONTROL = &H11 'Ctrl

Const VK_CAPITAL = &H14 'capslock

Const VK_ESCAPE = &H1B 'escape

etc.

Screen keyboard has 122 constraints. &HD, H10, &H1B etc. are the key�s hexadecimal codes.
Screen keyboard functions
Collapse

Public Function whichbuttonpushed(ByVal _
sender As String) As String

The sender string gets the button's name when you click the button.
Example
Collapse

Select Case _sender
Case "btna"
keybd_event(VK_A, 0, 0, 0) 'send a key

keybd_event(VK_A, 0, KEYEVENTF_KEYUP, 0)'release a key

shiftrelease() 'release shift

altrelease() 'release alt

leftaltrelease() 'release altgr

Case "btnb"
keybd_event(VK_B, 0, 0, 0) 'send b key

keybd_event(VK_B, 0, KEYEVENTF_KEYUP, 0)'release b key

shiftrelease() 'release shift

altrelease() 'release alt

leftaltrelease() 'release altgr

etc.
end select

The following function gets the numlockbutton names and controls the numlock key state:
Collapse

Public Function numlockfunc(ByVal numlock_key As String, _
ByVal open_close As Boolean) As String

Example
Collapse

If open_close = False Then 'numlock opened

Select Case numlock_key
Case "btnnumlockdiv"
keybd_event(VK_DIVIDE, 0, 0, 0)
keybd_event(VK_DIVIDE, 0, KEYEVENTF_KEYUP, 0)
etc.
End Select
Else 'numlock closed

Select Case numlock_key
Case "btnnumlockdiv"
keybd_event(VK_DIVIDE, 0, 0, 0)
keybd_event(VK_DIVIDE, 0, KEYEVENTF_KEYUP, 0)
etc.
End Select
End If

Screen keyboard set focus function

The keyboard sets the focus to the active window when the button is clicked. The active window introducer number is (8975651603260375040). I found this number using the GetActiveWindow function:
Collapse

Public Declare Function GetActiveWindow Lib "user32" _
Alias "GetActiveWindow" () As Long

The following code snippet is very important for the screen keyboard because the function SetActiveWindow sets the focus to the active window.
Example
Collapse

SetActiveWindow(8975651603260375040)

The SetActiveWindow function is used after the button is clicked.
Messenger API

.NET contains Interop.MessengerAPI. I have included Interop.MessengerAPI.dll as reference and used the singin method. In addition to this MessengerAPI contains the openinbox property. Openinbox enables us to go to the Hotmail inbox directly if any one signs in on the computer.
Get the Winamp process
Collapse

Dim processes() As process
Dim process As New process
Dim processstring As String
Sub Timer1_Tick
Dim mycoll As New Collection
processes = process.GetProcesses
For Each process In processes
mycoll.Add(process, process.Id.ToString)
If process.ProcessName = "winamp" Then
processstring = process.MainWindowTitle
'gets song�s name(MainWindowTitle)

Exit For
End If
Next
Me.Text = "MY SCREEN KEYBOARD" & Space(55) & _
DateTime.Now & Space(20) & processstring
End sub

Capture the screen
Collapse

Call keybd_event(System.Windows.Forms.Keys.Snapshot, 0, 0, 0)
System.Threading.Thread.Sleep(200)
' To have time to catch the clipboard

Static i As Integer
Dim data As IDataObject
data = Nothing
data = Clipboard.GetDataObject()
Dim bmap As Bitmap
Dim p As New PictureBox
If data.GetDataPresent(GetType(System.Drawing.Bitmap)) Then
bmap = CType(data.GetData(GetType(System.Drawing.Bitmap)),
Bitmap)
p.Image = bmap
i += 1
p.Image.Save(
"C:\Documents and Settings\All Users\Desktop\Screenshot " _
& i.ToString & " .jpg", Imaging.ImageFormat.Jpeg)
End If
If Not File.Exists(
"C:\Documents and Settings\All Users\Desktop\Screenshot " _
& i.ToString & " .jpg") Then
screenshot()
Else
End If

License

SMPP knowledge

Introduction

I gained some SMPP knowledge from one of my former jobs. After quitting the job, I wrote this library in my leisure time. Basically, what I did was to design the inheritance of the packets in a logic way, so far I think, and encode and decode the packets based on the SMPP specifications. Hope you guys like it.

SMPPLIB is a packet library for sending and receiving SMS messages (2-way SMS) based on SMPP specification v3.4 (partial support). The library is dependent on MFC (but what it depends on is CString and Winsock initialization only, I may strip off the dependence in future release).

The SMPPLIB is a building block for developing Short Message Service (SMS) value added services. It provides a simple solution to solve the interface problem with Short Message Service Center (SMSC).

Basically, there are plenty of classes for most packets defined in the SMPP specification, together with three ESME entities, which are EsmeTransmitter, EsmeReceiver and EsmeTransceiver. Usually, in the SMPP protocol, it works in the way that an ESME sends a command packet to the SMSC, then the SMSC replies with a reply packet. The same holds when the SMSC sends a packet to the ESME to indicate something, then the ESME sends back a reply packet to the SMSC.

Using the API to send a SMS message is easy.
Collapse

EsmeTransmitter trans;

CSmppAddress scr(2, 0, "85291234567");

trans.init("212.5.192.111", 5055);
trans.bind("t_test", "testpwd", "", scr);
trans.submitMessage("Hiya, this is my message", "85291111888", 2, 0);

Usage Instruction

The DLL library is built with SMPPLIB_EXPORTING defined, so when using it, this variable should not be defined. If the symbol is not defined, the classes in the SMPPLIB are imported using __declspec(dllimport).

To use the DLL, just put the Smpplib.dll and Smpplib.lib into a searchable directory of your application, and set your project options to link with Smpplib.lib. You are now ready for using the classes of SMPPLIB.

The SMPPCOM is the project for the COM component, it is dependent on the smpplib.dll built for the SMPPLIB project, so you may have to manually put them together. The SMPPCOM is an ATL project that supports MFC.

TestServer is only a project for me to test the sending/receiving of packets, so that I can verify the fields inside a packet after the complete encode/decode process. In TestServer, packets are processed in processPacket routine, while in SmppLibTest, packets are processed in the registered callback processPacket.
SMPPLIB Classes introduction
CSmppDate

It is the representation in the packets for specifying dates. It encapsulates a SYSTEMTIME as public member and a null indicator as protected member.
Collapse

void setDate(CString sd);
void setDate(SYSTEMTIME sdt);
CString toString();
CSmppDate& operator=(CSmppDate &dt);
int getLength(); void setNull();
bool isNull();

CSmppAddress

It is the representation of ESME address with type of number and numbering plan indicator.
Collapse

CSmppAddress(uint32 adrton, uint32 adrnpi, CString addr);
CSmppAddress& operator=(CSmppAddress& addr);
int getLength();
void setAddrNpi(uint32 adrnpi);
void setAddrTon(uint32 adrton);
void setAddr(CString addr);

CPacketBase

It is the base class of all SMPP packets. It deals with encoding the packet headers and loading the headers. In passing parameter, usually packets are passed by casting into CPacketBase, so for getting back the SMPP packet, they have to be cast to the respective type base on the command_id.

All packets in the library are inherited from CPacketBase, so they all support the following operations:

*
Collapse

uint32 getCommandId();

It gets the command ID from the packets as defined by the SMPP specification.
*
Collapse

uint32 getCommandStatus();

It gets the command status from the packet. Usually for SMSC response packets, a command status of 0 means no error, otherwise it indicates error condition. Detailed error code can be checked from the SMPP 3.4 Specification.
*
Collapse

uint32 getSequenceNumber();

It gets the sequence number for the packet. Sequence number is a monotonic increasing number for request packets and response packets. A response packet should set the same sequence number as the corresponding request packet.
*
Collapse

void setCommandId(uint32 cmid);

It sets the command ID for a packet. In most cases, user shouldn't set it by his own. Correct command ID is assigned as a packet is created. Beware, you shouldn't set command ID, it's been set based on the packet you created.
*
Collapse

void setCommandStatus(uint32 cmst);

It sets the command status of a packet. Command status is defined in the SMPP 3.4 specification.
*
Collapse

void setSequenceNumber(uint32 seqn);

It sets the sequence number of a packet.

SMPP packets supported in the API library

Every response packet in the SMPPLIB can be passing a command packet in the constructor, to set the sequence number of the response packet as the sequence number of the command packet.
CAlertNotification

Alert Notification Packet.
Collapse

CSmppAddress getSource();
CSmppAddress getEsme();

void setSource(CSmppAddress &src);
void setEsme(CSmppAddress &esme);

CGenericNack

Generic Negative Acknowledge Packet.
CBindPacketBase

Base class for Bind packets.
Collapse

CString getSystemId();
CString getPassword();
CString getSystemType();

CSmppAddress& getSourceRange();

void setSystemId(CString sid);
void setPassword(CString pwd);
void setSystemType(CString stype);
void setSourceRange(CSmppAddress &addr);

void setInterfaceVersion(uint32 iver);

CBindRespBase

Base class for Bind Response packets.
Collapse

void setSystemId(CString sid);
CString getSystemId();

CBindTransmitter

Bind Transmitter Packet. Inherited from CBindPacketBase.
CBindTransmitterResp

Bind Transmitter Response Packet. Inherited from CBindRespBase.
CBindReceiver

Bind Receiver Packet. Inherited from CBindPacketBase.
CBindReceiverResp

Bind Receiver Response Packet. Inherited from CBindRespBase.
CBindTransceiver

Bind Transceiver Packet. Inherited from CBindPacketBase.
CBindTransceiverResp

Bind Transceiver Response Packet. Inherited from CBindRespBase.
CUnbind

Unbind Packet.
CUnbindResp

Unbind Response Packet.
CMessagePacketBase

Base class for CSubmitSM and CDeliverSM packets.
Collapse

CString getServiceType();
CSmppAddress getSource();
CSmppAddress getDestination();

uint32 getEsmClass();
uint32 getProtocolId();
uint32 getPriorityFlag();

CSmppDate getScheduledDelivery();
CSmppDate getValidityPeriod();

uint32 getRegisteredDelivery();
uint32 getReplaceIfPresent();

uint32 getSmDefaultMsgId();
uint32 getDataCoding();

uint32 getSmLength();

void getMessage(PBYTE &msg, uint32 &nsz);

//setter

void setServiceType(CString stype);
void setSource(CSmppAddress &src);
void setDestination(CSmppAddress &dest);

void setEsmClass(uint32 esm);
void setProtocolId(uint32 pid);
void setPriorityFlag(uint32 pflag);

void setRegisteredDelivery(uint32 reg);
void setReplaceIfPresent(uint32 rip);
void setDataCoding(uint32 enc);

void setSmLength(uint32 smlen);
void setMessage(PBYTE msg, uint32 nsz);

CMessageRespBase

Base class for CSubmitSMResp and CDeliverSMResp.
Collapse

CString getMessageId();
void setMessageId(CString msgid);

CSubmitSM

Submit Short Message Packet. Inherited from CMessagePacketBase.
CSubmitSMResp

Submit Short Message Response Packet. Inherited from CMessageRespBase.
CDeliverSM

Deliver Short Message Packet. Inherited from CMessagePacketBase.
CDeliverSMResp

Deliver Short Message Response. Inherited from CMessageRespBase.
CEnquireLink

Enquire Link Packet.
CEnquireLinkResp

Enquire Link Response.
CQuerySM

Query Short Message Packet.
Collapse

CString getMessageId();
void setMessageId(CString msgid);

CQuerySMResp

Query Short Message Response Packet.
Collapse

CString getMessageId();
void setMessageId(CString msgid);

CSmppDate getFinalDate();
void setFinalDate(CSmppDate &fldate);

uint32 getMessageState();
void setMessageState(uint32 msgst);

uint32 getErrorCode();
void setErrorCode(uint32 errcode);

CDataPacketBase

Base class for CDataSM.
Collapse

CString getServiceType();
CSmppAddress getSource();
CSmppAddress getDestination();

uint32 getEsmClass();

uint32 getRegisteredDelivery();

uint32 getDataCoding();

void setServiceType(CString stype);
void setSource(CSmppAddress &src);
void setDestination(CSmppAddress &dest);

void setEsmClass(uint32 esm);
void setRegisteredDelivery(uint32 reg);
void setDataCoding(uint32 enc);

CDataSM

Data Short Message Packet. Inherited from CDataPacketBase.
CDataSMResp

Data Short Message Response Packet.
ESME entities supported:
CEsmeTransmitter

Transmitter to send SMS.
CEsmeReceiver

Receiver to receive SMS.
CEsmeTransceiver

Transceiver to send and receive SMS.

Besides bind, unbind, submitMessage and enquireLink operations of the ESME entities, packets can be sent directly through:
Collapse

bool sendPacket(CPacketBase &pak);

The ESME entity classes are designed to operate in asynchronous mode. A callback routine should be prepared in the order packets are received from SMSC. This is the basic place where a client application defines its logic. To register the call back routine to the ESME entities, call the registerProcessPacket with the callback function pointer.

The prototype of the callback function should be in the form:
Collapse

void __stdcall processPacket(CPacketBase *pak, LPVOID param)

The library is dependent on MFC. Before any call, the user application must initialize the Winsock library by issuing WSAStartup().
Sample Code
Collapse

#include "stdafx.h"

#include "SMPPAPI.h"

#include "winsock2.h"


#include "EsmeTransceiver.h"


#include "SmppUtil.h"

#include "smpppacket.h"

#include "smpp.h"


void __stdcall processPacket(CPacketBase *pak, LPVOID param)
{
switch (pak->getCommandId())
{
case SMPP_BIND_TRANSMITTER_RESP:
{
CBindTransmitterResp *pPak;
pPak = static_cast(pak);
TRACE("Packet is casted to CBindTransmitterResp\n");
TRACE(pPak->getSystemId());
TRACE("\n");

trans.submitMessage("Hiya, this is my message",
"85261110229", 2, 0);
}
break;

case SMPP_BIND_RECEIVER_RESP:
{
CBindReceiverResp *pPak;
pPak = static_cast(pak);

TRACE("Packet is casted to CBindReceiverResp\n");
TRACE(pPak->getSystemId());
TRACE("\n");
}
break;

case SMPP_BIND_TRANSCEIVER_RESP:
{
CBindTransceiverResp *pPak;
pPak = static_cast(pak);

TRACE("Packet is casted to CBindTransceiverResp\n");
TRACE(pPak->getSystemId());
TRACE("\n");
}
break;

case SMPP_DELIVER_SM:
{
CDeliverSM *pPak;
pPak = static_cast(pak);

TRACE("Packet is casted to CDeliverSM\n");
TRACE("\n");

}
break;

case SMPP_SUBMIT_SM_RESP:
{
CSubmitSMResp *pPak;
pPak = static_cast(pak);

TRACE("Packet is casted to CSubmitSMResp\n");
TRACE(pPak->getMessageId());
TRACE("\n");

trans.unbind();
}
break;

case SMPP_UNBIND_RESP:
{
CUnbindResp *pPak;
pPak = static_cast(pak);

TRACE("Packet is casted to CUnbindResp\n");
TRACE("\n");

}
break;

default:
break;
}
}

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{

WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);

int nRetCode = 0;

// initialize MFC and print and error on failure

if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs

cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
CSmppAddress scr(2, 0, "85263243234");

trans.init("212.5.192.111", 5055);
trans.registerProcessPacket(processPacket, NULL);
trans.bind("t_test", "testpwd", "", scr);
getch();
}
}

In the example, a callback function processPacket(CPacketBase *pak, LPVOID param) is prepared to handle the notifications from SMSC. To set the procedure, you've noticed that, in main, the sample called registerProcessPacket(processPacket, NULL). When the SMSC replies the SMPP_BIND_TRANSCEIVER_RESP, it displays the System ID of the remote system, and then submits a message to a mobile subscriber.

Example projects can be found in the example folders, the SmppLibTest and TestServer. To try it, just run the TestServer, then the SmppLibTest.

TestServer is a sample testing server built using the SMPPLIB, while SmppLibTest is a client application.
COM Support

With amazing COM support, several COM classes are created for the SMPPLIB. Now, you can send and receive SMS in any COM automation support language and system, including Visual Basic and ASP websites. After installation, you should have SMPPCOMLib defined in your system.

The following COM classes are created:
Collapse

SmppDateCom
Method
STDMETHOD(isNull)(VARIANT_BOOL *valnull);
STDMETHOD(setNull)(void);
STDMETHOD(toString)(BSTR *strdate);

SmppAddressCom
Property
TON
NPI
Address

SubmitSMCom / DeliverSMCom
Property
ServiceType
Source
Destination
esmClass
dataCoding
protocolID
priorityFlag
scheduledDelivery
validityPeriod
registeredDelivery
replaceIfPresent
smDefaultMsgId
Message

Method
STDMETHOD(compactMessage)(void);
STDMETHOD(flipByteOrder)(void);
STDMETHOD(setMessage)(VARIANT msgdata);
STDMETHOD(getMessage)(VARIANT* pmsgdata);

EsmeTransmitterCom / EsmeReceiverCom / EsmeTransceiverCom
Method
STDMETHOD(bind)(BSTR sysid, BSTR passwd,
BSTR systype, ISmppAddressCom* iaddr, VARIANT_BOOL* pret);
STDMETHOD(unbind)(VARIANT_BOOL* pret);
STDMETHOD(enquireLink)(VARIANT_BOOL* pret);
STDMETHOD(init)(BSTR svrip, LONG port);
STDMETHOD(close)(void);
STDMETHOD(get_Connected)(VARIANT_BOOL* pVal);

In addition to the common methods, EsmeTransmitterCom and EsmeReceiverCom also support a submitting message method.

STDMETHOD(submitMessage)(ISubmitSMCom* isubmit, BSTR* pMsgid, VARIANT_BOOL* pret);

For EsmeTransceiverCom and EsmeReceiver, whenever a MO message is received, an OnDeliverSM event will be fired. This event can be handled in an automation client.

For example, in VB.NET, it is handled by the subroutine declared with the following syntax.

Private Sub receiver_OnDeliverSM(ByVal dsm As SMPPCOMLib.DeliverSMCom) Handles receiver.OnDeliverSM

For details of their usage, please take a look to the SmppComTest project, which is a testing VB project for sending and receiving SMS messages.

In order to receive events, you should create your instance of EsmeReceiverCom or EsmeTransceiverCom with the WithEvent keyword in Visual Basic.
Acknowledgement

As I remember, I've partially used code from Norm Almond, which is the Buffer class and classes for TCP connections. Thanks.
Frequently Asked Questions

Q1: What kind of conformance with SMPP specifications v3.3 and v3.4, this library supports?

A1: This library partially supports SMPP spec v3.3 and v3.4. Here it provides a handful of tedious packet encoding and decoding methods, and put them into individual classes. Users have to just use exposing getter/setter to manipulate the packet contents instead. This library also provides some ESME entities to make connections to SMSC and the skeleton to exchange packets with SMSC.

Q2: How I use it in VB.NET, VC++.NET?

A2: This library was developed in VC6, and recompiled in VC++.NET. They are not managed classes. However, you can still use Interop Service to use COM components. If you don't want to use Interop service, you may consider to buy our commercial .NET version. It supports more than this free C++ version.

Q3: How to compile this library in VC6?

A3: For the smpplib, it comes with a VC6 project file, you just have to open it and compile it. For the SMPPCOM project, I didn't build the VC6 COM project in VS6, so you have to copy create the VC6 COM project yourself. It is quite trivial I think, you have to just create an ATL project and put the source code into the project.

Q4: What is linkListen in CEsmeReceiver?

A4: It listens to a port for SMSC to make connection to your receiver. This is usually for outbound packets send by SMSC to ask you to bind to them, to receive message.

Q5: What is the difference between the .NET commercial version and this free version?

A5: This free version was developed in VC++, while the commercial version is developed in C#. The commercial version offers more support on auto-reconnection, auto-enquirelink, auto deliver_sm and enquire_link responses, optional parameters, etc. It also comes with a featured SMS sending/receiving application with broadcast feature. Personally, I think C# is easier to use than VC++.

Q6: How do I receive delivery acknowledge?

A6: So far as I know, this needs some setup in your SMSC. Besides, in Submit SM packet, there is an attribute called Registered Delivery you can set. For details for the bit pattern you need to set, please consult the SMPP specification.

Q7: How do I start using this library within my VC++ project?

A7: Firstly, you should read thoroughly this document, and understand the sample project. This library operates in event (packet receiving) driven mode. You have to provide the callback handler for 'what is going to be done when a packet is received'.

Q8: Why couldn't my client create the COM components, but in my development platform it works fine?

A8: The COM project is just a wrapper to the smpplib.dll. You should put SmppCom.dll and Smpplib.dll together. If you did file copy to your client computer, you have to use 'regsrv32 SmppCom.dll' to register the COM into your client machine.

Q9: Does it support Optional Parameter?

A9: Currently, optional parameters are not supported in this C++ version, and I am not going to spend time to support them.

Q10: Can I use it to send free SMS?

A10: This packet library is used for connecting to an SMSC. It is not for sending SMS directly. Whether SMS is charged, it is up to the Telecommunication companies, but mostly they will charge. This library is not intended to be used by hobbyists, though you can still study it.

most of people use MS DCOM/COM+,

Currently, most of people use MS DCOM/COM+, Java Bean/RMI, and Corba to do distributed computing over internet. All of these technologies are provided with some tools and intermediate objects to simplify internet development. However, these technologies have one common and fundamental problem that all of the calls between a client and a server are blocked for a returned result (please correct me if this is wrong). A client stays still, and has to wait for a while after sending a request to a server. If the request is a lengthy action, the client application seems to be dead to a user. In most cases, we could use a worker thread at the client side to do background computation for solving this problem. However, this comes with an expensive price, more coding, data synchronization and mess in coding logic. Additionally, I also doubt that these technologies are able to really move data across internet efficiently.

Recently, I had time to create a framework package, named as SocketPro, on socket for writing distributed computing applications over the internet, which contains a set of COM dlls and a standard dll (NetBaseR.dll) for EASY development of both client and server applications. In the near future, the documentation for all of the dlls and other help articles will be provided at the site of UDAParts.

All of applications developed from this framework will have the following BIG advantages plus other numerous advantages:

* Non-blocking/blocking, parallel computing client and server.

In regards to the client side development, this advantages implies that a client is able to send simultaneously multiple requests to a server through either one or multiple sockets for non-blocking and parallel computation. The client GUI components could be NEVER blocked without creating a worker thread. The important point here is that you can achieve non-blocking compuing without involement of threads. All of client function calls, which lead to data movement over internet, can be switched between blocking and non-blocking modes at your will after a socket is created. This is another particular advantage. It is hard to get this feature from other technologies, MS DCOM/COM+, Java Bean/RMI, and Corba.
In regards to the server side development, this advantage implies that a server application is able to fast and parallel process multiple requests from one or different client machines no matter whether the request is lengthy action or short action. The server is developed with multiple threads and message queues. All of queues are running independently and in parallel. In some cases, the server can be developed so that a quick request can be processed and returned first, even though the request is sent to the server after a request with a lengthy action. The server application groups/clusters various calls and assign them to different queues for processing. In all of cases, all of short requests from one or different sockets are assigned to one queue only, for reduction of involved threads and queues. The server is also able to kill all of idle queues and threads automatically for reducing system resources required and thread context switch. The server is able to detect the dead clients with a ping function.

You may think that this must be difficult to be implemented. It is true to take me a long time to create this piece of software component. You may think that it is difficult to develop a server and client applications based on this framework because of message queues, multiple threads, data synchronization problems. You are wrong and wrong!!!! Actually, The frame wraps the Winsock 2 library, and automatically manages message queues, threads and data synchronizations for you, and also efficiently by default. All you need to do is that you need to implement your functions, and tell the framework if the function is lengthy action or not. That is all! Attached with this article, you will also find three Visual C++ examples, DResolver (client), NTSvs (Server) and NormalApp (Server), to show you how to write client and server applications from the SocketPro package. You could see how simple to write such cool client and server applications from the SocketPro package.

* Both client and server run at the fastest speed because of Nagle algorithm.

At the beginning, I said I doubt if MS DCOM/COM+, Java Bean/RMI, and Corba can move data over network efficiently. Let me tell you a simple reason using a typical example. On an Ethernet network, the MTU for TCP/IP is 1460 bytes. Suppose that a client sockets send 29 requests, each of them with 50 bytes, and get 29 returned data of 50 bytes. If you use MS DCOM, the number of data round-trips will definitely be 29. However, if your server and client applications runs in the non-blocking mode, the Nagle algorithm will come in effect by default. Socket stack will automatically coalesce all of 29 requests into one big stream, and send it onto a server. The server similarly does coalescing and sends all of processed results to the client with one big stream if all of 29 actions are not lengthy action. The number of data round-trips is just one! As far as I know, even though Nagle algorithm may be turned on, all of MS DCOM/COM+, Java Bean/RMI, and Corba can hardly take advantage of coalescing through Nagle algorithm (Correct me if I am wrong) because their functions can't be internally non-blocking. I can hardly imagine these technologies can generically and automatically batch all of function calls into one, and then unpack it into separately calls at the server side.

Even if your client socket-based application doesn't run in the non-blocking mode, you can batch calls into one by your code. But it is not friendly to do so. There is no way to pack function calls with DCOM/COM+, Java Bean/RMI, and Corba.
Nagle algorithm is important especially for the server application. Reduction of data round-trips are the most efficient way to improve the server and client application performance, especially when many clients are connected with a server application, and data movement over network is extensive or your network is slow like a dial-up networking.
Additionally, SocketPro directly talks with Winsock 2 without middle layers. In comparison to MS DCOM with identical protocol and hardware, SocketPro still runs 130 % faster even though SocketPro runs in the blocking mode across machine according to my experiments. In order to show you what performance you can obtain from this SocketPro package, I wrote a sample name as SpeedTest, and compared results with DCOM applications (DCOMSleep and ClientTest). All of sample source codes are provided with this package for your test. Look at the codes and play with samples. Like me, I think you will sign with various feelings after obtaining your performance data! Here is performance result I obtained from my machines.

Hardwares: client -- P133 with 48 RAM; Server -- P700 with 256 RAM; Fast Ethernet 10/100 Network Cards + Ethernet 5-Port Hub (10Mbps) + Two Category 5 100BaseTX Network Cables ($50 totally, LinkSys NC100).
Software: Client -- Win95; Server -- NT 4.0 Server.

Table 1. Speed comparison among different methods with the average of 1000 calls.
Asyn/Batch Asyn/Nagle Syn/OneByOne DCOM
Speed AutoDetect (100 Mb/10Mb Full-Duplex) 7-45 4-7 2.3 1 (2.5 ms/call)
10Mb Full-Duplex 9-52 28-60 1 1 (25.5 ms/call)

Under all of my tests and conditions, SocketPro applications always faster than DCOM. I hope this test data are attractive to you. Running with samples, please give me a message here or privately if you find your result is against with these results. In some cases, if only one request is sent to a server, the speeds among these methods do not change very much. However, the performance with batch or Nagle algorithm will be speeded up if two or more asynchronous calls are called together by reducing data frames over network.
* Cross-platform data communication and cross-language development.

DCOM/COM is limited to window platform only, although MS said DCOM frameworks are available for other platforms. It is hard to find a real DCOM application developed on other platforms. You can use many languages (Java, C/C++, VB, dot.net, etc) from any platforms to develop client socket application to talk with a SocketPro server application, because socket communication is based on binary data and has very low dependency. In regards to a server application from SocketPro, you can only develop it with C++ for the best performance on Win 9x, NT4.0 ME, 2k and XP platforms at this time.

Other advantages includes:

* Fast connection to a server.
* Quick initialization of a class object.
* Simple installation and security setup.
* Easy creation of server application specific authentication mechanism.
* Simple to learn and use, and no complicated concepts are involved.

One disadvantage is that a developer must be ready to handle both blocking and non-blocking calls at both the client and server sides. It is involved with more coding for packing and unpacking sending and received data. It is simple and understandable in concept but tedious and more codes. For the best performance and reduction of data trips without help of Nagle algorithm, a developer should try the best to pack data on his or her own instead of Nagle algorithm, because the Nagle algorithm still costs a little time if the machine is not fast. In order to reduce the code of packing, a help class, CPacking, is provided with the package. For how to use it, see the following VC++ samples code.
Setup of test server and client applications

After downloading the socketpro.zip, unzip the package into a directory. You need to set up both client and server applications which are located in the directories of ..\netdemo\clientcomponents\ and ..\netdemo\svrcomponents\, respectively.

To start up the sample server application, you could double click the application MultipleSvs.exe (port # =1741). If your server is NT or its descendents, you could also start a NT service, MultiSvs.exe (port # = 1739). To start the NT service, you should install it by executing the DOS command, MultiSvs -Service. After installing it, you could start it by ->Settings->Control Panel->Services->MultiSvs from NT platform. If you use the platform other than NT, you could follow the similar steps to start it.

To run the sample client application from a Win32 (not CE) platform, you need to register four COM dlls (ClientSocket.dll, SockFile.dll, SBroker.dll and DResolver.dll) at your client machines first, which are located at the directory of ..\netdemo\clientcomponents\. To test the performance of sample NT server application, you could run multiple applications at one client machine or multiple client machines. Additionally, the sample VB client application and code are provided. See the files in the directory of ..\netdemo\samples\. I hope you like the application and give me your bug reports, questions, suggestions and concerns.
Functionalities of demo client and server applications

Here is the list of functionalities available from the demo server application:

* Exchange files between a server and clients.
* Remotely access files in a server.
* Provide a socket proxy/broker service on behalf of clients to process various command to other servers.
* Provide host name resolving service.

Here is the list of functionalities available from client components:

* ClientSocket.dll: You can use it to build up the connection from a Win 32 platform to the SocketPro server application, and also to all of socket-based applications running on all of platforms. After a connection is set up, you could use it to excute various command either synchronously or asynchronously. Additionally, it also provides functions to do DNS resolving either synchronously or asynchronously at the client side. You can use it independently without prerequirement of other components.
* SockFile.dll: You can use it to fast exchange files between a server and a client. You can also use it to remotely access files in a server machine through a SocketPro server application. It has all of important functions of Win32 file I/O functions.
* SBroker.dll: You can use it to execute all of protocol commands through the demo server application.
* DResolver.dll: You can use it to do DNS resolving through the demo SocketPro server application.

Here is the features of the demo client and server applications:

* You can see the features after testing the attached samples. These features include speed, parallel/non-blocking computing without a worker thread, and simplicity, as well as many others.

Source code

In this package, you can get the source code of all of four VB client applications. In regards to VC++ code, you get the source code of two client applications (SpeedTest and DResolver) and two server applications (NTSvs and MultipleSvs).
Notes

All of applications included with this particular package are free with the following limitations:

* The NetBaseR.dll enables you to build only two client socket connections to a server from a client application. A SocketPro server application only supports accepting 32 client connections only.
* The file ClientSocket.dll is hard-coded to support 2 socket connections only from one client application.
* The client COM dlls, SBroker.dl and DResolver.dll, are totally free to you.
* The client COM dll, SockFile.dll, is free to you except that four functions, ISocketFile::GetFile, ISocketFile::SendFile, and ISocketFile::WriteText and ISocketFile::WriteChunk, are disabled after 60 days.
* Authentication of both server applications, MultipleSvs.exe and MultiSvs.exe, are disabled for your easy setup and testing. You should pay enough attention to security. If not, someone may access your server machine files and manage them through the server application.

If you need us to do something for you, give us your message using email yekerui@yahoo.com. Service is available now!

IOCP-enabled real-life echo server

Introduction

I've been working with Winsock for a while, and even had some experience with IOCP. I have read several books and several articles that examined various related issues - but none of which provided me with a complete basis for a real-life application. I decided to try to combine most of my findings into one article that will walk you through the creation of a scalable and robust, IOCP-enabled real-life echo server!

This article is targeted at people who have both Winsock 2, and multi-threaded programming experience. All the code in this article was successfully compiled and run using Visual C++ 6 SP4 and Windows 2000 Server.

The model presented in this article will only work on Windows 2000 Server, Advanced Server, and Data Center operating systems. It takes advantage of an API function that is not available prior to Windows 2000: BindIoCompletionCallback().

The code presented here, can be ported to earlier version of Windows (Windows NT 3.51+) with slight changes.

Use the advices and code that is given in this article at your own risk. This code has been tested by me, and as far as I know, is bug-free. Please let me know of any bugs/problems you find in this code. Please remember that I take no responsibility for any kind of damage caused to you as a direct or in-direct result of usage or mis-usage of any information or piece of code found in this article.

You may freely use the code presented here in your own applications, but I'd like to know about it when you do! It would be nice of you if you drop me an email when you use my code in your product.

Please do not republish this article or any part of it without my permission.

The source code for the server application was modified a little, to fix a bug that was present in buffer.h. My thanks go to Dave Flores for going into the details in that file and locating the bug!

Now let's get to business.

What In The World Are I/O Completion Ports (IOCP) ?

For a more complete description of what's IOCP, and what it can do for you and your threads, I would recommend you to consult Programming Server-Side Applications for Microsoft Windows 2000 by Jeffrey Richter and Jason D. Clark, chapter 2: "Device I/O and Interthread Communication". I am going to briefly discuss it in the context of Winsock and server application development.

IOCP is maybe the most difficult to understand way to work with sockets. If you have used Winsock before (and I assume that you have), then you probably have used WSAAsyncSelect() or WSAEventSelect(), to request Winsock to inform you of relevant sockets' events. You use WSAAsyncSocket() to request Winsock to post messages to your window procedure upon events. You use WSAEventSelect() to request Winsock to signal an event upon events. If you wanted to take advantage of the threading model that Windows offers you (and you certainly should, in order to scale on machines with several CPUs), you had to spawn and take care of threads on your own.

When you use IOCP, you spawn a pool of threads once - and they are used to handle the network I/O in your application. Technically, in Windows 2000, you don't even have to spawn the pool yourself - you can let Windows take care of the spawning and management of the threads in the pool, and this is exactly what I'll do in this article.

The Main Program:

1. Create and initialize 500 sockets.
2. Create a listener socket.
3. Call AcceptEx() for each socket.
4. Bind the listener socket with the appropriate callback function (ThreadFunction() )
5. Create an event object, call it My_Die_Event.
6. Wait on My_Die_Event.
7. Cleanup (details regarding this will come later).

ThreadFunction()

(This function is called by Windows when an I/O operation has completed. Windows uses one of the threads from the thread-pool it created earlier to execute the function)

1. Check on which client the I/O operation completed.
2. Check which I/O operation completed.
3. Do some processing.
4. Issue another I/O operation (another read, or another write on the socket).
5. Return from the function. It will be called again, when another I/O operation completes.

So what do we have here?

We have threads that are all waiting for an I/O operation to complete. Those threads, as was specified before - are automatically created by Windows, when we first try to bind a socket to a callback function. Note that this automatic creation of threads is only available in Windows 2000 (and probably in future versions of Windows). If you intend to develop for Windows NT 4 or 3.51, you will have to spawn the threads yourself, and associate them, and the sockets, with a completion port. I will not show you how to do this in this article, but the changes are rather minor. Once an I/O operation completes, the operating system posts what's called "an I/O completion packet" to our completion port. Once the packet is sent, Windows resumes a thread from the pool, and has it run ThreadFunction(), while setting the appropriate parameters for the function. All this happens behind the scenes. As far as we are concerned - our callback function is automatically executed by some thread Windows assigns to it, when an I/O operation completes.

Let's take a look at the numbers we have set.

Q: How many threads are there in the pool that Windows creates?
A: This is up to Windows. In versions of Windows prior to 2000, you had to create the pool of threads yourself. The advantage to that, was that you had control of the actual number of threads in the pool. The disadvantage was that it was more complex, and less readable.

Q: Why do we create 500 sockets?
A: We are creating a set of sockets to be used throughout the program. Creating only 500 sockets will limits us to only 500 simultaneous connections - this could be ok, depends on the application. It would be a good idea to make this a configurable value. Creation and destruction of sockets is expensive. By creating a pool of sockets at the beginning of the program, we are enabling sockets-reuse and enhancing performance.
Getting Our Hands Dirty

Before we jump ahead and start writing the IOCP-manipulation code, I'd like to pause the IOCP discussion - and bring up another issue. If you are writing a server application, you have clients. When you work with clients, you have to mess with buffers. I am going to show you my general approach in these cases - this involves the creation of generic buffer class, and also what I call "packet class" (we'll get into it a bit later).

In order to receive data from a client, you call ReadFile(). In that call, you supply a buffer into which you want the data to be retrieved. This buffer must remain valid until the I/O operation completes. So far so good. You can set the buffer size to 1024 bytes, or whatever - and read data in portions of 1024 bytes. Let's take a look at various scenarios - what the buffer may look like when we receive a notification that a read operation was completed:

Scenario #1:
Only 321 bytes were received.
You interpret the data, figure out what the client wants, do some processing, send some data back to the client and call ReadFile() again, to receive more data.

Scenario #2:
Only 321 bytes were received.
You try to interpret the data, but you can't figure out what the client wants - the 321 bytes are not a complete command.

You need to call ReadFile() again to retrieve more data, but this will overwrite the first 321 bytes in the buffer!

Scenario #3:
1024 bytes were received.
You try to interpret the data, but you can't figure out what the client wants - the 1024 bytes are not a complete command. You need to call ReadFile() again to retrieve more data, but this will overwrite the contents of the buffer!

As you can see, we have no problem with scenario #1, but scenarios #2 and #3 are more problematic. Sometimes you will be able to act upon a client's request as it gets in, but you can't rely on that.

The case with the output buffer is a bit different. The buffer that you provide to WriteFile() must remain valid for the duration of the I/O operation. We would, however, like to be able to freely add data to be written out, regardless of a current state of an output I/O operation.

For the output buffer, I created an expandable buffer class. The sending operation logic is pretty simple, as you'll see in the code later. Basically, whenever you try to write data to a client, the program attempts to send the data immediately. If it can't - it stores the data in the expandable buffer, and the data is sent whenever the current I/O operation completes.

The case is a little bit different for the input buffer. It would be too much overhead to use such a buffer class for the input buffer. Instead, the code expands the input buffer automatically when required. The input buffer management is pretty interesting, you can examine the code as I'll show it.

About thread-safety:
The expandable buffer class is thread-safe. I made it thread-safe using a critical section. In some applications, you don't need to make the buffer class thread-safe, because calls to it are always serialized, because of the nature of the application. (If a situation where two threads attempt to write data to the same client simultaneously cannot occur, then it's safe to remove the thread-safety mechanisms). In order to remove the thread-safety mechanisms, you can simply inherit from the class, and override the relevant member functions. (InitializeInUse(), EnterInUse(), LeaveInUse() and DeleteInUse() ).

Look at buffer.h to see the buffer class code.

When sending data to clients, we provide WriteFile() with a buffer to be sent. This buffer must remain valid until the I/O operation is completed. I implement this by holding 2 buffers. The first - is the one that is passed to WriteFile(). The second - accumulates data, and is copied to the first whenever a send is completed.

Before showing you some more code, I would like to discuss another issue. The server application that you are writing, probably needs to receive commands from the clients, and respond with commands. If you are designing your own protocol (and not implementing some standard-protocol server, such as an FTP server or a web server), you have the freedom to decide the actual format of the data transferred between the server and the client.

I like to base everything on what I call a "packet infra-structure". The client posts requests by sending a complete packet, and the server responds by sending a complete packet. You can define a packet in whichever way you want. In this article, I have implemented what I consider the most generic packet type there can be.

A packet, in this article, is a structure that consists of an integer, and binary data. When the client posts a request, it first sends 4 bytes, describing the length of the requests, and then the request itself. This makes it very easy for the server to know when a request has fully arrived from the client (all it needs to do, is check the length of the request, and see that it has received enough data). The server responds in much the same way.

Internally, I created a tagPacket class, that holds two integers and a buffer. The second integer, holds the current size of the buffer. This could always be identical to the other integer, which holds the length of the data in the buffer - depends on the way you implement the application. If you create a new packet instance for each packet received, you can easily do with only one integer, describing the length of the data, and having the buffer always in the same size of the data. If, however, you decide not to allocate and de-allocate a packet for every client request, you may do so by separating the size of the buffer, and the length of the data. Whenever a new packet is received, the size of the buffer is examined. If the buffer is found large enough to contain the new data, the data is copied to the buffer, and its length is stored in the other integer.

Look at general.h to check out the packets manipulation code.

I believe that this code is pretty straight-forward. As you can see I have another function in this file - the function used to log errors. You will probably want this function to do something else, probably log errors to the system's Event Log. In buffer.h you will also find a function to retrieve a packet from the buffer.

As you can see, it is very easy to see whether a packet has arrived or not. Note that this approach may lead to some problems. For example - what if as a result from a bug, or a hacking attempt, the first 4 bytes indicate a value of 2 giga? In such a case, the abuser can keep sending data, and consume a lot of server resources. We will handle such cases later, by limiting a request's size and terminating abusing connections.

It is time to talk about the client's class. The approach I have taken while designing the client class and manipulation mechanisms, was of reuse. I prefer allocating memory once, and then reusing it, instead of allocating memory for each new connection and de-allocating it when the connection terminates. Allocation and de-allocation of memory are expensive operations, and should be avoided - in my opinion - in most cases - even at the expense of extensive resources consumption.

A few explanations regarding client.h and client.cpp.

Whenever we perform an I/O operation, using ReadFile() or WriteFile(), we should pass an overlapped structure as one of the parameters. We are actually passing an extended OVERLAPPED structure (a structure derived from the OVERLAPPED structure). The structure we are passing, contains some context information. The context information consists of the memory address of the client's class' instance that requested the I/O operation, and the type of the operation requested (read or write). This information is required in the callback function.

When we call ReadFile() to receive data, we pass it end_in_buf_pos, and not actual_in_buf. We also start reading data from start_in_buf_pos, and not actual_in_buf. Basically, those manipulations are done to avoid unnecessary expansion of actual_in_buf, and unnecessary calls to memmove(..). Look at CClient::Read(..) to see how it is done.

Whenever a ReadFile(..) operation completes, a timestamp is recorded. Some other section in the class uses this data to ensure that inactive clients (possibly abusers) are disconnected from the server. The function that is responsible for that is CClient::Maintenance(..) and it will be discussed in a short while.

CClient is an abstract class, which means that you must derive your own class from it. In your own class, you have to override three functions - these functions will be explained now.

int CClient::ProcessPacket(tagPacket *p)
Whenever a complete packet is received, this function is called with the new packet's address in p. In the code that I will present, this packet is a member of CClient. I used only one packet per client throughout the life of the application, to avoid constant allocations and de-allocations of tagPacket. This function is responsible for processing data received from the client - you may do there whatever you like - including sending data back to the client, using Write(..). The value that is returned by the function, tells the server application if it needs to do something. I have defined three possible values. CMD_DO_NOTHING - no action is required. CMD_DISCONNECT - client must be disconnected (possibly an abuser that sent a bogus packet). CMD_SHUTDOWN - the server should now shutdown. Those command values are declared in commands.h which will be shown a bit later.

void CClient::CreateInvalidPacket(tagPacket *p)
It could happen that a client is found to be an abuser during CClient::Read(..). For example, when a client attempts to send too much data in one packet. In such a case, instead of providing a true packet, CClient::Read(..) will call CClient::CreateInvalidPacket(..) which is responsible for creating a packet that will be recognized by CClient::ProcessPacket(..) as a special-purpose invalid packet, so it can take appropriate action (probably disconnect the client).

void CClient::Maintenance()
This function should occasionally be called for each client. Its purpose is to ensure that no client is abusing the system. Currently, it performs two different checks, as you can see in its code. It is called from the main thread.

Look at client.h and client.cpp to see the client class code.
Look at client_0.h and client_0.cpp to see the code I wrote for those three functions.
Putting It All Together (or - Enter: IOCP)

Now let's tie the pieces together, with IOCP. Folks, this is what you've been waiting for. First of all, we will create the function that is called when an I/O operation is completed. The function's declaration is actually dictated to us by Windows.

Look at callback.h to see this function's declaration.

Now comes the body of this function. It's really not too complicated. Note that it contains the line extern HANDLE dieEvent. It signals the dieEvent, on which the main thread is waiting, when it's time to shutdown.

Look at callback.cpp to see this function's definition.

This function counts the packets that it receives, and prints some stuff to the screen. Eventually you will probably want to change that.
Now comes the code that starts things up. It says everything we said in the beginning - initiates clients, sockets and waits on dieEvent. One interesting point, is the way maintenance is done. Every CHECK_CYCLE seconds (which I set to 10), it resumes (it waits on dieEvent up to CHECK_CYCLE seconds), and executes the CClient::Maintenance() function on every client in the system. This function makes sure that the client is not abusing the system. One way to abuse the system, is to connect to it and not send any data - thus not allowing AcceptEx(..) to accept the connection. You will probably want to tweak the values there to suit your own needs. You also may want to add other types of maintenance to the CClient::Maintenance() function.

That's about it. It's not a real echo server, in the sense that it's not actually repeating exactly what it receives. The application expects packets and returns the very same packets that it receives - however, it will not respond correctly to clear text. Testing such servers with telnet is pretty much impossible. That's why I've created a small Visual Basic utility, which connects to the server, and allows you to send any number of packets.

Note that there seems to be some kind of bug in the code that retrieves packets and shows their content. I haven't looked for it too much - it only happens when you receive massive amount of data at once.

I hope that this article and sample code will be useful for you. Feel free to email me for questions / remarks / whatever :-)

Winsock error descriptions

Introduction

Recently I was working on my umpteenth client/server app using Winsock, and once again I found myself embedding Winsock error descriptions in the app. Not being under the usual pressure (read: I hadn't missed the deadline yet) I decided to centralize the Winsock error descriptions in a handy DLL.

Looking around for some authoritative source for Winsock errors, I found a table on this page on MSDN: Windows Sockets Error Codes.

Here are the first few entries from that table:

Return code/value Description
WSAEINTR
10004

Interrupted function call.
A blocking operation was interrupted by a call to WSACancelBlockingCall.

WSAEACCES
10013

Permission denied.
An attempt was made to access a socket in a way forbidden by its access permissions. An example is using a broadcast address for sendto without broadcast permission being set using setsockopt(SO_BROADCAST).

Another possible reason for the WSAEACCES error is that when the bind function is called (on Windows NT 4 SP4 or later), another application, service, or kernel mode driver is bound to the same address with exclusive access. Such exclusive access is a new feature of Windows NT 4 SP4 and later, and is implemented by using the SO_EXCLUSIVEADDRUSE option.

WSAEFAULT
10014

Bad address.
The system detected an invalid pointer address in attempting to use a pointer argument of a call. This error occurs if an application passes an invalid pointer value, or if the length of the buffer is too small. For instance, if the length of an argument, which is a sockaddr structure, is smaller than the sizeof(sockaddr).

Looking at this table, it is clear that there are four items of interest for each error code:

1. The error code - for example, WSAEFAULT.
2. The numeric value of the error code - for example, 10014.
3. A short description - for example, Bad address..
4. A long description - a text string between 50 and 800 characters in length.

Note: the Winsock error codes are defined in winsock2.h.
XWSAError Functions

XWSAError.dll includes functions to retrieve the error code string (given the error code), the numeric error code (given the error code string), the short description, and the long description. Also included are functions to get the maximum length of the returned strings (if you are going to retrieve the long description, it may be simpler just to allocate one buffer for the long description string, and use that buffer for the other strings as well).

Here are the functions available in XWSAError.dll:

* XWSA_GetErrorCode() - Retrieves error code from error code string.
Collapse

/////////////////////////////////////////////////////////////////////////////
//
// XWSA_GetErrorCode()
//
// Purpose: Retrieve numeric error code associated with error code string.
// For example, calling XWSA_GetErrorCode() with an error code
// string of "WSAEINTR" will return the value 10004.
//
// Parameters: lpszErrorString - [in] pointer to error code string
//
// Returns: int - numeric value of error code
//

* XWSA_GetErrorString() - Retrieves error code string from error code.
Collapse

////////////////////////////////////////////////////////////////////////////
//
// XWSA_GetErrorString()
//
// Purpose: Retrieve the string associated with the error code. For
// example, calling XWSA_GetErrorString() with an error code
// of 10004 will return the string "WSAEINTR".
//
// Parameters: nErrorCode - [in] Winsock error code
// lpszBuf - [out] pointer to buffer that receives the string
// nBufSize - [in] size of buffer in TCHARs
//
// Returns: int - 1 if error code string found; 0 otherwise
//

* XWSA_GetLongDescription() - Retrieves long description.
Collapse

/////////////////////////////////////////////////////////////////////////////
//
// XWSA_GetLongDescription()
//
// Purpose: Retrieve the long description string associated with the
// error code.
//
// Parameters: nErrorCode - [in] Winsock error code
// lpszBuf - [out] pointer to buffer that receives the string
// nBufSize - [in] size of buffer in TCHARs
//
// Returns: int - 1 if error code found; 0 otherwise
//

* XWSA_GetShortDescription() - Retrieves short description.
Collapse

////////////////////////////////////////////////////////////////////////////
//
// XWSA_GetShortDescription()
//
// Purpose: Retrieve the short description string associated with the
// error code.
//
// Parameters: nErrorCode - [in] Winsock error code
// lpszBuf - [out] pointer to buffer that receives the string
// nBufSize - [in] size of buffer in TCHARs
//
// Returns: int - 1 if error code found; 0 otherwise
//

* XWSA_GetErrorStringSize() - Retrieves max size of an error code string.
Collapse

//////////////////////////////////////////////////////////////////////////
//
// XWSA_GetErrorStringSize()
//
// Purpose: Returns the maximum size in TCHARs of an error code string.
//
// Parameters: none
//
// Returns: int - maximum size in TCHARs of an error code string
//

* XWSA_GetLongDescriptionSize() - Retrieves max size of a long description.
Collapse

/////////////////////////////////////////////////////////////////////////
//
// XWSA_GetLongDescriptionSize()
//
// Purpose: Returns the maximum size in TCHARs of a long description
// string.
//
// Parameters: none
//
// Returns: int - maximum size in TCHARs of a long description string
//

* XWSA_GetShortDescriptionSize() - Retrieves max size of a short description.
Collapse

//////////////////////////////////////////////////////////////////////////
//
// XWSA_GetShortDescriptionSize()
//
// Purpose: Returns the maximum size in TCHARs of a short description
// string.
//
// Parameters: none
//
// Returns: int - maximum size in TCHARs of a short description string
//

Sample Code

The following sample code - taken from the demo app - shows how to use the above functions.
Collapse

// is this a numeric error code (10004) or an error string ("WSAEINTR")
if (bNumeric)
{
int nCode = _ttoi(m_strError);

m_Code.SetWindowText(m_strError);

if (XWSA_GetErrorString(nCode, buf, XWSA_GetLongDescriptionSize()))
{
m_ID.SetWindowText(buf);
if (XWSA_GetShortDescription(nCode, buf, XWSA_GetLongDescriptionSize()))
m_ShortDescription.SetWindowText(buf);
if (XWSA_GetLongDescription(nCode, buf, XWSA_GetLongDescriptionSize()))
m_LongDescription.SetWindowText(buf);
}
else
{
m_ID.SetWindowText(_T("unknown error code"));
}
}
else
{
int nCode = XWSA_GetErrorCode(m_strError);

if (nCode)
{
m_ID.SetWindowText(m_strError);

CString s = _T("");
s.Format(_T("%d"), nCode);

m_Code.SetWindowText(s);

if (XWSA_GetShortDescription(nCode, buf, XWSA_GetLongDescriptionSize()))
m_ShortDescription.SetWindowText(buf);
if (XWSA_GetLongDescription(nCode, buf, XWSA_GetLongDescriptionSize()))
m_LongDescription.SetWindowText(buf);
}
}

How To Use With Visual C++

To integrate XWSAError.dll into your app, you first need to add XWSAError.h to your project. XWSAError.h automatically links to XWSAError.lib, so all you have to do is, add the XWSAError.lib directory to your project (in VC 6.0, go to Project | Settings | Link | Input and add the directory to the Additional Library Path). Next insert the line
Collapse

#include "XWSAError.h"

in the module where you want to call the functions. Finally, make sure the XWSAError.dll is in the same directory as the app's exe.
How To Use With Visual Basic

To get XWSAError.dll to work with VB apps, the first thing to do is define all the functions using the __stdcall calling convention:
Collapse

XWSAERROR_API int __stdcall XWSA_GetErrorString(int nErrorCode,
TCHAR * lpszBuf, int nBufSize);
XWSAERROR_API int __stdcall XWSA_GetErrorCode(const TCHAR * lpszErrorString);
XWSAERROR_API int __stdcall XWSA_GetErrorStringSize();
XWSAERROR_API int __stdcall XWSA_GetShortDescription(int nErrorCode,
TCHAR * lpszBuf, int nBufSize);
XWSAERROR_API int __stdcall XWSA_GetShortDescriptionSize();
XWSAERROR_API int __stdcall XWSA_GetLongDescription(int nErrorCode,
TCHAR * lpszBuf, int nBufSize);
XWSAERROR_API int __stdcall XWSA_GetLongDescriptionSize();

This, combined with the __declspec(dllexport) used in the XWSAERROR_API macro, causes the exported functions to have decorated names (as shown by dumpbin):
Collapse

ordinal hint RVA name

1 0 0000100F _XWSA_GetErrorCode@4
2 1 00001019 _XWSA_GetErrorString@12
3 2 0000101E _XWSA_GetErrorStringSize@0
4 3 00001014 _XWSA_GetLongDescription@12
5 4 00001023 _XWSA_GetLongDescriptionSize@0
6 5 00001028 _XWSA_GetShortDescription@12
7 6 00001032 _XWSA_GetShortDescriptionSize@0

This means that VB programs would have to alias the exported names before calling any of these functions. This is nasty, but we can avoid this problem by adding a module definition file (XWSAError.def):
Collapse

; XWSAError.def - module definition file
;
; The EXPORTS in this file remove the decoration from the function names,
; and so allow Visual Basic programs to call the XWSAError functions using
; the same names as VC++ programs.

LIBRARY XWSAError

DESCRIPTION "Winsock error lookup"

EXPORTS
XWSA_GetErrorCode
XWSA_GetErrorString
XWSA_GetErrorStringSize
XWSA_GetLongDescription
XWSA_GetLongDescriptionSize
XWSA_GetShortDescription
XWSA_GetShortDescriptionSize

Now the undecorated function names are exported:
Collapse

ordinal hint RVA name

1 0 0000100F XWSA_GetErrorCode
2 1 00001019 XWSA_GetErrorString
3 2 0000101E XWSA_GetErrorStringSize
4 3 00001014 XWSA_GetLongDescription
5 4 00001023 XWSA_GetLongDescriptionSize
6 5 00001028 XWSA_GetShortDescription
7 6 00001032 XWSA_GetShortDescriptionSize

Now VB programs can call these functions without any extra work, as shown in the following snippet (a sample VB program is included in the download):
Collapse

Private Declare Function XWSA_GetShortDescriptionSize Lib "XWSAError.dll" () _
As Integer
Private Declare Function XWSA_GetShortDescription Lib "XWSAError.dll"
(ByVal nerror%, ByVal sd$, ByVal sdsize%) As Integer

Private Sub Command1_Click()

Dim nSize As Integer, rc As Integer, error As Integer
Dim ErrorCode As String, ShortDesc As String

nSize = XWSA_GetShortDescriptionSize()
ShortDesc = String$(nSize, Chr$(0))
ErrorCode = Form1.Text1.Text
error = ErrorCode
rc = XWSA_GetShortDescription(error, ShortDesc, Len(ShortDesc))
Form1.Text2.Text = "XWSA_GetShortDescription() returned: " + ShortDesc

End Sub

For more information on using DLL functions in VB, please see Q142840: Visual Basic Requirements for Exported DLL Functions.
The Demo App

The demo app displays the various strings when you enter an error code:

screenshot
Revision History
Version 1.0 - 2005 January 6

* Initial public release.

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.
License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
About the Author
Hans Dietrich


Member I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.
Occupation: Software Developer (Senior)
Company: Hans Dietrich Software
Location: United States United States

Internet Transfer Control

INTRODUCTION

Most of you might have worked with Internet Transfer Control which is very handy control when it comes to Internet Programming but there is another control which even more robust and helps programmers creating more flexible applications. Winsock control comes with VB6 and is used to create applications that access the low-level functions of the Transmission Control Protocol/Internet Protocol (TCP/IP).


TCP/IP is a specification that defines a series of protocols used to standardize how computers exchange information with each other. TCP/IP provides communication across interconnected networks that use diverse hardware architectures and various operating systems. The protocols in TCP/IP are arranged in a series of layers known as a protocol stack. Each layer has its own functionality.

Winsock is a standard that is maintained by Microsoft. This standard is basically a set of routines that describe communications to and from the TCP/IP stack. These routines reside in a dynamic link library that runs under Windows. The winsock DLL is interfaced with TCP/IP and from there through the Internet.

In this article, I am going to show how to use the winsock in a client server environment, we will create two separate applications, one of which will be a server and the other will be a client. Both client and server will interact with each other to exchange data. Client will send a request to the server and the server which will be connected to a database will retrieve the information requested by the client from the database and will return the requested information back to the client. You will a database with this article, the database contains the item numbers and their prices. In real life situations, database might be located on a machine different from the one that hosts the client application.

I think it would be better to talk about the ports before we proceed any further. A port is a special memory location that exists when two computers are in communication via TCP/IP. Applications use a port number as an identifier to other computers, both the sending and receiving computers use this port to exchange data.

To make the job of communication easier, some port numbers have been standardized. These standard port numbers have no inherent value other than that users have agreed to use them with certain applications. Table below lists a number of popular and publicly accepted port numbers and their corresponding applications.

Service


Port
HTTP 80
FTP 20,21
GOPHER 70
SMTP 25
POP3 110
TELNET 23
FINGER 79
LOCAL LOOPS/CALLBACKS 0
Using the Winsock Control

Winsock is above the TCP/IP protocol stack in the ISO/OSI model. TCP/IP is an industry standard communication protocol that defines methods for packaging data into packets for transmission between computing devices on a heterogeneous network. TCP/IP is the standard for data transmission over networks, including the Internet. TCP establishes a connection for data transmission and IP defines the method for sending data packets.
The Microsoft Winsock control makes using the TCP/IP a breeze. Microsoft has wrapped up the Winsock and INetAPI API calls into a nice neat package that you can easily incorporate into your Visual Basic applications.
Winsock Operating Modes

The Transport layer (also known as the Host-to-Host Transport layer) is responsible for providing the Application layer with session and datagram communication services. The core protocols of the Transport layer are TCP and User Datagram Protocol (UDP). The Winsock control supports the following two operating modes:


* sckTCPProtocol
* sckUDPProtocol

Winsock Properties

Winsock enables you to create clients and servers using the same control. This dual functionality enables you to specify through property setting the type of application you will be building. The Winsock control uses a number of the same properties, whether you are creating client or a server, thereby all but eliminating the learning curve needed to create applications. Some of the important properties of the control are as following:

BytesReceived Property

This property returns the number of bytes currently in the receive buffer. This is a read-only property and is unavailable at design time. The value returned is a long integer.

LocalHostName Property

The LocalHostName property returns the name of the local host system. This is read-only property and is unavailable at the design time. The value returned is a string.

LocalIP Property

The LocalIP property returns the local host system IP address in the form of a string, such as 11.0.0.127. This property is read-only and is unavailable at design time.

LocalPort Property

This property returns or sets the local port number. This can be both read from and written to and is available at both design time and runtime. The value returned is a long integer.

Protocol Property

Returns or sets the protocol, either TCP or UDP, used by the Winsock control.

RemoteHost Property

The RemoteHost property returns or sets the remote host. This can be both read from and written to and is available both in design time and runtime. The value returned is a string and can be specified either as an IP address or as a DNS name.

RemotePort Property

This property returns or sets the remote port number.

State Property

This returns the state of the control as expressed by an enumerated list. This is read-only property and is unavailable at design time.
Winsock Methods

Some of the important methods of Winsock control are as following:

Accept Method

It accepts the request for connection from the client system. For this method to be used, the control must be in the listening state.

Close Method

The Close method terminates a TCP connection from either the client or server applications.

GetData Method

GetData is the method that retrieves the current block of data from the buffer and then stores it in a variable of the variant type.

PeekData Method

The PeekData method operates in a fashion similar to the GetData method. However, it does not remove data from the input queue.

Listen Method

This is invoked on the server application to have the server application wait for a TCP request for connection from a client system.

SendData Method

This method dispatches data to the remote computer. It is used for both the client and server systems.

Connect Method

The Connect method requests a connection to a remote computer.

I am not going to discuss events here. You can find the complete details of events on the Microsoft site (http://www.microsoft.com).

In the sample provided with this article, we are going to create two applications, one server and client. This is a real world example, where the clients requests some information from the server and the server retrieves some specific information from the database and sends the retrieved information back to the client. The database used in the sample is also provided with the code. The database name is Prices.mdb. This is a small database comprising of a single table containing two fields. The fields are item number and price. The clients sends the item number to the server and the server retrieves the price against that item number from the database and sends it back to the client. One of the current trends in software development today is the issue of thick clients versus thin clients. A thick client is basically an application that performs the bulk of the processing on the individual client PC, whereas a thin client performs the processing on the server.
Creating the Client

Follow the steps shown below:

1. Start a new EXE project.
2. Add a Winsock control to your application.
3. Add all the controls to the form (See the application for details).

Here is the complete code:
Collapse

Option Explicit

Private Sub cmdClose_Click()
Winsock1.Close
shpGo.Visible = False
shpWait.Visible = False
shpError.Visible = True
End Sub

Private Sub cmdConnect_Click()
Winsock1.RemoteHost = "11.0.0.1" 'Change this to your host ip
Winsock1.RemotePort = 1007
Winsock1.Connect
shpGo.Visible = True
txtItem.SetFocus
End Sub

Private Sub cmdSend_Click()
If Winsock1.State = sckConnected Then
Winsock1.SendData txtItem.Text
shpGo.Visible = True
Label3.Caption = "Sending Data"
Else
shpGo.Visible = False
shpWait.Visible = False
shpError.Visible = True
Label3.Caption = "Not currently connected to host"
End If
End Sub

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim sData As String
Winsock1.GetData sData, vbString
'Label1.Caption = sData
txtPrice.Text = sData
Label3.Caption = "Received Data"
shpGo.Visible = True
shpWait.Visible = False
shpError.Visible = False

End Sub

Private Sub Winsock1_SendComplete()

Label3.Caption = "Completed Data Transmission"

End Sub


Creating the Server

The server portion of the price lookup example is designed to accept the item number sent from the client and look up the associated price in a database. The server than sends the information back to the client. There is file named as �path.txt� in the folder called as �server�. Locate that file and change the database path in the file to the location where the database is located on your machine. The connection to the database is made in the DataArrival event of the Winsock control. The following code segment opens the database and finds the first occurrence of the value in sItemData. When the record is found, the value contained in the price field is sent back to the client.
Collapse

' Get clients request from database

strData = "Item = '" & sItemData & "'"
rs.Open "select * from prices", strConnect, adOpenKeyset,adLockOptimistic
rs.Find strData
strOutData = rs.Fields("Price")


Follow the steps shown below to create the server:

1. Start a new Standard EXE in VB.
2. Add the Winsock control to your application.
3. Add the controls to the form as shown in the accompanying code (See the folder named as �server�).

Here is the complete code:
Collapse

Option Explicit
Dim iSockets As Integer
Dim sServerMsg As String
Dim sRequestID As String

Private Sub Form_Load()

Form1.Show
lblHostID.Caption = Socket(0).LocalHostName
lblAddress.Caption = Socket(0).LocalIP
Socket(0).LocalPort = 1007
sServerMsg = "Listening to port: " & Socket(0).LocalPort
List1.AddItem (sServerMsg)
Socket(0).Listen
End Sub

Private Sub socket_Close(Index As Integer)
sServerMsg = "Connection closed: " & Socket(Index).RemoteHostIP
List1.AddItem (sServerMsg)
Socket(Index).Close
Unload Socket(Index)
iSockets = iSockets - 1
lblConnections.Caption = iSockets

End Sub

Private Sub socket_ConnectionRequest(Index As Integer, ByVal requestID As Long)
sServerMsg = "Connection request id " & requestID & " from " & Socket(Index).RemoteHostIP
If Index = 0 Then
List1.AddItem (sServerMsg)
sRequestID = requestID
iSockets = iSockets + 1
lblConnections.Caption = iSockets
Load Socket(iSockets)
Socket(iSockets).LocalPort = 1007
Socket(iSockets).Accept requestID
End If

End Sub

Private Sub socket_DataArrival(Index As Integer, ByVal bytesTotal As Long)

Dim sItemData As String
Dim strData As String
Dim strOutData As String
Dim strConnect As String


' get data from client
Socket(Index).GetData sItemData, vbString
sServerMsg = "Received: " & sItemData & " from " & Socket(Index).RemoteHostIP & "(" & sRequestID & ")"
List1.AddItem (sServerMsg)

'strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=G:\Prices.mdb;Persist Security Info=False"
Dim strPath As String

'Change the database path in the text file

Dim fso As New FileSystemObject, txtfile, _
fil1 As File, ts As TextStream

Set fil1 = fso.GetFile("path.txt")
' Read the contents of the file.
Set ts = fil1.OpenAsTextStream(ForReading)
strPath = ts.ReadLine
ts.Close
Set fso = Nothing

strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Persist Security Info=False;Data Source=" & strPath & _
"; Mode=Read|Write"

Dim rs As New ADODB.Recordset

' Get clients request from database
strData = "Item = '" & sItemData & "'"

rs.Open "select * from prices", strConnect, adOpenKeyset, adLockOptimistic
rs.Find strData
strOutData = rs.Fields("Price")

'send data to client
sServerMsg = "Sending: " & strOutData & " to " & Socket(Index).RemoteHostIP
List1.AddItem (sServerMsg)
Socket(Index).SendData strOutData

End Sub


Running the example

1. Create executable for both the applications.
2. Launch both the applications.
3. Click the Connect button.
4. Enter a value from 0 to 6 (currently the database contains only six records, error handling is not done in this code, you can add the error handling yourself) and click the Lookup button. The associated price will be displayed in the price field.