Trying to implement an asynchronous client based in mosquitto MQTT broker, running forever in the background. My purpose is create a client able to connect / reconnect to broker in case the broker becomes offline at some time. I expected all connection / reconnection logic was managed by mosquitto callbacks (event-driven). However, this code doesn't try to connect to broker in case you try to run the client when the broker is stopped (offline) at initial time:
struct mosquitto *broker;
char ip[INET_ADDRSTRLEN]; // broker ip address
int port; // 1883
int keepalive; // 60
bool running = true;
int main(void)
{
mosquitto_lib_init();
broker = mosquitto_new(NULL, true, NULL);
mosquitto_connect_callback_set(broker, on_connect_callback);
mosquitto_disconnect_callback_set(broker, on_disconnect_callback);
mosquitto_message_callback_set(broker, on_message_callback);
mosquitto_connect_async(broker, ip, port, keepalive);
mosquitto_loop_start(broker);
while(running) {
pause();
}
}
After some testing, replaced
mosquitto_connect_async(broker, ip, port, keepalive);
by a while loop polling for the first success returned by mosquitto_connect_async function:
bool connected_to_broker = false;
while (!connected_to_broker) {
rc = mosquitto_connect_async(broker, ip, port, keepalive);
if (rc == MOSQ_ERR_SUCCESS) {
connected_to_broker = true;
} else {
sleep(retry_timeout);
}
}
After the while loop, it seems all the source code can be based on mosquitto callbacks.
Is this the expected way to manage this situation? Or should be managed in a different way? Thanks!
You don't have to make multiple connection attempts. Your problem is that the first attempt fails for the same reason but does not yet fire any callbacks because the communication thread is not yet started.
If you check the return value of mosquitto_connect_async you will see it is MOSQ_ERR_ERRNO with errno being set to ECONNREFUSED (111).
If mosquitto_loop_start is called before your first connection attempt, the disconnect callback should be fired - also with rc == MOSQ_ERR_ERRNO and errno == ECONNREFUSED
Related
When a client ab ends the reactor seems to indefinitely go into a polling state resulting in roughly 15% of the processor being used. If the client reconnects I'm still losing that 15%, I'm trying to determine what is lacking in my code to handle this properly.
When the client ab ends _socket.available() immediately returns false so in the else block I'm attempting to do the right thing. Doing the same thing I do when a client terminates normally, 'delete this' eliminates the processor issue but the next time a client connects I get an allocation error, I'd like to understand why that is, what's the difference? Just putting a sleep in there solves everything but onSocketReadable continues to be called with _socket.available() == false, so it remains as a sort of orphaned active reactor, what am I missing? I also tried stopping the reactor, that stops the processor use but the restarted client will no longer connect, there's something I don't understand there also, seems like a new reactor would be created just as it was initially?
void onSocketReadable(const AutoPtr<ReadableNotification>& pNf)
{
// some socket implementations (windows) report available
// bytes on client disconnect, so we double-check here
if (_socket.available())
{
// No FIFO for now
//int len = _socket.receiveBytes(_fifoIn);
char* buffer = new char[65535];
memset(buffer, 0, 65535);
_socket.setReceiveBufferSize(65535);
int n = _socket.receiveBytes(buffer, 65535);
std::string json = buffer;
delete [] buffer;
if (json == "SHUTDOWN\r\n")
{
delete this;
return;
}
try
{
std::string result = _processor.process(json,_sm);
result.append("\r\n");
_socket.sendBytes(result.data(), (int)result.length());
}
catch (Poco::Exception& e)
{
std::cout << e.message();
}
}
else
{
// delete this;
// return;
// _reactor.stop();
Sleep(10);
}
}
I made this Source code to implement lwip tcp server
If you try to remove the st-link wire and do it without debugging, the server itself doesn't work.
I try to find a way, but I don't know where the problem is.
If the LED on the board blinks, it means it's down.
Is there a problem with why the server is not working?
If you turn it to debugging, you receive it without any problems, generate a message, and then send it.
Then isn't there a problem with the Source code?
If the connection itself is not working when there is no debugging, I don't think the server is created
But if it's a circuit problem, I think it's weird to be a server when you debug it
I don't know where to look.
When debugging, the client sent and received properly when it connected to the client from hercules or Raspberry Pi
void Tcp_Task(void const * argument)
{
/* USER CODE BEGIN Tcp_Task */
struct netconn *conn, *newconn;
err_t err, accept_err;
struct netbuf *buf;
void *data;
u16_t len;
MX_LWIP_Init();
LWIP_UNUSED_ARG(argument);
conn = netconn_new(NETCONN_TCP);
if (conn!=NULL)
{
// netconn_bind(conn, NULL, port 번호)
err = netconn_bind(conn, NULL, 5001);
if (err == ERR_OK)
{
netconn_listen(conn);
while (1)
{
accept_err = netconn_accept(conn, &newconn);
if (accept_err == ERR_OK)
{
while (netconn_recv(newconn, &buf) == ERR_OK)
{
do
{
netbuf_data(buf, &data, &len);
memcpy(receivemsg, data, len);
transmitmsg = procPacket();
msg_len = getsendPackSize();
netconn_write(newconn, transmitmsg, msg_len, NETCONN_COPY);
}
while (netbuf_next(buf) >= 0);
netbuf_delete(buf);
}
netconn_close(newconn);
netconn_delete(newconn);
}
osDelay(100);
}
}
else
{
netconn_delete(newconn);
}
}
/* Infinite loop */
for(;;)
{
osDelay(100);
}
/* USER CODE END Tcp_Task */
}
lwip tcp server Source code site
[https://blog.naver.com/eziya76/221867311729](lwip tcp server)
I don't think I can implement the server if I turn the board off and on or without debugging, but I don't know where to find the problem
Please let me know if the Source code is the problem, and if the other place is the problem, please tell me how to find it
I'm writing this using a translator, so please understand
I'm programming my ESP32 with the ArduinoIDE and have a problem with HTTP GET. What I'm doing:
the ESP32 connects as WiFi client to an existing WiFi network using a static, fixed IP
a webserver is started which provides a webpage for OTA firmware update -> this works, the webpage is accessible via the static IP
using HttpClient I try to GET an other, remote webserver, but this fails
This is the code I'm using for the HTTP GET call:
static WiFiClient wifi;
HttpClient wlanHttp=HttpClient(wifi,"my.server.tld");
wlanHttp.get("/setpos.php?id=DEADBEEF"); // -> this fails with error code -1
wlanHttp.responseStatusCode(); // follow-up error -1
wlanHttp.stop();
Any idea what goes wrong here?
The confusing part here is the ESP32 has a built in http client called HTTPClient. The one for Arduino is called HttpClient and I'd like to find the guy who decided on that name and see if he's okay. HTTPClient has a routine called getString() that is a lovely way to gather info from a json api call, but HttpClient won't compile with that because it has no clue what that is.
On ESp32 (if using the HTTPClient.h) the code should look like that:
static WiFiClient wifi;
HttpClient wlanHttp;
wlanHttp.begin("http://my.server.tld/setpos.php?id=DEADBEEF"); //Specify the URL
int httpCode = wlanHttp.GET(); //Make the request
if (httpCode > 0) { //Check for the returning code
if (httpCode == HTTP_CODE_OK) {
// get payload with http.getString();
Serial.println(httpCode);
// Serial.println(payload);
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", wlanHttp.errorToString(httpCode).c_str());
}
} else {
Serial.println("Error on HTTP request");
}
wlanHttp.end(); //Free the resources
I am trying to implement http connection pooling in my java code and when I try to use it I get a Handshake exception. If I take out that one line that sets the connection manager it works. This makes no sense to me. I am using these jar files:
httpclient-4.5.2.jar
httpcore-4.4.4.jar
With connection pooling in place:
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(10000)
.setConnectionRequestTimeout(10000)
.setSocketTimeout(5000)
.build();
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(readStore(), KEYSTOREPASS)
.build();
HttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
httpClient = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.setDefaultRequestConfig(requestConfig)
.setSSLContext(sslContext)
.build();
Throws Received fatal alert: handshake_failure exception:
main, RECV TLSv1.2 ALERT: fatal, handshake_failure
%% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
%% Invalidated: [Session-2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
main, called close()
main, called closeInternal(true)
00:22:51,523 ERROR TestHttps:155 - Received fatal alert: handshake_failure
with connection pooling commented out:
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(10000)
.setConnectionRequestTimeout(10000)
.setSocketTimeout(5000)
.build();
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(readStore(), KEYSTOREPASS) // use null as second param if you don't have a separate key password
.build();
HttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
httpClient = HttpClients.custom()
//.setConnectionManager(poolingConnManager)
.setDefaultRequestConfig(requestConfig)
.setSSLContext(sslContext)
.build();
it works successfully and returns my value (obfuscated here):
main, READ: TLSv1.2 Application Data, length = 40
Padded plaintext after DECRYPTION: len = 16
0000: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx 1234567890
main, called close()
main, called closeInternal(true)
main, SEND TLSv1.2 ALERT: warning, description = close_notify
Padded plaintext before ENCRYPTION: len = 2
0000: 01 00 ..
main, WRITE: TLSv1.2 Alert, length = 26
[Raw write]: length = 31
0000: 15 03 03 00 1A 00 00 00 00 00 00 00 01 2C 7D 6E .............,.n
0010: 66 04 BA 1D FF 4A EB 54 0F 60 C7 A4 41 4A 68 f....J.T.`..AJh
main, called closeSocket(true)
What am I doing wrong? Thanks
I found this doc for an standard implementation of httpconnection manager
**
*This connection manager implementation should be used inside an EJB container. May be because of this and you using this in main method **
HTTP connection managers
2.3.1. Managed connections and connection managers
HTTP connections are complex, stateful, thread-unsafe objects which need to be properly managed to function correctly. HTTP connections can only be used by one execution thread at a time. HttpClient employs a special entity to manage access to HTTP connections called HTTP connection manager and represented by the HttpClientConnectionManager interface. The purpose of an HTTP connection manager is to serve as a factory for new HTTP connections, to manage life cycle of persistent connections and to synchronize access to persistent connections making sure that only one thread can have access to a connection at a time. Internally HTTP connection managers work with instances of ManagedHttpClientConnection acting as a proxy for a real connection that manages connection state and controls execution of I/O operations. If a managed connection is released or get explicitly closed by its consumer the underlying connection gets detached from its proxy and is returned back to the manager. Even though the service consumer still holds a reference to the proxy instance, it is no longer able to execute any I/O operations or change the state of the real connection either intentionally or unintentionally.
This is an example of acquiring a connection from a connection manager:
HttpClientContext context = HttpClientContext.create();
HttpClientConnectionManager connMrg = new BasicHttpClientConnectionManager();
HttpRoute route = new HttpRoute(new HttpHost("localhost", 80));
// Request new connection. This can be a long process
ConnectionRequest connRequest = connMrg.requestConnection(route, null);
// Wait for connection up to 10 sec
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS);
try {
// If not open
if (!conn.isOpen()) {
// establish connection based on its route info
connMrg.connect(conn, route, 1000, context);
// and mark it as route complete
connMrg.routeComplete(conn, route, context);
}
// Do useful things with the connection.
} finally {
connMrg.releaseConnection(conn, null, 1, TimeUnit.MINUTES);
}
The connection request can be terminated prematurely by calling ConnectionRequest#cancel() if necessary. This will unblock the thread blocked in the ConnectionRequest#get() method.
2.3.2. Simple connection manager
BasicHttpClientConnectionManager is a simple connection manager that maintains only one connection at a time. Even though this class is thread-safe it ought to be used by one execution thread only. BasicHttpClientConnectionManager will make an effort to reuse the connection for subsequent requests with the same route. It will, however, close the existing connection and re-open it for the given route, if the route of the persistent connection does not match that of the connection request. If the connection has been already been allocated, then java.lang.IllegalStateException is thrown.
**
**
May be you are using this in main method because of this it is
creating issue.
**
*This connection manager implementation should be used inside an EJB container.**
*
2.3.3. Pooling connection manager
PoolingHttpClientConnectionManager is a more complex implementation that manages a pool of client connections and is able to service connection requests from multiple execution threads. Connections are pooled on a per route basis. A request for a route for which the manager already has a persistent connection available in the pool will be serviced by leasing a connection from the pool rather than creating a brand new connection.
PoolingHttpClientConnectionManager maintains a maximum limit of connections on a per route basis and in total. Per default this implementation will create no more than 2 concurrent connections per given route and no more 20 connections in total. For many real-world applications these limits may prove too constraining, especially if they use HTTP as a transport protocol for their services.
This example shows how the connection pool parameters can be adjusted:
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// Increase max total connection to 200
cm.setMaxTotal(200);
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(20);
// Increase max connections for localhost:80 to 50
HttpHost localhost = new HttpHost("locahost", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
2.3.4. Connection manager shutdown
When an HttpClient instance is no longer needed and is about to go out of scope it is important to shut down its connection manager to ensure that all connections kept alive by the manager get closed and system resources allocated by those connections are released.
CloseableHttpClient httpClient = <...>
httpClient.close();
2.4. Multithreaded request execution
When equipped with a pooling connection manager such as PoolingClientConnectionManager, HttpClient can be used to execute multiple requests simultaneously using multiple threads of execution.
The PoolingClientConnectionManager will allocate connections based on its configuration. If all connections for a given route have already been leased, a request for a connection will block until a connection is released back to the pool. One can ensure the connection manager does not block indefinitely in the connection request operation by setting 'http.conn-manager.timeout' to a positive value. If the connection request cannot be serviced within the given time period ConnectionPoolTimeoutException will be thrown.
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
// URIs to perform GETs on
String[] urisToGet = {
"http://www.domain1.com/",
"http://www.domain2.com/",
"http://www.domain3.com/",
"http://www.domain4.com/"
};
// create a thread for each URI
GetThread[] threads = new GetThread[urisToGet.length];
for (int i = 0; i < threads.length; i++) {
HttpGet httpget = new HttpGet(urisToGet[i]);
threads[i] = new GetThread(httpClient, httpget);
}
// start the threads
for (int j = 0; j < threads.length; j++) {
threads[j].start();
}
// join the threads
for (int j = 0; j < threads.length; j++) {
threads[j].join();
}
While HttpClient instances are thread safe and can be shared between multiple threads of execution, it is highly recommended that each thread maintains its own dedicated instance of HttpContext .
static class GetThread extends Thread {
private final CloseableHttpClient httpClient;
private final HttpContext context;
private final HttpGet httpget;
public GetThread(CloseableHttpClient httpClient, HttpGet httpget) {
this.httpClient = httpClient;
this.context = HttpClientContext.create();
this.httpget = httpget;
}
#Override
public void run() {
try {
CloseableHttpResponse response = httpClient.execute(
httpget, context);
try {
HttpEntity entity = response.getEntity();
} finally {
response.close();
}
} catch (ClientProtocolException ex) {
// Handle protocol errors
} catch (IOException ex) {
// Handle I/O errors
}
}
}
2.5. Connection eviction policy
One of the major shortcomings of the classic blocking I/O model is that the network socket can react to I/O events only when blocked in an I/O operation. When a connection is released back to the manager, it can be kept alive however it is unable to monitor the status of the socket and react to any I/O events. If the connection gets closed on the server side, the client side connection is unable to detect the change in the connection state (and react appropriately by closing the socket on its end).
HttpClient tries to mitigate the problem by testing whether the connection is 'stale', that is no longer valid because it was closed on the server side, prior to using the connection for executing an HTTP request. The stale connection check is not 100% reliable. The only feasible solution that does not involve a one thread per socket model for idle connections is a dedicated monitor thread used to evict connections that are considered expired due to a long period of inactivity. The monitor thread can periodically call ClientConnectionManager#closeExpiredConnections() method to close all expired connections and evict closed connections from the pool. It can also optionally call ClientConnectionManager#closeIdleConnections() method to close all connections that have been idle over a given period of time.
public static class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
#Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
// Close expired connections
connMgr.closeExpiredConnections();
// Optionally, close connections
// that have been idle longer than 30 sec
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// terminate
}
}
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
2.6. Connection keep alive strategy
The HTTP specification does not specify how long a persistent connection may be and should be kept alive. Some HTTP servers use a non-standard Keep-Alive header to communicate to the client the period of time in seconds they intend to keep the connection alive on the server side. HttpClient makes use of this information if available. If the Keep-Alive header is not present in the response, HttpClient assumes the connection can be kept alive indefinitely. However, many HTTP servers in general use are configured to drop persistent connections after a certain period of inactivity in order to conserve system resources, quite often without informing the client. In case the default strategy turns out to be too optimistic, one may want to provide a custom keep-alive strategy.
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
HttpHost target = (HttpHost) context.getAttribute(
HttpClientContext.HTTP_TARGET_HOST);
if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
// Keep alive for 5 seconds only
return 5 * 1000;
} else {
// otherwise keep alive for 30 seconds
return 30 * 1000;
}
}
};
CloseableHttpClient client = HttpClients.custom()
.setKeepAliveStrategy(myStrategy)
.build();
2.7. Connection socket factories
HTTP connections make use of a java.net.Socket object internally to handle transmission of data across the wire. However they rely on the ConnectionSocketFactory interface to create, initialize and connect sockets. This enables the users of HttpClient to provide application specific socket initialization code at runtime. PlainConnectionSocketFactory is the default factory for creating and initializing plain (unencrypted) sockets.
The process of creating a socket and that of connecting it to a host are decoupled, so that the socket could be closed while being blocked in the connect operation.
HttpClientContext clientContext = HttpClientContext.create();
PlainConnectionSocketFactory sf = PlainConnectionSocketFactory.getSocketFactory();
Socket socket = sf.createSocket(clientContext);
int timeout = 1000; //ms
HttpHost target = new HttpHost("localhost");
InetSocketAddress remoteAddress = new InetSocketAddress(
InetAddress.getByAddress(new byte[] {127,0,0,1}), 80);
sf.connectSocket(timeout, socket, target, remoteAddress, null, clientContext);
2.7.1. Secure socket layering
LayeredConnectionSocketFactory is an extension of the ConnectionSocketFactory interface. Layered socket factories are capable of creating sockets layered over an existing plain socket. Socket layering is used primarily for creating secure sockets through proxies. HttpClient ships with SSLSocketFactory that implements SSL/TLS layering. Please note HttpClient does not use any custom encryption functionality. It is fully reliant on standard Java Cryptography (JCE) and Secure Sockets (JSEE) extensions.
2.7.2. Integration with connection manager
Custom connection socket factories can be associated with a particular protocol scheme as as HTTP or HTTPS and then used to create a custom connection manager.
ConnectionSocketFactory plainsf = <...>
LayeredConnectionSocketFactory sslsf = <...>
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", plainsf)
.register("https", sslsf)
.build();
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
HttpClients.custom()
.setConnectionManager(cm)
.build();
2.7.3. SSL/TLS customization
HttpClient makes use of SSLConnectionSocketFactory to create SSL connections. SSLConnectionSocketFactory allows for a high degree of customization. It can take an instance of javax.net.ssl.SSLContext as a parameter and use it to create custom configured SSL connections.
KeyStore myTrustStore = <...>
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(myTrustStore)
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
Customization of SSLConnectionSocketFactory implies a certain degree of familiarity with the concepts of the SSL/TLS protocol, a detailed explanation of which is out of scope for this document. Please refer to the Java™ Secure Socket Extension (JSSE) Reference Guide for a detailed description of javax.net.ssl.SSLContext and related tools.
Hostname verification
In addition to the trust verification and the client authentication performed on the SSL/TLS protocol level, HttpClient can optionally verify whether the target hostname matches the names stored inside the server's X.509 certificate, once the connection has been established. This verification can provide additional guarantees of authenticity of the server trust material. The javax.net.ssl.HostnameVerifier interface represents a strategy for hostname verification. HttpClient ships with two javax.net.ssl.HostnameVerifier implementations. Important: hostname verification should not be confused with SSL trust verification.
DefaultHostnameVerifier: The default implementation used by HttpClient is expected to be compliant with RFC 2818. The hostname must match any of alternative names specified by the certificate, or in case no alternative names are given the most specific CN of the certificate subject. A wildcard can occur in the CN, and in any of the subject-alts.
NoopHostnameVerifier: This hostname verifier essentially turns hostname verification off. It accepts any SSL session as valid and matching the target host.
Per default HttpClient uses the DefaultHostnameVerifier implementation. One can specify a different hostname verifier implementation if desired
SSLContext sslContext = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext,
NoopHostnameVerifier.INSTANCE);
As of version 4.4 HttpClient uses the public suffix list kindly maintained by Mozilla Foundation to make sure that wildcards in SSL certificates cannot be misused to apply to multiple domains with a common top-level domain. HttpClient ships with a copy of the list retrieved at the time of the release. The latest revision of the list can found at https://publicsuffix.org/list/. It is highly adviseable to make a local copy of the list and download the list no more than once per day from its original location.
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat"));
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
One can disable verification against the public suffic list by using null matcher.
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null);
HttpClient proxy configuration
Even though HttpClient is aware of complex routing schemes and proxy chaining, it supports only simple direct or one hop proxy connections out of the box.
The simplest way to tell HttpClient to connect to the target host via a proxy is by setting the default proxy parameter:
HttpHost proxy = new HttpHost("someproxy", 8080);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
One can also instruct HttpClient to use the standard JRE proxy selector to obtain proxy information:
SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(
ProxySelector.getDefault());
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
Alternatively, one can provide a custom RoutePlanner implementation in order to have a complete control over the process of HTTP route computation:
HttpRoutePlanner routePlanner = new HttpRoutePlanner() {
public HttpRoute determineRoute(
HttpHost target,
HttpRequest request,
HttpContext context) throws HttpException {
return new HttpRoute(target, null, new HttpHost("someproxy", 8080),
"https".equalsIgnoreCase(target.getSchemeName()));
}
};
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
}
}
I'm having troubles setting the no_delay option on an asio socket. The following code runs well, except for the delay. My server receives the messages only after the 5000 ms expire.
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using namespace boost::asio;
struct Client
{
io_service svc;
ip::tcp::socket sock;
Client() : svc(), sock(svc)
{
ip::tcp::resolver resolver(svc);
ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query("127.0.0.1", "32323"));
connect(sock, endpoint);
}
void send(std::string const& message) {
sock.send(buffer(message));
}
};
int main()
{
Client client;
client.send("hello world\n");
client.send("bye world\n");
boost::this_thread::sleep_for(boost::chrono::milliseconds(5000));
}
When trying to add a delay I have a few options:
1) Add the option before connection:
Client() : svc(), sock(svc)
{
ip::tcp::resolver resolver(svc);
ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query("127.0.0.1", "32323"));
sock.set_option(ip::tcp::no_delay(true));
connect(sock, endpoint);
}
However this throws set_option: Bad file descriptor
2) Add the option after the connection:
Client() : svc(), sock(svc)
{
ip::tcp::resolver resolver(svc);
ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query("127.0.0.1", "32323"));
connect(sock, endpoint);
sock.set_option(ip::tcp::no_delay(true));
}
However in this case, the option has no effect and I still see the delay. According to boost::asio with no_delay not possible? , I need to set the option after I've opened the socket but before I've connected the socket. So I've tried this:
Client() : svc(), sock(svc)
{
ip::tcp::endpoint endpoint( ip::address::from_string("127.0.0.1"), 32323);
sock.open(ip::tcp::v4());
sock.set_option(ip::tcp::no_delay(true));
sock.connect(endpoint);
}
However, I still see no effect. How can I set this option?
Edit: It's possible that I am not setting the option correctly on the server-side. This is the complete server code:
#include <boost/asio.hpp>
#include <iostream>
int main() {
boost::asio::io_service io_service;
boost::asio::ip::tcp::acceptor acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 32323));
boost::asio::ip::tcp::socket socket(io_service);
acceptor.accept(socket);
socket.set_option(boost::asio::ip::tcp::no_delay(true));
boost::asio::streambuf sb;
boost::system::error_code ec;
while (boost::asio::read(socket, sb, ec)) {
std::cout << "received:\n" << &sb;
}
}
The client is properly setting the ip::tcp::no_delay option. However, the delay being observed is not the result of this option. Instead, it is the result of the server attempting to read more data than the client has sent, and when the client exits after sleeping 5000ms, the server's read operation completes with an error.
The read() operation initiated by the server will complete when either it has read streambuf.max_size() bytes or an error occurs. The streambuf's max size defaults to std::numeric_limits<std::size_t>::max() and can be configured in its constructor. In this case, the server attempts to read std::numeric_limits<std::size_t>::max() bytes, but the client only sends 22 bytes, sleeps 5000ms, then closes the socket. When the server observes that connection has closed, the read() operation completes with 22 bytes read and an error code of boost::asio::error::eof.