Skip to content

Instantly share code, notes, and snippets.

@tracker1
Created January 28, 2015 18:09
Show Gist options
  • Save tracker1/5204fd259e2f619ce4ec to your computer and use it in GitHub Desktop.
Save tracker1/5204fd259e2f619ce4ec to your computer and use it in GitHub Desktop.
SharpDoorKit - start of a ,Net based door kit - C wrapper for socket descriptor
/*******************************************************************************
SharpDoorKit Socket Descriptor Wrapper
(C) Copyright 2006 by Michael J. Ryan
Functionality thanks to the OpenDoors Online Software Programming Toolkit
================================================================================
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
or see: http://www.gnu.org/copyleft/gpl.html
================================================================================
File: sdwrap.c
Desc: Passthrough wrapper for IO based on a Socket Descriptor
This should make portability of the SharpDoorKit easier.
Revs: Date Ver Who Change
---------- ------- ----- ----------------------------------------
2006-05-31 1.0 MJR New File
*******************************************************************************/
#include <stdio.h>
#include <time.h>
#include "sdwrap.h"
/*** Windows Platform Definitions *********************************************/
#ifdef WIN32
//Windows Sockets
#include <windows.h>
#include <winsock.h>
#pragma comment(lib, "wsock32.lib")
static WSADATA WSAData; /* WinSock data */
#endif //WIN32
/******************************************************************************/
/*** *NIX Definitions *********************************************************/
#ifdef __NIX__
#include <sys/types.h>
//Unix/Berkley Sockets
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
// Win32 Compatability
#define SOCKET int
#define WSAEWOULDBLOCK EAGAIN
#define SOCKET_ERROR -1
#define WSAGetLastError() errno
#define ioctlsocket ioctl
#define closesocket close
#endif //__NIX__
/******************************************************************************/
static int started = 0;
//Starts the Winsock interface for windows..
static void StartNet() {
if (started == 0) {
#ifdef WIN32
WSAStartup(MAKEWORD(1,1), &WSAData);
#endif
started = 1;
}
}
/* -----------------------------------------------------------------------------
SDW_Sleep()
Sleeps for a number of milliseconds
----------------------------------------------------------------------------- */
void SDW_Sleep(int ms) {
#ifdef WIN32
if (ms < 1)
Sleep(1);
else
Sleep(ms);
#endif
#ifdef _NIX
if (ms < 1)
nanosleep(1);
else
nanosleep(ms)
#endif
}
/* -----------------------------------------------------------------------------
SendBytes()
Sends a series of bytes to a connected socket.
Returns the number of bytes sent, or MSG_ERROR
----------------------------------------------------------------------------- */
int DLLCALL SendBytes(SOCKET s, BYTE *bufferStream, int bufferSize, int* errorCode)
{
fd_set socket_set;
struct timeval tv;
int select_ret, send_ret;
StartNet();
FD_ZERO(&socket_set);
FD_SET(s,&socket_set);
tv.tv_sec=2;
tv.tv_usec=0;
if((select_ret = select(s+1,NULL,&socket_set,NULL,&tv)) != 1) {
#ifdef WIN32
*errorCode = WSAGetLastError();
#else
*errorCode = select_ret;
#endif
return(MSG_ERROR_SELECTING);
}
do {
send_ret = send(s, bufferStream, bufferSize, 0);
if (send_ret != SOCKET_ERROR)
break;
SDW_Sleep(25);
} while (WSAGetLastError() == WSAEWOULDBLOCK);
if (send_ret != bufferSize) {
#ifdef WIN32
*errorCode = WSAGetLastError();
#else
*errorCode = select_ret;
#endif
return (MSG_ERROR);
}
return send_ret;
}
/* -----------------------------------------------------------------------------
RecvByte()
Retrieves a single byte from from a connected socket.
Returns the next byte from the input stream.
Returns MSG_EMPTY if nothing to return after the timeout.
Returns MSG_ERROR on an error response.
----------------------------------------------------------------------------- */
int DLLCALL RecvByte(SOCKET s, int* errorCode)
{
fd_set socket_set;
struct timeval tv;
int select_ret, recv_ret;
byte nextByte = 0;
StartNet();
FD_ZERO(&socket_set);
FD_SET(s, &socket_set);
tv.tv_sec=0;
tv.tv_usec=1000;
select_ret = select(s+1, &socket_set, NULL, NULL, &tv);
if (select_ret == SOCKET_ERROR) {
#ifdef WIN32
*errorCode = WSAGetLastError();
#else
*errorCode = select_ret;
#endif
return (MSG_ERROR_SELECTING);
}
if (select_ret == 0)
return (MSG_EMPTY);
recv_ret = recv(s, &nextByte, 1, 0);
if(recv_ret == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
#ifdef WIN32
*errorCode = WSAGetLastError();
#else
*errorCode = select_ret;
#endif
return (MSG_ERROR);
}
return (int)nextByte;
}
using System;
using System.Runtime.InteropServices;
using System.IO;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace SharpDoorKit {
public class SocketDescriptorStream : Stream {
#region DLL/PInvoke method imports
/* SDWrap provides a common interface for sending/receiving data via the descriptor */
[DllImport("sdwrap")]
private static extern int SendBytes(Int32 s, IntPtr buffer, Int32 bufferSize, ref Int32 error);
[DllImport("sdwrap", CharSet = CharSet.Unicode)]
private static extern int RecvByte(Int32 s, ref Int32 error);
#endregion
//Used when an error is returned from the sdwrap
public class GeneralSocketError : ApplicationException {
public GeneralSocketError(string message) : base(message) { /**/ }
}
private const int MSG_ERROR = -2; //response if there was an error.
private const int MSG_EMPTY = -1; //response if there is nothing to receive.
private const int MAX_TIMEOUT = 300000; //5 minutes (in ms)
private int _descriptor; //holds the descriptor being communicated with.
private int _readTimeout = MAX_TIMEOUT; //holds the timeout value for reads. (default is max, 5 min (in ms)
private SocketDescriptorStream() { /**/ }
public SocketDescriptorStream(int socketDescriptor) {
_descriptor = socketDescriptor;
}
public int ReadByte(int timeout) {
int ot = this.ReadTimeout;
try {
this.ReadTimeout = timeout;
return ReadByte(true);
} finally {
this.ReadTimeout = ot;
}
}
public int ReadByte(bool wait) {
if (!wait)
return ReadByte();
DateTime timeout = DateTime.Now.AddMilliseconds(this.ReadTimeout);
int ret;
do {
if (DateTime.Now >= timeout)
throw new TimeoutException("Timeout during read operation.");
ret = ReadByte();
} while (ret < 0);
return ret;
}
public override int ReadByte() {
int err = 0;
int ret = RecvByte(_descriptor, ref err);
if (ret == MSG_ERROR)
throw new GeneralSocketError(string.Format("General Socket Error {0}, while attempting a read.", err));
return ret;
}
public override int Read(byte[] buffer, int offset, int count) {
DateTime timeout = DateTime.Now.AddMilliseconds(this.ReadTimeout);
int counter = 0;
for (int ret; counter < count; counter++) {
if (DateTime.Now >= timeout)
throw new TimeoutException("Timeout during read operation."); //should really never happen
ret = ReadByte();
if (ret < 0)
break;
buffer[offset + counter] = (byte)ret;
}
return counter;
}
public override void WriteByte(byte value) {
byte[] bout = new byte[1];
bout[0] = value;
Write(bout, 0, 1);
}
public override void Write(byte[] buffer, int offset, int count) {
IntPtr outptr = Marshal.AllocHGlobal(count);
Marshal.Copy(buffer, offset, outptr, count);
int err = 0;
int ret = SendBytes(_descriptor, outptr, count, ref err);
if (ret != count)
throw new GeneralSocketError(string.Format("General Socket Error {0}, while attempting a write.", err));
Marshal.FreeHGlobal(outptr);
}
public void Write(string output) {
byte[] bout = new byte[output.Length];
for (int i=0; i<bout.Length; i++)
bout[i] = (byte)output[i];
Write(bout, 0, bout.Length);
}
public override bool CanRead {
get { return true; }
}
public override bool CanWrite {
get { return true; }
}
public override bool CanSeek {
get { return false; }
}
public override bool CanTimeout {
get { return true; }
}
public override long Length {
get { return 0; }
}
public override long Position {
get { return 0; }
set { /* Not Needed/Used */ }
}
public override int WriteTimeout {
get {
throw new NotImplementedException("SocketDescriptorStream doesn't support WriteTimeout.");
}
set {
throw new NotImplementedException("SocketDescriptorStream doesn't support WriteTimeout.");
}
}
public override int ReadTimeout {
get {
return _readTimeout;
}
set {
if (value < 1 || value > MAX_TIMEOUT)
_readTimeout = MAX_TIMEOUT;
else
_readTimeout = value;
}
}
public override void Flush() { /* Not Used/Available */ }
public override long Seek(long offset, SeekOrigin origin) {
throw new NotImplementedException("SocketDescriptorStream doesn't support Seek.");
}
public override void SetLength(long value) {
throw new NotImplementedException("SocketDescriptorStream doesn't support SetLength.");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment