Hi Mindaugas,
>>> socket.c - alternative lightweight socket library, I do not like
>>> hbinet.c a lot. If you want to compile it for Linux,
>>> you'll need to adjust includes, but code should be
>>> portable;
I ported it to Linux but it needs also some cleanup to remove MS-Windows
only code and fix some function parameters which are not POSIX compatible:
1. Here is the list of necessary header files:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
2. SOCKET and INVALID_SOCKET are MS-Windows only types as declarations so
these lines have to be added:
#define INVALID_SOCKET (-1)
typedef int SOCKET;
3. Some functions like accept(), getsockname(), getpeername() needs as
parameter reference to socklen_t not int so for Windows builds we should
add:
#define socklen_t int
and change iSize type from int to socklen_t.
Unfortunately some *nixes also uses int instead of socklen_t so the
above will have to be adopted also for them.
4. documented member of struct sockaddr_in which keeps the address is
sin_addr.s_addr not sin_addr.S_un.S_addr so this code:
( ( struct sockaddr_in* ) sa)->sin_addr.S_un.S_addr = inet_addr(...)
should be changed to:
( ( struct sockaddr_in* ) sa)->sin_addr.s_addr = inet_addr(...)
5. SOCKET_INIT() and SOCKET_EXIT() are not necessary so they can be changed
to simple:
hb_retni( 0 );
in *nixes.
6. In SOCKET_ERROR()
hb_retni( WSAGetLastError() );
should be changed to:
hb_retni( h_errno );
h_errno may not be available in some older *nixes and in such case should
be replaced by other function or variable. Anyhow here we have 1-st
serious problem. Portable way of error reporting. The error values are
different between platforms so we should introduce our own list of errors
and make necessary translation. OS level error code can be used only as
helper information.
7. closesocket() is MS-Windows extension. In *nixes close() should be used.
8. The constant identifiers for shutdown() are different. Instead of SD_BOTH
SHUT_RDWR should be used.
With the above modifications it can be used with current Linux versions.
Anyhow there are still some problems which can be exploited by upper level
.prg code because there are some small differences in socket functions
behavior on different platform:
1. Some functions which returns pointers to some structures/buffers may not
be MT safe on some platforms and usually are not. They should be replaced
by MT safe functions, f.e. inet_ntoa().
2. bind(), listen(), close(), shutdown() and some other functions changes
errno but may not change h_errno on some systems. This should be
respected by SOCKET_ERROR() function so we will have to store errors
after function call and probably bound them with socket or at least keep
them in thread local area to make error checking MT safe. Here we should
probably add error code translation.
3. select() works in different way in *nixes then in MS-Windows. In some
cases in Windows select() is not interrupted when connection is broken
and the associated socket number is not given in exception fd_set.
It means that the same .prg code used in *nixes will not work in
Windows in the same way. If we do not hide such differences inside
Harbour functions then we will force users to resolve them at .prg
level. I do not think that many users have enough knowledge to make it.
4. reading from socket closed by peer host also gives different results
and the method of detecting closed or broken connections is different
depending on blocking or unblocking socket IO mode.
I like the idea of giving as close as possible access to socket functions
at .prg level but we should also design a layer which will hide platform
differences. Each of use has own preferences from C programming. F.e.
I used to use read() and write() for connected sockets instead of recv()
send(). In general I used them just like any other descriptors for files,
pipes, character devices, ... also with select(). But it does not work in
MS-Windows and I have to accept it and change my preferences if I want
to create common for all platforms harbour interface.
>> Just for curiosity what you don't like in hbinet.c ?
> 1) strange, difficult to remember function names, ex., hb_InetPort()
> returns port, but I can't remember local or remote. getpeername() is
> more clear to me. hb_InetTimeout(), hb_InetTimelimit() - I do not know
> that is we difference. hb_InetCount() - number of what? Internet
> providers on my computer? I do not find bind() and listen() functions in
> the list. hb_InetRecvEndBlock() - what kind of animal is it?
I only remember that one is inter recv()/send() delay and second is
used as total timeout limit for whole receive or send operation.
hb_inet*() functions try to complete this operation until given
stop condition (f.e. full buffer received or transmitted) so they
can repeat recv()/send() operations.
I've just check hbinet.c source code that hb_InetCount() returns number
of bytes received or sent in given socket in recently executed socket
read/write hb_inet*() function so seems to be alternate form of extracting
information which is returned or passed to parameter by reference by these
functions.
hb_InetRecvEndBlock() read from socket data until it receives one or
more given terminators. Probably the name is confusing - I also have to
refresh my mind checking the source code.
In general such functions are very usefull, f.e. this code in uhttpd2.prg
cRequest := ""
nLen := 1
DO WHILE AT(CR_LF + CR_LF, cRequest) == 0 .AND. nLen > 0
nLen := socket_recv(hSocket, @cBuf)
cRequest += cBuf
ENDDO
Can be replaced by:
cRequest := hb_inetRecvEndBlock( hSocket, CR_LF + CR_LF )
Please also note that the original version cannot be used for protocols
which may pass additional data after double CR_LF because it's possible
that it will be read as part of cRequest and lost. Unfortunately the
internal implementation of hb_inetRecvEndBlock() and hb_inetRecvLine()
is very inefficient and reads data from socket in single bytes. It's a
performance killer. To resolve it for connected socket we should implement
read buffer which will be used like FILE stdio structure reading data in
bigger peaces and then it will be used by all Harbour level socket functions.
> Perhaps I'm just used to see original socket function names and original
> function behaviour. I need to start learning sockets from beginning, if
> I want to write an application using hbinet.c. Actually I've tried to
> use hbinet.c instead of my own library for uhttpd to make it work under
> pure Harbour, but after I've used hb_InetCreate() and hb_InetServer(),
> I've understood I'm going to go my way. I haven't reached hb_InetAccept().
Each time when I want to write sth using hb_inet*() functions I'm using
xharbour documentation from xharbour/doc/inet.txt and verify it with source
code. I also do not remember the exact function names and their behavior
because I use my own socket library. Anyhow my library is not easy portable
to MS-Windows just like your code so I cannot commit it to Harbour. Anyhow
each portable library which should hide some platform differences will have
to give some upper level interface and cannot be simple wrapper to existing
socket functions. I also think that it's not bad to implement some functions
like hb_inetRecvEndBlock() (though probably using different names ;-)) to
move some operations which may be very expensive in .prg level to .c code
so I'm not against them.
> 2) absent C API;
It's very serious problem. I assume that I should give higher priority to
new hbinet implementation.
> 3) looking to .c code is also a little difficult.
I hope that the new implementation will be much easier to understand.
Separating Harbour C API from .prg functions, cleaner definition for
base socket structure and code more closer to original BSD socket
interface should help.
best regards,
Przemek
_______________________________________________
Harbour mailing list
[email protected]
http://lists.harbour-project.org/mailman/listinfo/harbour