TCP Communications

  • Thread starter Thread starter Tibby
  • Start date Start date
T

Tibby

Okay, hopefully this thread will stqay pretty much as is. Anyhew, right
off the bat, I miss winsock!!!!
With that off my chest, I'm having a huge problem and a concern. I need
to create a client app that will communicate with my FTP server. Now, with
the Socket sending in Byte(), is the server able to handle this? I wonder
this because I have a realy nice SocketClient class, but, it don't seem to
send anything, just recieve. I have uploaded it to my website, @
http://www.tiberiansun.us/Projects/SocketClient.vb

I'm having some major issues here.. :(

Tibby
 
Okay, hopefully this thread will stqay pretty much as is. Anyhew, right
off the bat, I miss winsock!!!!

I don't... The System.Net and System.Net.Socket stuff is much more
flexable, IMHO. And easier to work with... Of course, this is comming
from someone who has used the Winsock control exactly once and then
abandoned it to us the Winsocke API instead :)
With that off my chest, I'm having a huge problem and a concern. I need
to create a client app that will communicate with my FTP server. Now, with
the Socket sending in Byte(), is the server able to handle this? I wonder
this because I have a realy nice SocketClient class, but, it don't seem to
send anything, just recieve. I have uploaded it to my website, @
http://www.tiberiansun.us/Projects/SocketClient.vb

I'm having some major issues here.. :(

Sockets send arrays of bytes... That's all they do under the covers -
it is up to the client and server to determine how that stream is
interpreted. So, sending an array of bytes is not a problem :) What
exactly are your issues? I might be able to help you out some, if I
understand exactly what your trying to accomplish.
 
Tom Shelton
MVP [Visual Basic]

Well, I need FTP functionality, but nothing more than changing to a
predetermined directory, and snagging the files. I have the commands, and
it's all clear text to the server, but the class that I mentioned does not
send data back to the server. I get connected and get the initial greeting,
but nothing is sent back.
Unfortunatly, I'm very pressed for time so I can't get the knowledge I
need, just the basic stuff and get a solution running until I can go back
and revamp it. So, basically, in a nutshell, I'm making an FTP client that
will recognize 200, 331, 257, 150, 226, 350 return codes, and send USER,
PASS, TYPE, PWD, CWD, PORT, LIST, CDUP, and RETR. I've looked at some of
the "commercial" products, and the boss won't shell out for it, but wants it
done, urgghh.

Thank you again for your help Tom,
Tibby
 
Tom Shelton
MVP [Visual Basic]

Just as an update, I rewrote the class based off of some research. I know
am running into this problem which only happens on the second iteration
through code.

Imports System
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Imports System.Collections
Imports System.IO
Public Class StateObject
Public workSocket As Socket = Nothing ' Client socket.
Public BufferSize As Integer = 32767 ' Size of receive buffer.
Public buffer(32767) As Byte ' Receive buffer.
Public sb As New StringBuilder() ' Received data string.
End Class
Public Class SocketClient
#Region "Class Events"
Public Event onConnect()
Public Event onError(ByVal Description As String)
Public Event onDataArrival(ByVal Data As String, ByVal TotalBytes As
Integer)
Public Event onDisconnect()
Public Event onSendComplete(ByVal DataSize As Integer)
#End Region
#Region "Private Variables"
Protected response As String = String.Empty
Protected Port As Integer = 21
Protected IPHostInfo As IPHostEntry = Dns.Resolve("localhost")
Protected IPAddress As IPAddress = IPHostInfo.AddressList(0)
Protected Client As Socket
#End Region
#Region "Public Members"
Public Sub Connect(ByVal RemoteHost As String, Optional ByVal RemotePort As
Integer = 21)
Try
Port = RemotePort
IPHostInfo = Dns.Resolve(RemoteHost)
IPAddress = IPHostInfo.AddressList(0)
Dim remoteEP As New IPEndPoint(IPAddress, Port)
Client.BeginConnect(remoteEP, AddressOf sockConnected, Client)
Catch
RaiseEvent onError(Err.Description)
End Try
End Sub
Public Sub SendData(ByVal Data As String)
Try
Dim OutData() As Byte = Encoding.ASCII.GetBytes(Data)
Client.BeginSend(OutData, 0, OutData.Length, SocketFlags.None, AddressOf
SendEnd, Client)
Catch
RaiseEvent onError(Err.Description)
End Try
End Sub
Public Sub Disconnect()
Try
Client.Shutdown(SocketShutdown.Both)
Client.Close()
Catch
RaiseEvent onError(Err.Description)
End Try
End Sub
Public Sub New()
Client = New Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp)
End Sub
#End Region
#Region "Private Members"
Protected Sub sockConnected(ByVal ar As IAsyncResult)
If Client.Connected = False Then RaiseEvent onError("Connection refused.") :
Exit Sub
Try
Dim state As New StateObject()
state.workSocket = Client
Client.BeginReceive(state.buffer, 0, state.BufferSize, 0, AddressOf
sockDataArrival, state)
Catch
RaiseEvent onError(Err.Description)
Exit Sub
End Try
RaiseEvent onConnect()
End Sub
Private Sub sockDataArrival(ByVal ar As IAsyncResult)
Dim state As StateObject = CType(ar.AsyncState, StateObject) <<<<==== I get
a "Specified Cast is not valid"

