📄 sockutils.c
字号:
}
}
/*!
\brief Closes the present (TCP and UDP) socket connection.
This function sends a shutdown() on the socket in order to disable send() calls
(while recv() ones are still allowed). Then, it closes the socket.
\param sock: the socket identifier of the connection that has to be closed.
\param errbuf: a pointer to an user-allocated buffer that will contain the complete
error message. This buffer has to be at least 'errbuflen' in length.
It can be NULL; in this case the error cannot be printed.
\param errbuflen: length of the buffer that will contains the error. The error message cannot be
larger than 'errbuflen - 1' because the last char is reserved for the string terminator.
\return '0' if everything is fine, '-1' if some errors occurred. The error message is returned
in the 'errbuf' variable.
*/
int sock_close(SOCKET sock, char *errbuf, int errbuflen)
{
// SHUT_WR: subsequent calls to the send function are disallowed.
// For TCP sockets, a FIN will be sent after all data is sent and
// acknowledged by the Server.
if (shutdown(sock, SHUT_WR) )
{
sock_geterror("shutdown(): ", errbuf, errbuflen);
// close the socket anyway
closesocket(sock);
return -1;
}
closesocket(sock);
return 0;
}
/*!
\brief Checks that the address, port and flags given are valids and it returns an 'addrinfo' stucture.
This function basically calls the getaddrinfo() calls, and it performs a set of sanity checks
to control that everything is fine (e.g. a TCP socket cannot have a mcast address, and such).
If an error occurs, it writes the error message into 'errbuf'.
\param address: a pointer to a user-allocated buffer containing the network address to check.
It could be both a numeric - literal address, and it can be NULL or "" (useful in case of a server
socket which has to bind to all addresses).
\param port: a pointer to a user-allocated buffer containing the network port to use.
\param hints: an addrinfo variable (passed by reference) containing the flags needed to create the
addrinfo structure appropriately.
\param addrinfo: it represents the true returning value. This is a pointer to an addrinfo variable
(passed by reference), which will be allocated by this function and returned back to the caller.
This variable will be used in the next sockets calls.
\param errbuf: a pointer to an user-allocated buffer that will contain the complete
error message. This buffer has to be at least 'errbuflen' in length.
It can be NULL; in this case the error cannot be printed.
\param errbuflen: length of the buffer that will contains the error. The error message cannot be
larger than 'errbuflen - 1' because the last char is reserved for the string terminator.
\return '0' if everything is fine, '-1' if some errors occurred. The error message is returned
in the 'errbuf' variable. The addrinfo variable that has to be used in the following sockets calls is
returned into the addrinfo parameter.
\warning The 'addrinfo' variable has to be deleted by the programmer by calling freeaddrinfo() when
it is no longer needed.
\warning This function requires the 'hints' variable as parameter. The semantic of this variable is the same
of the one of the corresponding variable used into the standard getaddrinfo() socket function. We suggest
the programmer to look at that function in order to set the 'hints' variable appropriately.
*/
int sock_initaddress(const char *address, const char *port,
struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen)
{
int retval;
retval = getaddrinfo(address, port, hints, addrinfo);
if (retval != 0)
{
// if the getaddrinfo() fails, you have to use gai_strerror(), instead of using the standard
// error routines (errno) in UNIX; WIN32 suggests using the GetLastError() instead.
if (errbuf)
#ifdef WIN32
sock_geterror("getaddrinfo(): ", errbuf, errbuflen);
#else
if (errbuf)
{
snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval));
errbuf[errbuflen - 1]= 0;
}
#endif
return -1;
}
/*!
\warning SOCKET: I should check all the accept() in order to bind to all addresses in case
addrinfo has more han one pointers
*/
// This software only supports PF_INET and PF_INET6.
if (( (*addrinfo)->ai_family != PF_INET) && ( (*addrinfo)->ai_family != PF_INET6))
{
if (errbuf)
{
snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported");
errbuf[errbuflen - 1]= 0;
}
return -1;
}
if ( ( (*addrinfo)->ai_socktype == SOCK_STREAM) && (sock_ismcastaddr( (*addrinfo)->ai_addr) == 0) )
{
if (errbuf)
{
snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
errbuf[errbuflen - 1]= 0;
}
return -1;
}
return 0;
}
/*!
\brief It sends the amount of data contained into 'buffer' on the given socket.
This function basically calls the send() socket function and it checks that all
the data specified in 'buffer' (of size 'size') will be sent. If an error occurs,
it writes the error message into 'errbuf'.
In case the socket buffer does not have enough space, it loops until all data
has been sent.
\param socket: the connected socket currently opened.
\param buffer: a char pointer to a user-allocated buffer in which data is contained.
\param size: number of bytes that have to be sent.
\param errbuf: a pointer to an user-allocated buffer that will contain the complete
error message. This buffer has to be at least 'errbuflen' in length.
It can be NULL; in this case the error cannot be printed.
\param errbuflen: length of the buffer that will contains the error. The error message cannot be
larger than 'errbuflen - 1' because the last char is reserved for the string terminator.
\return '0' if everything is fine, '-1' if some errors occurred. The error message is returned
in the 'errbuf' variable.
*/
int sock_send(SOCKET socket, const char *buffer, int size, char *errbuf, int errbuflen)
{
int nsent;
send:
#ifdef linux
/*
Another pain... in Linux there's this flag
MSG_NOSIGNAL
Requests not to send SIGPIPE on errors on stream-oriented
sockets when the other end breaks the connection.
The EPIPE error is still returned.
*/
nsent = send(socket, buffer, size, MSG_NOSIGNAL);
#else
nsent = send(socket, buffer, size, 0);
#endif
if (nsent == -1)
{
sock_geterror("send(): ", errbuf, errbuflen);
return -1;
}
if (nsent != size)
{
size-= nsent;
buffer+= nsent;
goto send;
}
return 0;
}
/*!
\brief It copies the amount of data contained into 'buffer' into 'tempbuf'.
and it checks for buffer overflows.
This function basically copies 'size' bytes of data contained into 'buffer'
into 'tempbuf', starting at offset 'offset'. Before that, it checks that the
resulting buffer will not be larger than 'totsize'. Finally, it updates
the 'offset' variable in order to point to the first empty location of the buffer.
In case the function is called with 'checkonly' equal to 1, it does not copy
the data into the buffer. It only checks for buffer overflows and it updates the
'offset' variable. This mode can be useful when the buffer already contains the
data (maybe because the producer writes directly into the target buffer), so
only the buffer overflow check has to be made.
In this case, both 'buffer' and 'tempbuf' can be NULL values.
This function is useful in case the userland application does not know immediately
all the data it has to write into the socket. This function provides a way to create
the "stream" step by step, appendning the new data to the old one. Then, when all the
data has been bufferized, the application can call the sock_send() function.
\param buffer: a char pointer to a user-allocated buffer that keeps the data
that has to be copied.
\param size: number of bytes that have to be copied.
\param tempbuf: user-allocated buffer (of size 'totsize') in which data
has to be copied.
\param offset: an index into 'tempbuf' which keeps the location of its first
empty location.
\param totsize: total size of the buffer in which data is being copied.
\param checkonly: '1' if we do not want to copy data into the buffer and we
want just do a buffer ovreflow control, '0' if data has to be copied as well.
\param errbuf: a pointer to an user-allocated buffer that will contain the complete
error message. This buffer has to be at least 'errbuflen' in length.
It can be NULL; in this case the error cannot be printed.
\param errbuflen: length of the buffer that will contains the error. The error message cannot be
larger than 'errbuflen - 1' because the last char is reserved for the string terminator.
\return '0' if everything is fine, '-1' if some errors occurred. The error message
is returned in the 'errbuf' variable. When the function returns, 'tempbuf' will
have the new string appended, and 'offset' will keep the length of that buffer.
In case of 'checkonly == 1', data is not copied, but 'offset' is updated in any case.
\warning This function assumes that the buffer in which data has to be stored is
large 'totbuf' bytes.
\warning In case of 'checkonly', be carefully to call this function *before* copying
the data into the buffer. Otherwise, the control about the buffer overflow is useless.
*/
int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen)
{
if ((*offset + size) > totsize)
{
if (errbuf)
{
snprintf(errbuf, errbuflen, "Not enough space in the temporary send buffer.");
errbuf[errbuflen - 1]= 0;
}
return -1;
};
if (!checkonly)
memcpy(tempbuf + (*offset), buffer, size);
(*offset)+= size;
return 0;
}
/*!
\brief It waits on a connected socket and it manages to receive data.
This function basically calls the recv() socket function and it checks that no
error occurred. If that happens, it writes the error message into 'errbuf'.
This function changes its behaviour according to the 'receiveall' flag: if we
want to receive exactly 'size' byte, it loops on the recv() until all the requested
data is arrived. Otherwise, it returns the data currently available.
In case the socket does not have enough data available, it cycles on the recv()
util the requested data (of size 'size') is arrived.
In this case, it blocks until the number of bytes read is equal to 'size'.
\param sock: the connected socket currently opened.
\param buffer: a char pointer to a user-allocated buffer in which data has to be stored
\param size: size of the allocated buffer. WARNING: this indicates the number of bytes
that we are expecting to be read.
\param receiveall: if '0' (or SOCK_RECEIVEALL_NO), it returns as soon as some data
is ready; otherwise, (or SOCK_RECEIVEALL_YES) it waits until 'size' data has been
received (in case the socket does not have enough data available).
\param errbuf: a pointer to an user-allocated buffer that will contain the complete
error message. This buffer has to be at least 'errbuflen' in length.
It can be NULL; in this case the error cannot be printed.
\param errbuflen: length of the buffer that will contains the error. The error message cannot be
larger than 'errbuflen - 1' because the last char is reserved for the string terminator.
\return the number of bytes read if everything is fine, '-1' if some errors occurred.
The error message is returned in the 'errbuf' variable.
*/
int sock_recv(SOCKET sock, char *buffer, int size, int receiveall, char *errbuf, int errbuflen)
{
int nread;
int totread= 0;
// We can obtain the same result using the MSG_WAITALL flag
// However, this is not supported by recv() in Win32
if (size == 0)
{
SOCK_ASSERT("I have been requested to read zero bytes", 1);
return 0;
}
again:
nread= recv(sock, &(buffer[totread]), size - totread, 0);
if (nread == -1)
{
sock_geterror("recv(): ", errbuf, errbuflen);
return -1;
}
if (nread == 0)
{
if (errbuf)
{
snprintf(errbuf, errbuflen, "The other host terminated the connection.");
errbuf[errbuflen - 1]= 0;
}
return -1;
}
// If we want to return as soon as some data has been received,
// let's do the job
if (!receiveall)
return nread;
totread+= nread;
if (totread != size)
goto again;
return totread;
}
/*!
\brief It discards N bytes that are currently waiting to be read on the current socket.
This function is useful in case we receive a message we cannot undestand (e.g.
wrong version number when receiving a network packet), so that we have to discard all
data before reading a new message.
This function will read 'size' bytes from the socket and discard them.
It defines an internal buffer in which data will be copied; however, in case
this buffer is not large enough, it will cycle in order to read everything as well.
\param sock: the connected socket currently opened.
\param size: number of bytes that have to be discarded.
\param errbuf: a pointer to an user-allocated buffer that will contain the complete
error message. This buffer has to be at least 'errbuflen' in length.
It can be NULL; in this case the error cannot be printed.
\param errbuflen: length of the buffer that will contains the error. The error message cannot be
larger than 'errbuflen - 1' because the last char is reserved for the string terminator.
\return '0' if everything is fine, '-1' if some errors occurred.
The error message is returned in the 'errbuf' variable.
*/
int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen)
{
#define TEMP_BUF_SIZE 32768
char buffer[TEMP_BUF_SIZE]; // network buffer, to be used when the message is discarded
// A static allocation avoids the need of a 'malloc()' each time we want to discard a message
// Our feeling is that a buffer if 32KB is enough for most of the application;
// in case this is not enough, the "while" loop discards the message by calling the
// sockrecv() several times.
// We do not want to create a bigger variable because this causes the program to exit on
// some platforms (e.g. BSD)
while (size > TEMP_BUF_SIZE)
{
if (sock_recv(sock, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
return -1;
size-= TEMP_BUF_SIZE;
}
// If there is still data to be discarded
// In this case, the data can fit into the temporaty buffer
if (size)
{
if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
return -1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -