On 2001-02-26 08:21:23, Stig Venaas <[EMAIL PROTECTED]> wrote:
> Sounds good to me. It's also impossible in a portable way to know if a
> socket is AF_INET, AF_INET6 or AF_LOCAL, so for socket() function etc.
> I've been thinking of doing something like this to store what domain
> a socket is in (maybe type and protocol too). So I think that all
We could add these fields to the php_sockbuf, along with the things listed below; in
this implementation, a socketd will always have a php_sockbuf present.
I've been delving into it, and these are my thoughts:
When opening a socket there are 3 stages (and thus operations on the socket):
1. Create a socketd of appropriate domain/type/family (socket(2))
2. resolve the address to connect to (domain specific)
3. make the connection (connect_nonb)
If there is an error in one of these stages the reason might also need to be returned
in a human readable form.
Once the socket is opened we have 3 remaining operations:
1. read
2. write
3. close
If we bring this together (and borrow from fopen_wrappers) we can have an extensible
socket abstraction, where the protocol part of the hostname from the fsockopen call
determines which underlying socket family to use.
The protocol name would map to a php_socket_ops structure, which might look something
like this:
struct php_socket_ops {
int (*socket)(void ** cookie, socklen_t * addrlen);
int (*resolve)(void * cookie, struct sockaddr * addr, char * hostname, unsigned
short port);
int (*connect)(void * cookie, struct sockaddr * addr, struct timeval * timeout);
int (*strerror)(void * cookie, int errno, char * buf, size_t buflen);
int (*close)(void * cookie);
size_t (*read)(void * cookie, char * buf, size_t buflen);
size_t (*write)(void * cookie, char * buf, size_t buflen);
};
php_sockbuf {
...
struct php_socket_ops * ops;
void * cookie;
};
It should be fairly self explanatory how things work, but the socket, resolve and
connect methods need a little more detail:
Since the size of a struct sockaddr varies depending on the domain of the socket, the
call to socket returns the size required for this particular socket type. It is the
callers responsibility to allocate a buffer of that size and pass it to the resolve
and connect methods. After that point the buffer can be freed.
So, the pseudo code for php_fsockopen would like something like this:
(most error checking removed for brevity)
... parse/convert php args : hostname, port, timeout ...
... extract protocol string from hostname, default to "tcp://" ...
struct php_socket_ops * ops = fsockprotocol_to_ops(protocol);
void * cookie = NULL;
socklen_t socklen;
struct sockaddr * addr;
// create a socket and return the address length and cookie
int socketd = ops->socket(&cookie, &socklen);
// allocate address buffer
addr = emalloc(socklen);
// resolve the address
ops->resolve(cookie, addr, hostname, port);
// make the connection - note that the error code is returned
int errcode = ops->connect(cookie, addr, &timeout);
// free the addr buffer here to avoid complications in exiting this function
efree(addr);
// check success of connection
if (errcode != 0)
{
// failed, so complain - get the error message
char errbuf[256];
ops->strerror(cookie, errcode, errbuf, sizeof(errbuf));
// copy/use/print errbuf
// cleanup
ops->close(cookie);
RETURN_FALSE;
}
// succeeded; record the socket and cookie in a php_sockbuf
struct php_sockbuf * sock = SOCK_FIND(socketd);
sock->ops = ops;
sock->cookie = cookie;
RETURN_TRUE;
Thats the basics. The other side of the implementation, for tcp sockets might look
something like this: (udp would be similar)
int tcp_socket(void ** cookie, socklen_t * len)
{
int socketd = socket(AF_INET, SOCK_STREAM, 0);
*len = sizeof(struct sockaddr_in);
*cookie = (void*)socketd;
return socketd;
}
int tcp_resolve(void * cookie, struct sockaddr * addr, char * hostname, unsigned short
port)
{
struct sockaddr_in * server = (struct sockaddr_in*)addr;
server.sin_family = AF_INET;
if (php_lookup_hostname(hostname, &server->sin_addr))
return errno;
server.sin_port = htons(port);
return 0;
}
int tcp_connect(void * cookie, struct sockaddr * addr, struct timeval * timeout)
{
if (connect_nonb((int)cookie, addr, sizeof(struct sockaddr_in), timeout) ==
SOCK_CONN_ERR)
return errno;
return 0;
}
// declared this way because the openSSL error functions are like this
int tcp_strerror(void * cookie, int errno, char * buf, size_t buflen)
{
char * errstr = strerror(errno);
size_t len = strlen(errstr);
if (len > buflen)
len = buflen;
strncpy(buf, errstr, len);
return 0;
}
int tcp_close(void * cookie)
{
shutdown((int)cookie, 0);
closesocket((int)cookie);
}
size_t tcp_read(void * cookie, char * buf, size_t buflen)
{
return read((int)cookie, buf, buflen);
// or recv((int)cookie, buf, buflen, 0)
}
size_t tcp_write(void * cookie, char * buf, size_t buflen)
{
return write((int)cookie, buf, buflen);
}
Hopefully, you all get the idea.
Possible problems that I can see with this are that some of the traditional socket
operations might not be applicable to all the different types of sockets - if it does
pose a problem, a possible solution might be to add another method to the ops
structure.
Another possible problem is returning error codes/messages - perhaps we should have an
errno method too, because the openSSL calls keep the errno per connection.
Can you see any other problems we should consider?
Let me know your thoughts,
--Wez.
--
PHP Development Mailing List <http://www.php.net/>
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
To contact the list administrators, e-mail: [EMAIL PROTECTED]