mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
introduce keepalive option and make common socket options function
Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
parent
da1fb0b89f
commit
a690cd066e
8 changed files with 124 additions and 28 deletions
|
@ -162,3 +162,25 @@ copy interesting headers by handling LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION
|
|||
callback, for clients there's a new callback just for this purpose
|
||||
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH.
|
||||
|
||||
|
||||
TCP Keepalive
|
||||
-------------
|
||||
|
||||
It is possible for a connection which is not being used to send to die
|
||||
silently somewhere between the peer and the side not sending. In this case
|
||||
by default TCP will just not report anything and you will never get any more
|
||||
incoming data or sign the link is dead until you try to send.
|
||||
|
||||
To deal with getting a notification of that situation, you can choose to
|
||||
enable TCP keepalives on all libwebsockets sockets, when you create the
|
||||
context.
|
||||
|
||||
To enable keepalive, set the ka_time member of the context creation parameter
|
||||
struct to a nonzero value (in seconds) at context creation time. You should
|
||||
also fill ka_probes and ka_interval in that case.
|
||||
|
||||
With keepalive enabled, the TCP layer will send control packets that should
|
||||
stimulate a response from the peer without affecting link traffic. If the
|
||||
response is not coming, the socket will announce an error at poll() forcing
|
||||
a close.
|
||||
|
||||
|
|
|
@ -10,6 +10,14 @@ User api additions
|
|||
"1.1 9e7f737", representing the library version from configure.ac
|
||||
and the git HEAD hash the library was built from
|
||||
|
||||
- TCP Keepalive can now optionally be applied to all lws sockets, with
|
||||
controllable timeout, number of probes and probe interval. This
|
||||
enables detection of idle connections which are logically okay, but
|
||||
are in fact dead, due to network connectivity issues at the server,
|
||||
client, or any intermediary. By default it's not enabled, but you
|
||||
can enable it by setting a non-zero timeout (in seconds) at the new
|
||||
ka_time member at context creation time.
|
||||
|
||||
|
||||
User api changes
|
||||
----------------
|
||||
|
|
|
@ -10,10 +10,6 @@ struct libwebsocket *__libwebsocket_client_connect_2(
|
|||
int n;
|
||||
int plen = 0;
|
||||
char pkt[512];
|
||||
int opt = 1;
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
struct protoent *tcp_proto;
|
||||
#endif
|
||||
|
||||
lwsl_client("__libwebsocket_client_connect_2\n");
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
|
@ -41,7 +37,8 @@ struct libwebsocket *__libwebsocket_client_connect_2(
|
|||
* prepare the actual connection (to the proxy, if any)
|
||||
*/
|
||||
|
||||
lwsl_client("__libwebsocket_client_connect_2: address %s", wsi->c_address);
|
||||
lwsl_client("__libwebsocket_client_connect_2: address %s\n",
|
||||
wsi->c_address);
|
||||
|
||||
server_hostent = gethostbyname(wsi->c_address);
|
||||
if (server_hostent == NULL) {
|
||||
|
@ -61,16 +58,6 @@ struct libwebsocket *__libwebsocket_client_connect_2(
|
|||
server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
|
||||
bzero(&server_addr.sin_zero, 8);
|
||||
|
||||
/* Disable Nagle */
|
||||
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
|
||||
setsockopt(wsi->sock, SOL_TCP, TCP_NODELAY,
|
||||
(const void *)&opt, sizeof(opt));
|
||||
#else
|
||||
tcp_proto = getprotobyname("TCP");
|
||||
setsockopt(wsi->sock, tcp_proto->p_proto, TCP_NODELAY,
|
||||
&opt, sizeof(opt));
|
||||
#endif
|
||||
|
||||
if (connect(wsi->sock, (struct sockaddr *)&server_addr,
|
||||
sizeof(struct sockaddr)) == -1) {
|
||||
lwsl_debug("Connect failed\n");
|
||||
|
@ -80,6 +67,12 @@ struct libwebsocket *__libwebsocket_client_connect_2(
|
|||
|
||||
lwsl_client("connected\n");
|
||||
|
||||
if (lws_set_socket_options(context, wsi->sock)) {
|
||||
lwsl_err("Failed to set wsi socket options\n");
|
||||
close(wsi->sock);
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
insert_wsi_socket_into_fds(context, wsi);
|
||||
|
||||
/* we are connected to server, or proxy */
|
||||
|
|
|
@ -519,6 +519,60 @@ int libwebsockets_get_random(struct libwebsocket_context *context,
|
|||
return n;
|
||||
}
|
||||
|
||||
int lws_set_socket_options(struct libwebsocket_context *context, int fd)
|
||||
{
|
||||
int optval = 1;
|
||||
socklen_t optlen = sizeof(optval);
|
||||
#ifdef WIN32
|
||||
unsigned long optl = 0;
|
||||
#endif
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
struct protoent *tcp_proto;
|
||||
#endif
|
||||
|
||||
if (context->ka_time) {
|
||||
/* enable keepalive on this socket */
|
||||
optval = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
|
||||
/* set the keepalive conditions we want on it too */
|
||||
optval = context->ka_time;
|
||||
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
|
||||
optval = context->ka_probes;
|
||||
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
|
||||
optval = context->ka_interval;
|
||||
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Disable Nagle */
|
||||
optval = 1;
|
||||
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
|
||||
setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
|
||||
#else
|
||||
tcp_proto = getprotobyname("TCP");
|
||||
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
|
||||
#endif
|
||||
|
||||
/* We are nonblocking... */
|
||||
#ifdef WIN32
|
||||
ioctlsocket(fd, FIONBIO, &optl);
|
||||
#else
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lws_send_pipe_choked(struct libwebsocket *wsi)
|
||||
{
|
||||
struct pollfd fds;
|
||||
|
|
|
@ -742,6 +742,13 @@ struct libwebsocket_extension {
|
|||
* @options: 0, or LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK
|
||||
* @user: optional user pointer that can be recovered via the context
|
||||
* pointer using libwebsocket_context_user
|
||||
* @ka_time: 0 for no keepalive, otherwise apply this keepalive timeout to
|
||||
* all libwebsocket sockets, client or server
|
||||
* @ka_probes: if ka_time was nonzero, after the timeout expires how many
|
||||
* times to try to get a response from the peer before giving up
|
||||
* and killing the connection
|
||||
* @ka_interval: if ka_time was nonzero, how long to wait before each ka_probes
|
||||
* attempt
|
||||
*/
|
||||
|
||||
struct lws_context_creation_info {
|
||||
|
@ -756,6 +763,10 @@ struct lws_context_creation_info {
|
|||
int uid;
|
||||
unsigned int options;
|
||||
void *user;
|
||||
int ka_time;
|
||||
int ka_probes;
|
||||
int ka_interval;
|
||||
|
||||
};
|
||||
|
||||
LWS_EXTERN
|
||||
|
|
|
@ -265,6 +265,10 @@ struct libwebsocket_context {
|
|||
int listen_service_fd;
|
||||
int listen_service_extraseen;
|
||||
|
||||
int ka_time;
|
||||
int ka_probes;
|
||||
int ka_interval;
|
||||
|
||||
#ifdef LWS_LATENCY
|
||||
unsigned long worst_latency;
|
||||
char worst_latency_info[256];
|
||||
|
@ -489,6 +493,9 @@ user_callback_handle_rxflow(callback_function, struct libwebsocket_context * con
|
|||
enum libwebsocket_callback_reasons reason, void *user,
|
||||
void *in, size_t len);
|
||||
|
||||
extern int
|
||||
lws_set_socket_options(struct libwebsocket_context *context, int fd);
|
||||
|
||||
#ifndef LWS_OPENSSL_SUPPORT
|
||||
|
||||
unsigned char *
|
||||
|
|
14
lib/server.c
14
lib/server.c
|
@ -133,7 +133,6 @@ int lws_server_socket_service(struct libwebsocket_context *context,
|
|||
unsigned int clilen;
|
||||
struct sockaddr_in cli_addr;
|
||||
int n;
|
||||
int opt = 1;
|
||||
ssize_t len;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
int m;
|
||||
|
@ -217,18 +216,7 @@ int lws_server_socket_service(struct libwebsocket_context *context,
|
|||
break;
|
||||
}
|
||||
|
||||
/* Disable Nagle */
|
||||
opt = 1;
|
||||
setsockopt(accept_fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
(const void *)&opt, sizeof(opt));
|
||||
|
||||
/* We are nonblocking... */
|
||||
#ifdef WIN32
|
||||
opt = 0;
|
||||
ioctlsocket(accept_fd, FIONBIO, (unsigned long *)&opt );
|
||||
#else
|
||||
fcntl(accept_fd, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
lws_set_socket_options(context, accept_fd);
|
||||
|
||||
/*
|
||||
* look at who we connected to and give user code a chance
|
||||
|
|
|
@ -968,6 +968,9 @@ all sessions, etc, if it wants
|
|||
<i>int</i> <b>uid</b>;<br>
|
||||
<i>unsigned int</i> <b>options</b>;<br>
|
||||
<i>void *</i> <b>user</b>;<br>
|
||||
<i>int</i> <b>ka_time</b>;<br>
|
||||
<i>int</i> <b>ka_probes</b>;<br>
|
||||
<i>int</i> <b>ka_interval</b>;<br>
|
||||
};<br>
|
||||
<h3>Members</h3>
|
||||
<dl>
|
||||
|
@ -1005,5 +1008,15 @@ else ignored
|
|||
<dt><b>user</b>
|
||||
<dd>optional user pointer that can be recovered via the context
|
||||
pointer using libwebsocket_context_user
|
||||
<dt><b>ka_time</b>
|
||||
<dd>0 for no keepalive, otherwise apply this keepalive timeout to
|
||||
all libwebsocket sockets, client or server
|
||||
<dt><b>ka_probes</b>
|
||||
<dd>if ka_time was nonzero, after the timeout expires how many
|
||||
times to try to get a response from the peer before giving up
|
||||
and killing the connection
|
||||
<dt><b>ka_interval</b>
|
||||
<dd>if ka_time was nonzero, how long to wait before each ka_probes
|
||||
attempt
|
||||
</dl>
|
||||
<hr>
|
||||
|
|
Loading…
Add table
Reference in a new issue