Ich hab bei mir etwas "quick and dirty"-Code für Windows gefunden, ist eine Mischung aus C und (prozeduraler) C++-Notation. "Windows"-spezifisch scheint aber aber nur das Include des Winsock-API, was sich von original Berkeley nur in Nuancen unterscheidet. Ich weiß auch nicht mehr, welche Anteile von mir sind. #define für Konstanten benutze ich eigentlich seit 15 Jahren nicht mehr. Ich stelle das mal hier ein, ohne jede Gewähr. Und ich hoffe, dass keine Rechter Dritter verletzt werden. Bitte, bitte nicht als Ersatz für eine gescheite OO-Implementierung nehmen. Ich vermute, es ging damals nur um einen "Gangbarkeitsversuch", aufbauend auf zugeliefertem Code-Fragment. Das Ergebnis zeigt den Umgang mit den Sockets in C/C++, und den grundsätzlichen Kommunikationsaufbau. Mehr nicht.
Code: Alles auswählen
#include <stdio.h>
#include <time.h>
#include "winsock2.h"
//--------------------------------------------
// protocol version
#define VERSION 2
//--------------------------------------------
// commands
#define HELLO 1
#define ACK_HELLO 2
#define NEEDED_DATA 3
#define ACK_NEEDED_DATA 4
#define DATA 0x0a
//--------------------------------------------
// sub-commands
#define DATA_CTRLSTAND 0x0a
#define DATA_FINAL 0
//--------------------------------------------
// id for control stand client
#define CLIENT_CTRLSTAND 2
//--------------------------------------------
// data value ids
#define KEINE_FUNKTION 0
#define GESCHWINDIGKEIT 1
#define DRUCK_HAUPTLUFTLEITUNG 2
#define DRUCK_BREMSZYLINDER 3
#define DRUCK_HAUPTLUFTBEHAELTER 4
#define ZUGKRAFT_GESAMT 5
#define ZUGKRAFT_PRO_ACHSE 6
#define STROM 7
#define SPANNUNG 8
#define MOTORDREHZAHL 9
#define UHRZEIT_STUNDE 10
#define UHRZEIT_MINUTE 11
#define UHRZEIT_SEKUNDE 12
#define LZB_ZIEL_GESCHWINDIGKEIT 13
#define LZB_AFB_SOLL_GESCHWINDIGKEIT 14
#define LZB_ZIELWEG 15
#define FAHRSTUFE 16
#define FENSTER_3D 17
#define AFB_SOLL_GESCHWINDIGKEIT 18
#define DRUCK_HILFSLUFTBEHAELTER 19
#define LM_PZB_1000HZ 20
#define LM_PZB_500HZ 21
#define LM_PZB_BEFEHL 22
#define LM_PZB_ZUGART_U 23
#define LM_PZB_ZUGART_M 24
#define LM_PZB_ZUGART_O 25
#define LM_LZB_H 26
#define LM_LZB_G 27
#define LM_LZB_E40 28
#define LM_LZB_EL 29
#define LM_LZB_ENDE 30
#define LM_LZB_V40 31
#define LM_LZB_B 32
#define LM_LZB_S 33
#define LM_LZB_U 34
#define LM_LZB_PRUEFEN 35
#define LM_SIFA 36
#define LM_HAUPTSCHALTER 37
#define LM_GETRIEBE 38
#define LM_SCHLEUDERN 39
#define LM_GLEITEN 40
#define LM_MG_BREMSE 41
#define LM_H_BREMSE 42
#define LM_R_BREMSE 43
#define LM_HOCHABBREMSUNG 44
#define LM_SCHNELLBREMSUNG 45
#define LM_NOTBREMSUNG 46
#define LM_TUEREN 47
#define LM_TFZ_NUMMER 48
#define LM_MAX_TFZ_GESCHWINDIDIGKEIT 49
#define LM_UHRZEIT 50 // TDateTime, 8 Byte
#define SCHALTER_FAHRSTUFEN 51
#define SCHALTER_FUEHRERBREMSVENTIL 52
#define SCHALTER_DYN BREMSE 53
#define SCHALTER_ZUSATZBREMSE 54
#define SCHALTER_AFB_GESCHWINDIGKEIT 55
#define SCHALTER_AFB_EIN_AUS 56
#define SCHALTER_MG_BREMSE 57
#define SCHALTER_PZB_WACHSAM 58
#define SCHALTER_PZB_FREI 59
#define SCHALTER_PZB_BEFEHL 60
#define SCHALTER_SIFA 61
#define SCHALTER_HAUPTSCHALTER 62
#define SCHALTER_MOTOR_EIN_AUS 63
#define SCHALTER_FAHRTRICHTUNG 64
#define SCHALTER_PFEIFE 65
#define SCHALTER_SANDEN 66
#define SCHALTER_TUEREN 67
#define SCHALTER_GLOCKE 68
#define SCHALTER_LOKBREMSE_ENTLUEFTEN 69
#define SCHALTER_SCHLEUDERSCHUTZBREMSE 70
#define LM_DREHZAHLVERSTELLUNG 71
#define LM_FAHRTRICHTUNG_VOR 72
#define LM_FAHRTRICHTUNG_ZURUECK 73
#define SCHALTER_SIGNUM 74
#define LM_LZB_ZIELWEG_AB_0 75
#define LZB SOLL_GESCHWINDIGKEIT 76
#define LM_BLOCK_BIS_ZU_DEM_DIE_STRECKE_FREI_IST 77 // String
#define SCHALTER_LUEFTER 78
#define LM_GNT_G 79
#define LM_GNT_U 80
#define LM_GNT_B 81
#define LM_GNT_S 82
#define HINTERGRUNDBILD 83
#define PLATZHALTER_NACHTINSTRUMENT 84
#define STRECKEN_KM 85
#define TUEREN 86
#define AUTOPILOT 87
#define REISEZUG 88
#define PZB_SYSTEM 89
#define FRAMES_PER_SECOND 90
#define FUEHRERSTAND_SICHTBAR 91
#define NAECHSTER_BLOCKNAME 92 // String
#define NAECHSTES_GLEIS 93 // String
#define BREMSHUNDERTSTEL 94
#define BREMSSTELLUNG 95
#define ZUGDATEI 96 // String
//--------------------------------------------
// a string in delphi
// no 0-termination !
#pragma pack (1)
struct DelphiString {
unsigned char length;
unsigned char string [255];
};
#pragma pack ()
//--------------------------------------------
// union for data value types returned
union DataValues {
DelphiString valString;
double valDateTime;
float valFloat;
};
//--------------------------------------------
// get the length of a value data field
int getDataValLen (unsigned char idData, DataValues * pVal) {
int len;
len = 0;
switch (idData) {
default:
len = sizeof(float);
break;
case LM_BLOCK_BIS_ZU_DEM_DIE_STRECKE_FREI_IST:
case NAECHSTER_BLOCKNAME:
case NAECHSTES_GLEIS:
case ZUGDATEI:
if (!pVal)
break;
len = pVal->valString.length + 1;
break;
case LM_UHRZEIT:
len = sizeof (double);
break;
}
return len;
}
//--------------------------------------------
// send a datagram, executable function
int sendDatagramExe (SOCKET * pSock, unsigned short int cmd, unsigned short int * pSubCmd, char * data, int datalen) {
// long int assumed as 4 byte
unsigned long int len;
int bytesSent;
unsigned short int ncmd;
unsigned short int nsubcmd;
// network byte order
ncmd = htons (cmd);
len = datalen + 2; // sizeof (short int);
if (pSubCmd) {
len += 2;
nsubcmd = htons (*pSubCmd);
}
//no need to send TCP datagrams in one go
//TCP will pack automatically
//datagram len
bytesSent = send(*pSock, (char*)&len, 4, 0 );
if (bytesSent != 4)
return -1;
// datagram command
bytesSent = send(*pSock, (char*)&ncmd, 2, 0 );
if (bytesSent != 2)
return -1;
// datagram sub command
if (pSubCmd) {
bytesSent = send(*pSock, (char*)&nsubcmd, 2, 0 );
if (bytesSent != 2)
return -1;
}
// datagram data
if (datalen > 0) {
bytesSent = send(*pSock, data, datalen, 0 );
if (bytesSent != datalen)
return -1;
}
printf( "Command %d sent, total length = %d\n", cmd, len );
return 0;
}
//--------------------------------------------
// send a datagram, wrapper for calls w/o sub-command
int sendDatagram (SOCKET * pSock, unsigned short int cmd, char * data, int datalen) {
return sendDatagramExe (pSock, cmd, 0, data, datalen);
}
//--------------------------------------------
// receive a datagram, incl. basic checks
int receiveDatagram (SOCKET * pSock, unsigned short int * pCmd, char* pData, int* pDatalen) {
long int len;
long int datalen;
int bytesRecv;
unsigned short int ncmd;
// receive header
bytesRecv = recv( *pSock, (char*)&len, 4, 0 );
if (bytesRecv != 4)
return -1;
if (len <= 2 || !pCmd)
return -1;
// receive command
bytesRecv = recv( *pSock, (char*)&ncmd, 2, 0 );
if (bytesRecv != 2)
return -1;
*pCmd = ntohs (ncmd);
datalen = len - 2;
if (datalen > *pDatalen)
return -1;
// receive data
bytesRecv = recv( *pSock, pData, datalen, 0 );
if (bytesRecv != datalen)
return -1;
*pDatalen = datalen;
return 0;
}
//--------------------------------------------
// send & receive HELLO datagram
// client name hard coded in demo
int primitiveHello (SOCKET * pSock) {
char helloData[] = {VERSION, CLIENT_CTRLSTAND, 4, 'D', 'e', 'm', 'o'};
unsigned short int cmd;
int result;
char replyData;
int replySize;
cmd = HELLO;
result = sendDatagram (pSock, cmd, helloData, sizeof(helloData));
if (result < 0)
return -1;
replySize = sizeof(replyData);
result = receiveDatagram (pSock, &cmd, &replyData, &replySize);
if (result < 0)
return -1;
if (cmd != ACK_HELLO || replyData != 0)
return -1;
printf( "\"Hello\" primitive completed.\n" );
return 0;
}
//--------------------------------------------
// send & receive NEEDED_DATA datagram, executable function
int primitiveDataExe (SOCKET * pSock, unsigned short int dataCmd, unsigned char * idData, int idDatalen) {
int result;
char replyData;
int replySize;
unsigned short int cmd;
cmd = NEEDED_DATA;
result = sendDatagramExe (pSock, cmd, &dataCmd, (char*)idData, idDatalen);
if (result < 0)
return -1;
replySize = sizeof(replyData);
result = receiveDatagram (pSock, &cmd, &replyData, &replySize);
if (result < 0)
return -1;
if (cmd != ACK_NEEDED_DATA || replyData != 0)
return -1;
return 0;
}
//--------------------------------------------
// send & receive NEEDED_DATA datagram, wrapper for actual subscription
int primitiveData (SOCKET * pSock, unsigned char * idData, int idDatalen) {
int result;
result = primitiveDataExe (pSock, DATA_CTRLSTAND, idData, idDatalen);
if (result < 0)
return -1;
printf( "\"Needed Data\" primitive completed.\n" );
return 0;
}
//--------------------------------------------
// send & receive NEEDED_DATA datagram, wrapper for terminator
int primitiveDataFinal (SOCKET * pSock) {
int result;
result = primitiveDataExe (pSock, DATA_FINAL, 0, 0);
printf( "\"Needed Data Final\" primitive completed.\n" );
return 0;
}
//--------------------------------------------
// connect to server, address hard coded to local in demo
int connectServer (SOCKET * pSock) {
// Initialize Winsock.
WSADATA wsaData;
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if ( iResult != NO_ERROR )
printf("Error at WSAStartup()\n");
// Create a socket.
*pSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if ( *pSock == INVALID_SOCKET ) {
printf( "Error at socket(): %ld\n", WSAGetLastError() );
WSACleanup();
return -1;
}
// Connect to a server.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" );
//clientService.sin_addr.s_addr = inet_addr( "192.168.0.2" );
clientService.sin_port = htons( 1435 );
if ( connect( *pSock, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) {
printf( "Failed to connect.\n" );
WSACleanup();
return -1;
}
printf( "Connected.\n" );
return 0;
}
//--------------------------------------------
// init a session
int initSession (SOCKET * pSock) {
int result;
// send HELLO
result = primitiveHello (pSock);
if (result < 0)
return -1;
// IDs for subscription
unsigned char neededData[] = {
GESCHWINDIGKEIT,
LM_PZB_1000HZ,
LM_PZB_500HZ,
LM_UHRZEIT,
NAECHSTER_BLOCKNAME,
ZUGDATEI
};
// send NEEDED_DATA
result = primitiveData (pSock, neededData, sizeof(neededData));
if (result < 0)
return -1;
// finish
result = primitiveDataFinal (pSock);
if (result < 0)
return -1;
printf( "Session initialized.\n" );
return 0;
}
//--------------------------------------------
// handling unknown IDs
void handleDataDefault (unsigned char idData) {
printf ("Data id = %d\n", idData);
}
//--------------------------------------------
// sample of handling float value (single precision)
void handleDataFloat (unsigned char idData, float val) {
printf ("Data id = %d, float val = %5.1f\n", idData, val);
}
//--------------------------------------------
// sample of handling bool value
void handleDataBool (unsigned char idData, float val) {
int boolVal = val == 1;
printf ("Data id = %d, bool val = %d\n", idData, boolVal);
}
//--------------------------------------------
// month lengths for handling Delphi format
int MonthLength [] = {31,28,31, 30,31,30, 31,31,30, 31,30,31};
//--------------------------------------------
// sample of handling weird Delphi date & time format
void handleDataTime (unsigned char idData, double val) {
// lep years w/ century exceptions
double OneYearInDays = 365.25;
// days is integral part of value
int days = (int)val;
// time of day is fractional part of val
double time = val - days;
// simplified handling of century exception
if (days > 100 * OneYearInDays)
days --;
int year = (int)(days / OneYearInDays);
days -= (int)(year * OneYearInDays);
year = year + 1900;
int leapyear = year % 4 == 0;
int leapyear100 = year % 100 == 0;
int leapyear400 = year % 400 == 0;
leapyear = leapyear && (!leapyear100 || leapyear400);
if (leapyear)
MonthLength[1] = 29;
else
MonthLength[1] = 28;
int idx = 0;
while (idx <= 11 && MonthLength[idx] <= days) {
days -= MonthLength[idx];
idx++;
}
int month = idx + 1;
// time of day in secs
int secs = (int)(time * 3600 * 24);
int hour = secs / 3600;
secs = secs % 3600;
int min = secs / 60;
secs = secs % 60;
tm tim;
tim.tm_year = year - 1900;
tim.tm_mon = month - 1;
tim.tm_mday = days;
tim.tm_hour = hour;
tim.tm_min = min;
tim.tm_sec = secs;
tim.tm_isdst = 0;
char timstr [32];
strftime (timstr, 32, "%d.%m.%Y %H:%M:%S", &tim);
printf ("Data id = %d, date/time val = %s\n", idData, timstr);
}
//--------------------------------------------
void handleDataString (unsigned char idData, DelphiString * pVal) {
pVal->string[pVal->length] = 0;
printf ("Data id = %d, string val = \"%s\"\n", idData, pVal->string);
}
//--------------------------------------------
// receiving and processing data values from server
int receiveData (SOCKET * pSock) {
char buf [2048];
int result;
unsigned short int cmd;
char idData;
DataValues * pDataValues;
int datalen;
int idx;
datalen = 2048;
result = receiveDatagram (pSock, &cmd, buf, &datalen);
if (result < 0)
return -1;
if (cmd != DATA)
return -1;
// data body may contain more than one value
idx = 0;
while (idx < datalen) {
idData = buf[idx++];
pDataValues = (DataValues*)&buf[idx];
idx += getDataValLen (idData, pDataValues);
switch (idData) {
default:
handleDataDefault (idData);
break;
case GESCHWINDIGKEIT:
handleDataFloat (idData, pDataValues->valFloat);
break;
case LM_PZB_1000HZ:
case LM_PZB_500HZ:
handleDataBool (idData, pDataValues->valFloat);
break;
case LM_UHRZEIT:
handleDataTime (idData, pDataValues->valDateTime);
break;
case NAECHSTER_BLOCKNAME:
case ZUGDATEI:
handleDataString (idData, &pDataValues->valString);
break;
}
}
return 0;
}
//--------------------------------------------
// main function of demo program
int main(int, char**) {
SOCKET sock;
int result;
result = connectServer (&sock);
if (result < 0)
return 0;
result = initSession (&sock);
if (result < 0)
return 0;
printf( "Entering endless data loop. Ctrl/C to abort.\n" );
while (result == 0)
result = receiveData (&sock);
return 1;
}