<<<<==== the second chunk of data, the first chunk works fine
Dim client As Socket = state.workSocket
Dim Outdata As String
Try
'ReDim state.buffer(32767)
client.BeginReceive(state.buffer, 0, state.BufferSize, 0, AddressOf
sockDataArrival, client)
Dim inData() As Byte = state.buffer
Dim bytesIncoming As Integer = client.EndReceive(ar)
Outdata = Encoding.ASCII.GetString(inData)
RaiseEvent onDataArrival(Outdata.Trim, bytesIncoming)
Catch
RaiseEvent onError(Err.Description)
End Try
End Sub
Protected Sub SendEnd(ByVal ar As IAsyncResult)
Dim bytessent As Integer = Client.EndSend(ar)
RaiseEvent onSendComplete(bytessent)
End Sub
#End Region
End Class

Thanks again
Tibby
 
Tom Shelton
MVP [Visual Basic]

Just as an update, I rewrote the class based off of some research. I know
am running into this problem which only happens on the second iteration
through code.

Imports System

Thanks again
Tibby

Tibby,

I am looking over the code and will see what I can do with it. I do
have some ftp code that I wrote that uses the WinInet API. It is in C#,
but it should be fairly straight forward to either convert to VB.NET or
just compile it into a separate dll. Might work as quick and dirty
temporary solution (that's why I wrote it :)

// wininet.cs
using System;
using System.Text;
using System.Runtime.InteropServices;

namespace FireAnt.Net.Ftp
{
/// <summary>
/// WinInet API declarations.
/// </summary>
internal sealed class WinInet
{
public const int INTERNET_OPEN_TYPE_PRECONFIG = 0;
public const int INTERNET_OPEN_TYPE_DIRECT = 1;
public const int INTERNET_OPEN_TYPE_PROXY = 3;
public const short INTERNET_DEFAULT_FTP_PORT = 21;
public const int INTERNET_SERVICE_FTP = 1;
public const int FTP_TRANSFER_TYPE_ASCII = 0x01;
public const int FTP_TRANSFER_TYPE_BINARY = 0x02;
public const int GENERIC_WRITE = 0x40000000;
public const int GENERIC_READ = unchecked((int)0x80000000);
public const int MAX_PATH = 260;

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr InternetOpen(
string lpszAgent,
int dwAcessType,
string lpszProxyName,
string lpszProxyBypass,
int dwFlags);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr InternetConnect(
IntPtr hInternet,
string lpszServerName,
short nServerPort,
string lpszUserName,
string lpszPassword,
int dwService,
int dwFlags,
ref int dwContext);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool FtpGetCurrentDirectory(
IntPtr hConnect,
StringBuilder lpszCurrentDirectory,
ref int lpdwCurrentDirectory);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool FtpSetCurrentDirectory(
IntPtr hConnect,
string lpszCurrentDirectory);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr FtpOpenFile(
IntPtr hConnect,
string lpszFileName,
int dwAccess,
int dwFlags,
out int dwContext);

[DllImport("wininet.dll", SetLastError=true)]
public static extern bool InternetWriteFile(
IntPtr hFile,
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
int dwNumberOfBytesToWrite,
out int lpdwNumberOfBytesWritten);

[DllImport("wininet.dll", SetLastError=true)]
public static extern bool InternetReadFile(
IntPtr hFile,
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
int dwNumberOfBytesToRead,
out int lpdwNumberOfBytesRead
);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool InternetCloseHandle(IntPtr hInternet);

private WinInet()
{
}
}
}


// SimpleFTP.cs
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

namespace FireAnt.Net.Ftp
{
[Flags()]
public enum AccessMode
{
Read = WinInet.GENERIC_READ,
Write = WinInet.GENERIC_WRITE,
}

public enum TransferMode
{
Ascii = WinInet.FTP_TRANSFER_TYPE_ASCII,
Binary = WinInet.FTP_TRANSFER_TYPE_BINARY,
}

/// <summary>
/// SimpleFTP class using the WinInet API.
/// </summary>
public sealed class SimpleFtp : IDisposable
{
private IntPtr internet;
private IntPtr connection;
private IntPtr fileHandle;
private int context;

private const int BUFFER_SIZE = 2048;

public SimpleFtp(string host, string userName, string password)
{
internet = WinInet.InternetOpen(
null,
WinInet.INTERNET_OPEN_TYPE_DIRECT,
null,
null,
0);

if (internet == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}

connection = WinInet.InternetConnect(
this.internet,
host,
WinInet.INTERNET_DEFAULT_FTP_PORT,
userName,
password,
WinInet.INTERNET_SERVICE_FTP,
0,
ref this.context);

if (connection == IntPtr.Zero)
{
WinInet.InternetCloseHandle(this.internet);
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

~SimpleFtp()
{
this.CleanUp();
}

void IDisposable.Dispose()
{
this.CleanUp();
GC.SuppressFinalize(this);
}

public string CurrentDirectory
{
get
{
StringBuilder path = new StringBuilder(260);
int bufferSize = path.Capacity;

if (!WinInet.FtpGetCurrentDirectory(this.connection, path, ref bufferSize))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return path.ToString();
}
set
{
if (!WinInet.FtpSetCurrentDirectory(this.connection, value))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}

public void Close()
{
((IDisposable)this).Dispose();
}

public void OpenFile(string fileName, AccessMode access, TransferMode mode)
{
this.fileHandle = WinInet.FtpOpenFile(this.connection, fileName, (int) access, (int) mode, out this.context);
if (this.fileHandle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

public void CloseFile()
{
if (this.fileHandle != IntPtr.Zero)
{
if (WinInet.InternetCloseHandle(this.fileHandle))
{
this.fileHandle = IntPtr.Zero;
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}

public int WriteFile(string buffer)
{
byte[] bytes = new ASCIIEncoding().GetBytes(buffer);
return this.WriteFile(bytes);
}

public int WriteFile(byte[] buffer)
{
int byteCount;
if (!WinInet.InternetWriteFile(this.fileHandle, buffer, buffer.Length, out byteCount))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return byteCount;
}

public bool ReadFile(out string buffer)
{
// clear the buffer...
buffer = string.Empty;

// read from the file
int bytesRead;
byte[] readBuffer = new byte[SimpleFtp.BUFFER_SIZE];
bool success = WinInet.InternetReadFile(this.fileHandle, readBuffer, readBuffer.Length, out bytesRead);

// the call failed!
if (!success)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}

// we got some data, so convert it for the return...
if (bytesRead != 0)
{
buffer = Encoding.ASCII.GetString(readBuffer, 0, bytesRead);
}

return (bytesRead != 0) ? true : false;
}

public bool ReadFile(byte[] buffer)
{
int bytesRead;
bool success = WinInet.InternetReadFile(this.fileHandle, buffer, buffer.Length, out bytesRead);
if (!success)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return (bytesRead != 0) ? true : false;
}

private void CleanUp()
{
if (this.fileHandle != IntPtr.Zero)
{
WinInet.InternetCloseHandle(this.fileHandle);
}

if (this.connection != IntPtr.Zero)
{
WinInet.InternetCloseHandle(this.connection);
}

if (this.internet != IntPtr.Zero)
{
WinInet.InternetCloseHandle(this.internet);
}
}
}
}

Anyway, should do most of the basic stuff... here is a quick little
snippit of code for using it...

SimpleFTP ftp = new SimpleFTP("myserver.mydomain.com", "myuser",
"mypassword");

if (ftp.CurrentDirectory != "my directory")
{
ftp.CurrentDirectory = "my directory";
}

string data;
ftp.OpenFile("myfile.txt", AccessMode.Read, TransferMode.Ascii);
while (ftp.ReadFile(out data))
// you can of course write the
// data to your local file here :)
Console.WriteLine(data);
}

// If your not done with the object, and
// are going to transfer more files, then
// call .CloseFile() instead to just close
// the current file handle.
ftp.Close();


Anyway, sorry for the C# - but you should be able to use this pretty
much the way it is. At least until we get this VB code worked out :)
 
How can one have 2 functions named the same thing? This makes no sense to
me at all.. :)

Tibby
Tom Shelton said:
Tom Shelton
MVP [Visual Basic]

Just as an update, I rewrote the class based off of some research. I know
am running into this problem which only happens on the second iteration
through code.

Imports System

Thanks again
Tibby

Tibby,

I am looking over the code and will see what I can do with it. I do
have some ftp code that I wrote that uses the WinInet API. It is in C#,
but it should be fairly straight forward to either convert to VB.NET or
just compile it into a separate dll. Might work as quick and dirty
temporary solution (that's why I wrote it :)

// wininet.cs
using System;
using System.Text;
using System.Runtime.InteropServices;

namespace FireAnt.Net.Ftp
{
/// <summary>
/// WinInet API declarations.
/// </summary>
internal sealed class WinInet
{
public const int INTERNET_OPEN_TYPE_PRECONFIG = 0;
public const int INTERNET_OPEN_TYPE_DIRECT = 1;
public const int INTERNET_OPEN_TYPE_PROXY = 3;
public const short INTERNET_DEFAULT_FTP_PORT = 21;
public const int INTERNET_SERVICE_FTP = 1;
public const int FTP_TRANSFER_TYPE_ASCII = 0x01;
public const int FTP_TRANSFER_TYPE_BINARY = 0x02;
public const int GENERIC_WRITE = 0x40000000;
public const int GENERIC_READ = unchecked((int)0x80000000);
public const int MAX_PATH = 260;

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr InternetOpen(
string lpszAgent,
int dwAcessType,
string lpszProxyName,
string lpszProxyBypass,
int dwFlags);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr InternetConnect(
IntPtr hInternet,
string lpszServerName,
short nServerPort,
string lpszUserName,
string lpszPassword,
int dwService,
int dwFlags,
ref int dwContext);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool FtpGetCurrentDirectory(
IntPtr hConnect,
StringBuilder lpszCurrentDirectory,
ref int lpdwCurrentDirectory);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool FtpSetCurrentDirectory(
IntPtr hConnect,
string lpszCurrentDirectory);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr FtpOpenFile(
IntPtr hConnect,
string lpszFileName,
int dwAccess,
int dwFlags,
out int dwContext);

[DllImport("wininet.dll", SetLastError=true)]
public static extern bool InternetWriteFile(
IntPtr hFile,
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
int dwNumberOfBytesToWrite,
out int lpdwNumberOfBytesWritten);

[DllImport("wininet.dll", SetLastError=true)]
public static extern bool InternetReadFile(
IntPtr hFile,
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
int dwNumberOfBytesToRead,
out int lpdwNumberOfBytesRead
);

[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool InternetCloseHandle(IntPtr hInternet);

private WinInet()
{
}
}
}


// SimpleFTP.cs
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

namespace FireAnt.Net.Ftp
{
[Flags()]
public enum AccessMode
{
Read = WinInet.GENERIC_READ,
Write = WinInet.GENERIC_WRITE,
}

public enum TransferMode
{
Ascii = WinInet.FTP_TRANSFER_TYPE_ASCII,
Binary = WinInet.FTP_TRANSFER_TYPE_BINARY,
}

/// <summary>
/// SimpleFTP class using the WinInet API.
/// </summary>
public sealed class SimpleFtp : IDisposable
{
private IntPtr internet;
private IntPtr connection;
private IntPtr fileHandle;
private int context;

private const int BUFFER_SIZE = 2048;

public SimpleFtp(string host, string userName, string password)
{
internet = WinInet.InternetOpen(
null,
WinInet.INTERNET_OPEN_TYPE_DIRECT,
null,
null,
0);

if (internet == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}

connection = WinInet.InternetConnect(
this.internet,
host,
WinInet.INTERNET_DEFAULT_FTP_PORT,
userName,
password,
WinInet.INTERNET_SERVICE_FTP,
0,
ref this.context);

if (connection == IntPtr.Zero)
{
WinInet.InternetCloseHandle(this.internet);
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

~SimpleFtp()
{
this.CleanUp();
}

void IDisposable.Dispose()
{
this.CleanUp();
GC.SuppressFinalize(this);
}

public string CurrentDirectory
{
get
{
StringBuilder path = new StringBuilder(260);
int bufferSize = path.Capacity;

if (!WinInet.FtpGetCurrentDirectory(this.connection, path, ref bufferSize))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return path.ToString();
}
set
{
if (!WinInet.FtpSetCurrentDirectory(this.connection, value))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}

public void Close()
{
((IDisposable)this).Dispose();
}

public void OpenFile(string fileName, AccessMode access, TransferMode mode)
{
this.fileHandle = WinInet.FtpOpenFile(this.connection, fileName, (int)
access, (int) mode, out this.context);
if (this.fileHandle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}

public void CloseFile()
{
if (this.fileHandle != IntPtr.Zero)
{
if (WinInet.InternetCloseHandle(this.fileHandle))
{
this.fileHandle = IntPtr.Zero;
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}

public int WriteFile(string buffer)
{
byte[] bytes = new ASCIIEncoding().GetBytes(buffer);
return this.WriteFile(bytes);
}

public int WriteFile(byte[] buffer)
{
int byteCount;
if (!WinInet.InternetWriteFile(this.fileHandle, buffer, buffer.Length, out byteCount))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return byteCount;
}

public bool ReadFile(out string buffer)
{
// clear the buffer...
buffer = string.Empty;

// read from the file
int bytesRead;
byte[] readBuffer = new byte[SimpleFtp.BUFFER_SIZE];
bool success = WinInet.InternetReadFile(this.fileHandle, readBuffer,
readBuffer.Length, out bytesRead);
// the call failed!
if (!success)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}

// we got some data, so convert it for the return...
if (bytesRead != 0)
{
buffer = Encoding.ASCII.GetString(readBuffer, 0, bytesRead);
}

return (bytesRead != 0) ? true : false;
}

public bool ReadFile(byte[] buffer)
{
int bytesRead;
bool success = WinInet.InternetReadFile(this.fileHandle, buffer, buffer.Length, out bytesRead);
if (!success)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return (bytesRead != 0) ? true : false;
}

private void CleanUp()
{
if (this.fileHandle != IntPtr.Zero)
{
WinInet.InternetCloseHandle(this.fileHandle);
}

if (this.connection != IntPtr.Zero)
{
WinInet.InternetCloseHandle(this.connection);
}

if (this.internet != IntPtr.Zero)
{
WinInet.InternetCloseHandle(this.internet);
}
}
}
}

Anyway, should do most of the basic stuff... here is a quick little
snippit of code for using it...

SimpleFTP ftp = new SimpleFTP("myserver.mydomain.com", "myuser",
"mypassword");

if (ftp.CurrentDirectory != "my directory")
{
ftp.CurrentDirectory = "my directory";
}

string data;
ftp.OpenFile("myfile.txt", AccessMode.Read, TransferMode.Ascii);
while (ftp.ReadFile(out data))
// you can of course write the
// data to your local file here :)
Console.WriteLine(data);
}

// If your not done with the object, and
// are going to transfer more files, then
// call .CloseFile() instead to just close
// the current file handle.
ftp.Close();


Anyway, sorry for the C# - but you should be able to use this pretty
much the way it is. At least until we get this VB code worked out :)
 
How can one have 2 functions named the same thing? This makes no sense to
me at all.. :)

Different parmaters. It's called overloading. It's supported in VB.NET
as well. They are used depending on the type of data your trying to
receive. If you transfering binary (such as images), then you probably
want to use a byte array, and if you want to get text - then a string is
more appropriate.

I'm still going to look over your vb code tonight, but I just thought
I'd throw this version out as a quick and dirty alternative :)
 
You are the man! What you gave me earlier works wonderfully! No noftyness,
but it works :)
Overloading, going to have to look more into that one.
Tibby
 
You are the man! What you gave me earlier works wonderfully! No noftyness,
but it works :)

Thanks. It was really designed to be a quick and dirty solution to a
similar situation I had. It could use a lot of work, but any way there
you have it :)
Overloading, going to have to look more into that one.

It is pretty useful actually.
 
Back
Top