- Subject: Re: S-Lang & sockets
- From: Andreas Kraft <kraft@xxxxxxxxxxxx>
- Date: Tue, 23 Nov 1999 16:35:31 +0100 ((MEZ) Mitteleuropäische Zeit)
Hi,
On Tue, 23 Nov 1999, John E. Davis wrote:
> Alexandru Liuba <alexandru.liuba@xxxxxxxxxxx> wrote:
> >Does anyone know if slang can be used to connect, listen or send
> >messages to a port?
> I have not added any code for this.
I wrote some (c++) functions to open a TCP/IP connection to a server
and to establish a server. I attached the C++ file to this mail. There
is only one non-portable part in it, which is a class called "QMap"
which is a template to hold objects in a hash table. This is done
because I want to support more than one connection at a time. I am
sure you can identify the parts you have to change.
Please note, that this is work in progress :-) I tested the functions
on MS Windows (using the Watcom compiler), Sun Solaris and Linux
(egcs). Maybe they can be integrated into the next S-Lang release?
The client functions are:
integer TCPopen(string host, integer port);
Open a connection to a server on host at port. Returns a
connection id or -1 in case of an error.
void TCPclose(integer connection_id);
Close an open connection.
integer TCPsend(integer connection_id, bstring buffer);
Send binary data on an open connection. Returns the number of
bytes send or -1 in case of an error.
(integer, bstring) TCPreceive(integer connection_id, integer max);
Receive max bytes from an open connection and return the number of
bytes received and the data as a bstring.
The server functions are:
integer TCPinitServer(integer port);
Initialize a server on a port. The function returns a
connection_id or -1 in case of an error. The connection_id must be
used in TCPserve(). The server must be shut down with a call to
TCPclose().
void TCPserve(integer connection_id, Function callback);
This is the main loop of a server. connection_id is the identifier of a
connection session created with TCPinitServer(). The callback
function is either a reference to or a name of a function of the
format integer func(integer id). The callback function is called
when a client connects to the server on the port and a new client
connection is established. The identifier of the connection is
passed to the callback function and can be used in calls to
TCPsend() and TCPreceive(). The callback function must loop for
the whole connection to the connected client. It must return 0
when the server should wait for further connections or -1 when the server
should shut down. The callback function _must_not_ close the connection to
the client. This is done by the server loop.
I hope I could be of help.
Andreas
--
o _ Andreas Kraft
(\_|_) GMD FOKUS, kraft@xxxxxxxxxxxx, +49 30 3463-7232
T> ] [ The sky is the limit
//
// tcp.cc
//
// TCP connection functions for S-Lang.
//
// written 1999 by Andreas Kraft <kraft@xxxxxxxxxxxx>
//
// TODO: Close all connections on error
// Get the current client/server address
//
//////////////////////////////////////////////////////////////////////////////
//
// Includes for the networking stuff
//
# include <sys/types.h> /* used for socket routines */
# if defined(__WATCOMC__) || defined(_MSC_VER)
# include <winsock.h>
# else
# include <sys/socket.h> /* used for socket routines */
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <sys/file.h> /* used for fcntl() */
# endif
# include <stdio.h>
# include <fcntl.h> /* used for fcntl() */
# if !defined(__WATCOMC__) && !defined(_MSC_VER)
# include <unistd.h> /* defines STDIN_FILENO */
# endif
# include <errno.h>
//////////////////////////////////////////////////////////////////////////////
# include <slangutil.h>
# include <slang.h>
# include <QMap++.h>
EXTERN int gethostname(char *, int); // for linux...
struct tcpconnectionT {
int id;
int netfd;
int type; // 0 = client, 1 = server
};
typedef QMap<tcpconnectionT> ConnectionList;
static ConnectionList connections;
static long lastConnectionId = 0;
static long numconnections = 0;
static void SLTCPclose(int *);
//
//:+ int |TCPopen|(string host, int port): Open a TCP connection to the-
//: `host` on the given `port`. The function returns an identifier for-
//: the connection or -1.
static int
SLTCPopen(char * hostname, int * port) {
struct hostent *hostStr; // struct modified by gethostbyname()
struct sockaddr_in addr; // struct used to bind() socket
int error;
unsigned long ipaddr;
int netfd;
tcpconnectionT connection;
if (strlen(hostname) == 0) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPopen: no hostname given");
return -1;
}
# ifdef _WINDOWS_
WORD wVersionRequested = MAKEWORD( 1, 1 );
WSADATA wsaData;
int err = WSAStartup( wVersionRequested, &wsaData );
if (err != 0) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPopen: can't init WSAStartup()");
return -1;
}
# endif
// initalize the address used by bind()
memclr((char *) & addr, sizeof(addr));
addr.sin_family = AF_INET; // address family
addr.sin_port = htons(*port); // service port
// determine the address of the host
if ((ipaddr = inet_addr(hostname)) == (unsigned long)-1l) {
hostStr = gethostbyname(hostname);
if (hostStr == NULL) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPopen: Unable to obtain host entry for %s", hostname);
return -1;
}
memcpy((char *) &addr.sin_addr, hostStr->h_addr, hostStr->h_length);
} else
addr.sin_addr.s_addr = ipaddr;
// create a socket
netfd = socket(AF_INET,SOCK_STREAM,0);
if (netfd < 0) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPopen: unable to create socket");
return -1;
}
connection.id = ++lastConnectionId;
connection.netfd = netfd;
connection.type = 0;
connections.put(connection, connection.id);
numconnections++;
// connect to server
error = connect(netfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (error < 0) {
SLTCPclose(&(connection.id));
SLang_verror(SL_INTRINSIC_ERROR, "TCPopen: can't connect to server");
return -1;
}
return connection.id;
}
//
//:+ void |TCPclose|(int id): Close the TCP connection for the given `id`.
//
static void
SLTCPclose(int * id) {
tcpconnectionT connection;
if ( ! connections.exist(*id)) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPclose: no connection with ID=%d", *id);
return;
}
connection = connections[*id];
numconnections--;
# ifndef _WINDOWS_
if (connection.netfd >= 0)
close(connection.netfd);
# else
if (connection.netfd >= 0)
closesocket(connection.netfd);
if (numconnections == 0)
WSACleanup();
# endif
connections.del(*id);
}
//
//:+ int |TCPsend|(int id, SLang_BString_Type * buffer): Send some data-
//: via a TCP connection `id`. The `buffer` contains the binary data and-
//: `length` specifies the length of the data. The function returns the-
//: number of bytes send or -1 in case of an error.
static int
SLTCPsend(int * id, SLang_BString_Type * value) {
tcpconnectionT connection;
int sendlen;
unsigned int bufferlen;
unsigned char * buffer;
if ( ! connections.exist(*id)) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPsend: no connection with ID=%d", *id);
return -1;
}
connection = connections[*id];
if (connection.type != 0) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPsend: wrong connection type for client");
return -1;
}
buffer = SLbstring_get_pointer(value, &bufferlen);
sendlen = send(connection.netfd, (const char *)buffer, (int)bufferlen, 0);
if (sendlen != bufferlen) {
if (sendlen < 0) {
SLTCPclose(&connection.id);
SLang_verror(SL_INTRINSIC_ERROR, "TCPsend: can't send message");
return -1;
} else
SLang_vmessage("TCPsend: could only send %d of %d bytes", sendlen, bufferlen);
}
return sendlen;
}
//
//:+ (int length, bstring buffer) = |TCPreceive|(int id, int maxreceive): -
//: Receive some data from an TCP connection `id`. `maxreceive` bytes are-
//: read from the stream. The function returns the length of the received-
//: data and the buffer as a binary string.
//
static void
SLTCPreceive(int * id, int * maxbuffer) {
tcpconnectionT connection;
int receivelen;
static char * buffer = 0;
SLang_BString_Type * bstr = 0;
if ( ! connections.exist(*id)) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPreceive: no connection with ID=%d", *id);
return;
}
connection = connections[*id];
if (connection.type != 0) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPreceive: wrong connection type for client");
return;
}
if (*maxbuffer < 0) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPreceive: maxbuffer must be 0 or positive");
return;
}
if (buffer)
delete [] buffer;
buffer = new char[*maxbuffer+1];
memset(buffer, 0, *maxbuffer+1);
// check socket for normal inputs
receivelen = recv(connection.netfd, buffer, *maxbuffer, 0);
if (receivelen < 0) {
# ifndef _WINDOWS_
if (errno != EWOULDBLOCK) {
# else
if (WSAGetLastError() != WSAEWOULDBLOCK) {
#endif
SLTCPclose(&connection.id);
SLang_verror(SL_INTRINSIC_ERROR, "TCPreceive: Error calling recv()");
return;
} else
SLang_vmessage("TCPreceive: errno = EWOULDBLOCK");
} else { // EOF or data waiting in buffer
if (receivelen == 1 && buffer[0] == EOF) {
SLTCPclose(&connection.id);
SLang_verror(SL_INTRINSIC_ERROR, "TCPreceive: EOF character received");
return;
}
# if 0
if (receivelen == 0) { // interpret this as EOF
SLTCPclose(&connection.id);
}
# endif
}
// As BString
if (bstr)
SLbstring_free(bstr);
bstr = SLbstring_create((unsigned char *)buffer, receivelen);
SLang_push_integer(receivelen);
//SLang_push_string(buffer);
SLang_push_bstring(bstr);
}
/////////////////////////////////////////////////////////////////////////////
//
// Server functions
//
//
//:+ int |TCPinitServer|(int port): Initialise a file descriptor for a-
//: server connection. The `port` specifies the port number on which the-
//: server accepts connections. The function returns the identifier of-
//: the server connection or -1 in case of an error. The returned identifier-
//: must be passed to `TCPserve()`. The server is shut down with a call to-
//: `TCPclose()`.
//
static int
SLTCPinitServer(int * port) {
int sfd;
struct sockaddr_in addr;
char hostname[1000];
struct hostent *hp;
tcpconnectionT connection;
# ifdef _WINDOWS_
WORD wVersionRequested = MAKEWORD( 1, 1 );
WSADATA wsaData;
int err = WSAStartup( wVersionRequested, &wsaData );
if (err != 0) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPopen: can't init WSAStartup()");
return -1;
}
# endif
if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
SLang_verror(SL_INTRINSIC_ERROR, "TCPinitServer: Can't init socket");
return -1;
}
connection.id = ++lastConnectionId;
connection.netfd = sfd;
connection.type = 1;
connections.put(connection, connection.id);
numconnections++;
int flag = 1;
if (setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,(char*)&flag,sizeof(flag))) {
SLTCPclose(&connection.id);
SLang_verror(SL_INTRINSIC_ERROR, "TCPinitServer: can't setsockopt");
return -1;
}
if (gethostname(hostname, 1000)== -1) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPinitServer: No hostname found");
return -1;
}
hp = gethostbyname(hostname);
if (!hp) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPinitServer: No hostentry found");
return -1;
}
MEMCLR(&addr);
addr.sin_family = AF_INET;
addr.sin_port = htons((unsigned short)*port);
//memcpy(&addr.sin_addr,hp->h_addr,hp->h_length);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(connection.netfd,(struct sockaddr*)&addr, (int)sizeof(addr)) < 0) {
SLTCPclose(&connection.id);
SLang_verror(SL_INTRINSIC_ERROR, "TCPinitServer: can't bind address");
return -1;
}
if (listen (connection.netfd, 5) == -1) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPinitServer: listen() failed");
return -1;
}
return connection.id;
}
//
//:+ void |TCPserve|(int id, Function callback): This is the main loop of-
//: a server. `id` is the identifier of a connection session created with-
//: `TCPinitServer()`. The `callback` function is either a reference to or a-
//: name of a function of the format `int func(int id)`. The callback function-
//: is called when a client connects to the server on the port and a new-
//: client connection is established. The identifier of the connection is-
//: passed to the callback function and can be used in calls to `TCPsend()`-
//: `TCPreceive()`. The callback function must loop for the whole connection to the-
//: connected client. It returns 0 when the server should wait for further-
//: connections or -1 when the server should shut down. The callback function-
//: |does not| close the connection to the client. This is done by the-
//: server loop.
//:
static void
SLTCPserve() {
int id;
SLang_Name_Type *cbfunc;
// First get the index and the callback function from the stack
if (SLang_Num_Function_Args != 2) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: wrong number of arguments");
return;
}
if ((cbfunc = SLang_pop_function()) == NULL) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: 2.argument must be a function");
return;
}
if (SLang_pop_integer(&id) == -1) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: 1.argument must be an integer");
return;
}
// Get connection
tcpconnectionT connection;
tcpconnectionT newconnection;
if ( ! connections.exist(id)) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: no connection with ID=%d", id);
return;
}
connection = connections[id];
if (connection.type != 1) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: wrong connection type for server");
return;
}
// Do the TCP stuff
int result;
struct sockaddr addr;
int addrlen;
do {
MEMCLR(&addr);
addrlen = 0;
int nfd = accept(connection.netfd, NULL, NULL);
if (nfd < 0) {
# ifndef _WINDOWS_
if (errno != EWOULDBLOCK)
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: error on accept (%d - %s)", errno, strerror(errno));
# else
if (WSAGetLastError() != WSAEWOULDBLOCK)
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: error on accept (%d)", WSAGetLastError());
#endif
return;
}
// Create a new connection and pass the id to the callback function
newconnection.id = ++lastConnectionId;
newconnection.netfd = nfd;
newconnection.type = 0;
connections.put(newconnection, newconnection.id);
numconnections++;
SLang_push_integer(newconnection.id);
// Execute the callback function "int cbfunc(int id)". The function
// returns 0 when the server loop should continue or -1 if it should
// terminate
if (SLexecute_function(cbfunc) == -1) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: error executing callback function");
return;
}
if (SLang_pop_integer(&result) == -1) {
SLang_verror(SL_INTRINSIC_ERROR, "TCPserve: missing return value from callback function");
return;
}
SLTCPclose(&newconnection.id); // close the connection
} while (result == 0);
}
/////////////////////////////////////////////////////////////////////////////
static SLang_Intrin_Fun_Type SLtcp_Name_Table[] = {
// Client functions
MAKE_INTRINSIC_SI("TCPopen", SLTCPopen, SLANG_INT_TYPE),
MAKE_INTRINSIC_I("TCPclose", SLTCPclose, SLANG_VOID_TYPE),
MAKE_INTRINSIC_2("TCPsend", SLTCPsend, SLANG_INT_TYPE, SLANG_INT_TYPE, SLANG_BSTRING_TYPE),
MAKE_INTRINSIC_II("TCPreceive", SLTCPreceive, SLANG_VOID_TYPE),
// Server functions
MAKE_INTRINSIC_I("TCPinitServer", SLTCPinitServer, SLANG_INT_TYPE),
MAKE_INTRINSIC_0("TCPserve", SLTCPserve, SLANG_VOID_TYPE),
SLANG_END_TABLE
};
int
init_SLtcp() {
return SLadd_intrin_fun_table(SLtcp_Name_Table, "_TCP");
}
[1999 date index]
[1999 thread index]
[Thread Prev] [Thread Next]
[Date Prev] [Date Next]