Java Sockets over the Internet: ConnectException (operation timed out) - java

I'm trying to open a socket to a website over the Internet, but can't. After about a minute, a ConnectException is thrown saying that the operation timed out.
Socket clientSocket = new Socket(InetAddress.getByName("gmail.com"), 25);
My computer is connected to a router, which is connected to the Internet. My router is configured to direct all incoming port 25 data to port 2550 on my local machine (192.168.2.2). So, I thought maybe if I set the "local address" and "local port" parameters on the Socket constructor it might work...but this also gives me an "operation timed out" error.
Socket clientSocket = new Socket(InetAddress.getByName("gmail.com"), 25, InetAddress.getByName("192.168.2.2"), 2550);
I saw this SO question, but was wondering if anyone could shed any more light on the issue. Thanks.

You are trying to connect to port 25 on gmail.com, but that machine is not an email server. You must first look up the MX record for gmail.com, and then try to connect to one of the delivery servers mentioned in the returned MX record.
For example, with dig mx gmail.com on my machine, I get:
; <<>> DiG 9.3.6-P1-RedHat-9.3.6-16.P1.el5 <<>> mx gmail.com
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34063
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 4, ADDITIONAL: 4
;; QUESTION SECTION:
;gmail.com. IN MX
;; ANSWER SECTION:
gmail.com. 1697 IN MX 30 alt3.gmail-smtp-in.l.google.com.
gmail.com. 1697 IN MX 40 alt4.gmail-smtp-in.l.google.com.
gmail.com. 1697 IN MX 5 gmail-smtp-in.l.google.com.
gmail.com. 1697 IN MX 10 alt1.gmail-smtp-in.l.google.com.
gmail.com. 1697 IN MX 20 alt2.gmail-smtp-in.l.google.com.
;; AUTHORITY SECTION:
gmail.com. 266895 IN NS ns1.google.com.
gmail.com. 266895 IN NS ns2.google.com.
gmail.com. 266895 IN NS ns3.google.com.
gmail.com. 266895 IN NS ns4.google.com.
;; ADDITIONAL SECTION:
ns1.google.com. 262525 IN A 216.239.32.10
ns2.google.com. 262525 IN A 216.239.34.10
ns3.google.com. 262525 IN A 216.239.36.10
ns4.google.com. 262525 IN A 216.239.38.10
;; Query time: 65 msec
;; SERVER: 144.52.10.15#53(144.52.10.15)
;; WHEN: Tue Feb 28 14:30:04 2012
;; MSG SIZE rcvd: 295
So try connecting to port 25 on gmail-smtp-in.l.google.com (which is the server with the lowest MX priority number).

So you want to receive the mail messages sent from Google to your router? That's the only reason to forward incoming traffic from port 25 to 2550. If you want to do that then you're going to need to use a ServerSocket to receive the incoming connection on your 192.168.2.2 server. What you're having problems with is that Socket initiates a connection to a server. It's a client socket. A ServerSocket waits for incoming connections. Do a little research in how ServerSockets work, but if you really want to receive incoming connections you'll have to implement the SMTP protocol which is no small feat. There are SMTP libraries out there that you could reuse. Check the Apache James project which is like Servlets for Mail servers.
http://james.apache.org/

Related

EROR when try to reach twilio API from China environment (Remote host closed connection during handshake)

When I try to send SMS in the China AWS environment in logs I got an exception. What can cause this issue? Is Twilio API is nor reachable from the China region? When we run this in not China environments sending SMS process works correctly.
I try to send SMS to not Chinese user.(Ukraine for example)
020-11-20 13:06:36,597 ERROR (ID 9f95d1c6-f082-4729-8dc2-22f4f71860c0) (USER ) (SRC c.m.d.c.c.ejb) (REQ POST /carePartners/changeStatus) ConnectEJB.sms 'dihik' [server name: null, max mem (mb): 5813, total mem (mb): 5813, free mem (mb): 4781, used mem (mb): 1032, dihik, loaded account in 5, [ConnectEJB.sms], alarm UUID: null, +38093 118 6575, smsVendor=TwilioSMS, toPhoneNum=+38093 118 6575, submitted in 735, ERROR]: java.lang.RuntimeException: 'dihik' sending SMS: com.medtronic.diabetes.carelink.connect.sms.SMSException: Remote host closed connection during handshake; com.twilio.exception.ApiException Remote host closed connection during handshake
at com.medtronic.diabetes.carelink.connect.ejb.ConnectSMSBean.onMessageWorker(ConnectSMSBean.java:165)
at com.medtronic.diabetes.carelink.connect.ejb.AbstractConnectBean.executeWorkerContext(AbstractConnectBean.java:336)

udp socket receive buffer size

I am use Netty ( a java network framework) to server UDP request .
I found the
SO_RCVBUF
and
/proc/sys/net/core/rmem_default
and
/proc/net/udp
output confusing:
By the manual of socket( man 7 socket) It explains :
rmem_default
contains the default setting in bytes of the socket receive
buffer.
rmem_max
contains the maximum socket receive buffer size in bytes which
a user may set by using the SO_RCVBUF socket option.
and the /proc/net/udp show's the udp receive queue size :
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
60: 00000000:2F3C 00000000:0000 07 00000000:0003E094 00:00000000 00000000 500 0 2224362 2 ffff810066908cc0
I can get the SO_RCVBUF = 12904, but the rx_queue size is 254100(3E094), and rmem_default is 262142
my confuse is, why the SO_RCVBUF not equals to the rmem_default, and why the queue size not equals the SO_RCVBUF (but larger than it ) ?
It seams the ctx.channel().config().getOption(ChannelOption.SO_RCVBUF)'s value is half of /proc/sys/net/core/rmem_default and the real receive buffer size( queue size ) is also the value of /proc/sys/net/core/rmem_default
so why ctx.channel().config().getOption(ChannelOption.SO_RCVBUF) not equals to the system's config ?
The accepted answer is wrong. The correct alignment is:
tx_queue rx_queue
00000000:0003E094
So rx_queue is 0x0003E094 or 254100. This can be confirmed using netstat -unlp | grep pid , Recv-Q is second column. Example output:
cat /proc/net/udp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
8101: 41FD080A:06A5 00000000:0000 07 00000000:00005A00 00:00000000 00000000 1000 0 3892411 2 0000000000000000 0
netstat --udp -nl
Proto Recv-Q Send-Q Local Address Foreign Address State
udp 23040 0 10.8.253.65:1701 0.0.0.0:*
Here, 0x00005A00==23040
You seem to have misread the output. When you line up the columns properly, rx_queue is zero.

WebRTC java server trouble

I think I am very close to getting my Java server app to talk to a browser page via WebRTC, but I can't quite get it to work. I feel like I am missing something small, so I hope that someone here might have a suggestion.
I took a close look at the WebRTC examples - the Java unit test (org.webrtc.PeerConnectionTest), and the example Android app (trunk/talk/examples/android). Based on what I learned, I put together a java app that uses WebSockets for signalling and attempts to send a video stream to Chrome.
The problem is that there is no video in the browser, even though all of my code (both Javascript and Java) is executed in the order I expect it to, hitting all the correct logging statements. There is some suspicious output in the console log from the native libjingle code, but I am not sure what to make of it. I have highlighted suspicious lines in the log with '>>' below. For example, it seems that the video port allocators are destroyed shortly after being created, so something is obviously wrong. Also, "Changing video state, recv=1 send=0" seems incorrect as well, since the Java side should be sending the video, rather than receiving.... Perhaps I am misusing the OfferToReceiveVideo option?
If you look at the logs below, you'll see that the WebSocket communication with the browser is working perfectly, and that I am able to successfully send the SDP Offer to the browser and receive an SDP Answer from the browser. Setting the Local and Remote Descriptions on the PeerConnections seems to work properly as well. The HTML5 video element gets the source set to a BLOB url, as it should. So, what could I be missing? Do I need to do anything with ICE candidates, even though my client and server are on the same machine right now?
Any advice would be greatly appreciated!
SDP Messages (from Chrome's Javascript Console)
1.134: Java Offer:
v=0
o=- 5893945934600346864 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE video
a=msid-semantic: WMS JavaMediaStream
m=video 1 RTP/SAVPF 100 116 117
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:dJxTlMlXy7uASrDU
a=ice-pwd:r8BRkXVnc4dqCABUDhuRjpp7
a=ice-options:google-ice
a=mid:video
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=sendrecv
a=rtcp-mux
a=crypto:0 AES_CM_128_HMAC_SHA1_80 inline:yq6wOHhk/QfsWuh+1oOEqfB4GjKZzz8XfQnGCDP3
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 goog-remb
a=rtpmap:116 red/90000
a=rtpmap:117 ulpfec/90000
a=ssrc:3720473526 cname:nul6R21KmwAms3Ge
a=ssrc:3720473526 msid:JavaMediaStream JavaMediaStream_v0
a=ssrc:3720473526 mslabel:JavaMediaStream
a=ssrc:3720473526 label:JavaMediaStream_v0
1.149: Received remote stream
1.150: Browsers Answer:
v=0
o=- 4261396844048664099 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE video
a=msid-semantic: WMS
m=video 1 RTP/SAVPF 100 116 117
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:quzQNsX+ZlUWUQqV
a=ice-pwd:y5A0+7sM8P88AatBLd1fdd5G
a=mid:video
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=recvonly
a=rtcp-mux
a=crypto:0 AES_CM_128_HMAC_SHA1_80 inline:WClNA69OfpjdJy3Bv4ujejk/IYnn4DW8kjrB18xP
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 goog-remb
a=rtpmap:116 red/90000
a=rtpmap:117 ulpfec/90000
This seems ok to me. Java's offer includes my video stream.
Native code logging (libjingle)
(Suspicious lines marked with '>>')
Camera '/dev/video0' started with format YUY2 640x480x30, elapsed time 59 ms
Ignored line: c=IN IP4 0.0.0.0
NACK enabled for channel 0
NACK enabled for channel 0
Created channel for video
Jingle:Channel[video|1|__]: NULL DTLS identity supplied. Not doing DTLS
Jingle:Channel[video|2|__]: NULL DTLS identity supplied. Not doing DTLS
Session:5893945934600346864 Old state:STATE_INIT New state:STATE_SENTINITIATE Type:urn:xmpp:jingle:apps:rtp:1 Transport:http://www.google.com/transport/p2p
Setting local video description
AddSendStream {id:JavaMediaStream_v0;ssrcs:[3720473526];ssrc_groups:;cname:nul6R21KmwAms3Ge;sync_label:JavaMediaStream}
Add send ssrc: 3720473526
>> Warning(webrtcvideoengine.cc:2704): SetReceiverBufferingMode(0, 0) failed, err=12606
Changing video state, recv=0 send=0
Transport: video, allocating candidates
Transport: video, allocating candidates
Jingle:Net[eth0:192.168.0.0/24]: Allocation Phase=Udp
Jingle:Port[:1:0::Net[eth0:192.168.0.0/24]]: Port created
Adding allocated port for video
Jingle:Port[video:1:0::Net[eth0:192.168.0.0/24]]: Added port to allocator
Jingle:Net[tun0:192.168.128.6/32]: Allocation Phase=Udp
Jingle:Port[:1:0::Net[tun0:192.168.128.6/32]]: Port created
Adding allocated port for video
Jingle:Port[video:1:0::Net[tun0:192.168.128.6/32]]: Added port to allocator
Ignored line: c=IN IP4 0.0.0.0
Warning(webrtcvideoengine.cc:2309): GetStats: sender information not ready.
Jingle:Channel[video|1|__]: Other side didn't support DTLS.
Jingle:Channel[video|2|__]: Other side didn't support DTLS.
Enabling BUNDLE, bundling onto transport: video
Channel enabled
>> Changing video state, recv=1 send=0
Session:5893945934600346864 Old state:STATE_SENTINITIATE New state:STATE_RECEIVEDACCEPT Type:urn:xmpp:jingle:apps:rtp:1 Transport:http://www.google.com/transport/p2p
Setting remote video description
Hybrid NACK/FEC enabled for channel 0
Hybrid NACK/FEC enabled for channel 0
SetSendCodecs() : selected video codec VP8/1280x720x30fps#2000kbps (min=50kbps, start=300kbps)
Video max quantization: 56
VP8 number of temporal layers: 1
VP8 options : picture loss indication = 0, feedback mode = 0, complexity = normal, resilience = off, denoising = 0, error concealment = 0, automatic resize = 0, frame dropping = 1, key frame interval = 3000
WARNING: no real random source present!
SRTP activated with negotiated parameters: send cipher_suite AES_CM_128_HMAC_SHA1_80 recv cipher_suite AES_CM_128_HMAC_SHA1_80
Changing video state, recv=1 send=0
Session:5893945934600346864 Old state:STATE_RECEIVEDACCEPT New state:STATE_INPROGRESS Type:urn:xmpp:jingle:apps:rtp:1 Transport:http://www.google.com/transport/p2p
Jingle:Net[eth0:192.168.0.0/24]: Allocation Phase=Relay
Jingle:Net[tun0:192.168.128.6/32]: Allocation Phase=Relay
Jingle:Net[eth0:192.168.0.0/24]: Allocation Phase=Tcp
Jingle:Port[:1:0:local:Net[eth0:192.168.0.0/24]]: Port created
Adding allocated port for video
Jingle:Port[video:1:0:local:Net[eth0:192.168.0.0/24]]: Added port to allocator
Jingle:Net[tun0:192.168.128.6/32]: Allocation Phase=Tcp
Jingle:Port[:1:0:local:Net[tun0:192.168.128.6/32]]: Port created
Adding allocated port for video
Jingle:Port[video:1:0:local:Net[tun0:192.168.128.6/32]]: Added port to allocator
Jingle:Net[eth0:192.168.0.0/24]: Allocation Phase=SslTcp
Jingle:Net[tun0:192.168.128.6/32]: Allocation Phase=SslTcp
All candidates gathered for video:1:0
Transport: video, component 1 allocation complete
Transport: video allocation complete
Candidate gathering is complete.
Capture delay changed to 120 ms
Captured frame size 640x480. Expected format YUY2 640x480x30
Capture size changed : selected video codec VP8/640x480x30fps#2000kbps (min=50kbps, start=300kbps)
Video max quantization: 56
VP8 number of temporal layers: 1
VP8 options : picture loss indication = 0, feedback mode = 0, complexity = normal, resilience = off, denoising = 0, error concealment = 0, automatic resize = 1, frame dropping = 1, key frame interval = 3000
VAdapt Frame: 0 / 300 Changes: 0 Input: 640x480 Scale: 1 Output: 640x480 Changed: false
>> Jingle:Port[video:1:0::Net[eth0:192.168.0.0/24]]: Port deleted
>> Jingle:Port[video:1:0::Net[eth0:192.168.0.0/24]]: Removed port from allocator (3 remaining)
Removed port from p2p socket: 3 remaining
Jingle:Port[video:1:0::Net[tun0:192.168.128.6/32]]: Port deleted
Jingle:Port[video:1:0::Net[tun0:192.168.128.6/32]]: Removed port from allocator (2 remaining)
Removed port from p2p socket: 2 remaining
>> Jingle:Port[video:1:0:local:Net[eth0:192.168.0.0/24]]: Port deleted
>> Jingle:Port[video:1:0:local:Net[eth0:192.168.0.0/24]]: Removed port from allocator (1 remaining)
Removed port from p2p socket: 1 remaining
Jingle:Port[video:1:0:local:Net[tun0:192.168.128.6/32]]: Port deleted
Jingle:Port[video:1:0:local:Net[tun0:192.168.128.6/32]]: Removed port from allocator (0 remaining)
Removed port from p2p socket: 0 remaining
HTML
<html lang="en">
<head>
<title>Web Socket Signalling</title>
<link rel="stylesheet" href="css/socket.css">
<script src="js/socket.js"></script>
</head>
<body>
<h2>Repsonse from Server</h2>
<textarea id="responseText"></textarea>
<h2>Video</h2>
<video id="remoteVideo" autoplay></video>
</body>
</html>
Javascript
(function() {
var remotePeerConnection;
var sdpConstraints = {
'mandatory' : {
'OfferToReceiveAudio' : false,
'OfferToReceiveVideo' : true
}
};
var Sock = function() {
var socket;
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8080/websocket");
socket.onopen = onopen;
socket.onmessage = onmessage;
socket.onclose = onclose;
} else {
alert("Your browser does not support Web Socket.");
}
function onopen(event) {
getTextAreaElement().value = "Web Socket opened!";
}
function onmessage(event) {
appendTextArea(event.data);
sdpOffer = new RTCSessionDescription(JSON.parse(event.data));
remotePeerConnection = new webkitRTCPeerConnection(null);
remotePeerConnection.onaddstream = gotRemoteStream;
trace("Java Offer: \n" + sdpOffer.sdp);
remotePeerConnection.setRemoteDescription(sdpOffer);
remotePeerConnection.createAnswer(gotRemoteDescription, onCreateSessionDescriptionError, sdpConstraints);
}
function onCreateSessionDescriptionError(error) {
console.log('Failed to create session description: '
+ error.toString());
}
function gotRemoteDescription(answer) {
remotePeerConnection.setLocalDescription(answer);
trace("Browser's Answer: \n" + answer.sdp);
socket.send(JSON.stringify(answer));
}
function gotRemoteStream(event) {
var remoteVideo = document.getElementById("remoteVideo");
remoteVideo.src = URL.createObjectURL(event.stream);
trace("Received remote stream");
}
function onclose(event) {
appendTextArea("Web Socket closed");
}
function appendTextArea(newData) {
var el = getTextAreaElement();
el.value = el.value + '\n' + newData;
}
function getTextAreaElement() {
return document.getElementById('responseText');
}
function trace(text) {
console.log((performance.now() / 1000).toFixed(3) + ": " + text);
}
}
window.addEventListener('load', function() {
new Sock();
}, false);
})();
Java Server
public class PeerConnectionManager {
/**
* Called when the WebSocket handshake is completed
*/
public void createOffer() {
peerConnection = factory.createPeerConnection(
new ArrayList<PeerConnection.IceServer>(),
new MediaConstraints(),
new PeerConnectionObserverImpl());
// Get the video source
videoSource = factory.createVideoSource(VideoCapturer.create(""), new MediaConstraints());
// Create a MediaStream with one video track
MediaStream lMS = factory.createLocalMediaStream("JavaMediaStream");
VideoTrack videoTrack = factory.createVideoTrack("JavaMediaStream_v0", videoSource);
videoTrack.addRenderer(new VideoRenderer(new VideoRendererObserverImpl()));
lMS.addTrack(videoTrack);
peerConnection.addStream(lMS, new MediaConstraints());
// We don't want to receive anything
MediaConstraints sdpConstraints = new MediaConstraints();
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
"OfferToReceiveAudio", "false"));
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
"OfferToReceiveVideo", "false"));
// Get the Offer SDP
SdpObserverImpl sdpOfferObserver = new SdpObserverImpl();
peerConnection.createOffer(sdpOfferObserver, sdpConstraints);
SessionDescription offerSdp = sdpOfferObserver.getSdp();
// Set local SDP, don't care for any callbacks
peerConnection.setLocalDescription(new SdpObserverImpl(), offerSdp);
// Serialize Offer and send to the Browser via a WebSocket
JSONObject offerSdpJson = new JSONObject();
offerSdpJson.put("sdp", offerSdp.description);
offerSdpJson.put("type", offerSdp.type.canonicalForm());
webSocketContext.channel().writeAndFlush(
new TextWebSocketFrame(offerSdpJson.toString()));
}
/**
* Called when an SDP Answer arrives via the WebSocket
*/
public void setRemoteDescription(SessionDescription answer) {
peerConnection.setRemoteDescription( new SdpObserverImpl(), answer);
}
}
Ugh. Never mind. Sorry for the stupid question.
The missing part was the exchange of ICE candidates between the browser and Java server. Now that I added the code to do ICE negotiation via my WebSocket, everything works fine!

Socket Channel - readIntoNativeBuffer() Error

I have been experiencing this issue for a while now. A quick explanation of the system:
A simple application will read in data over a tcp connection. The application uses Socketchannel object to establish a data communication line. The connection to hardware is established, and the application processes roughly between 400 - 700 packets before throwing the error:
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:218)
at sun.nio.ch.IOUtil.read(IOUtil.java:186)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:359)
I suspect it has to do with the operating system (windows 7) buffer, although I have tried a few work arounds (increasing JVM heap size, and creating additional registry variables for TCP settings), but with no luck.
Inspecting packet data using wireshark, the following information can been seen just as the error occurs
[TCP Window Full] 5001 > 49995 [PSH, ACK] Seq=123177 Ack=1 Win=200 Len=176
[TCP ZeroWindow] 49995 > 5001 [ACK] Seq=1 Ack=123353 Win=0 Len=0
Which is then followed by a series of
[TCP Dup ACK 144#1] 5001 > 49995 [ACK] Seq=123353 Ack=1 Win=200 Len=0
until the connection is terminated.
I am hoping perhaps someone has had a similar issue in the past,or could offer some guidance on something I may be overlooking.
Here is sample code where data is read, and the error is thrown.
try{
do{
socketChannel.read(syncBuf);
start = syncBuf.get(0);
int b = start;
System.out.println("start byte is " + b);
syncBuf.clear();
}while(start != INIT_BYTE);
}catch(Exception e){
e.printStackTrace();
}
packetBuf.order(ByteOrder.LITTLE_ENDIAN);
packetBuf.clear();
try{
while(socketChannel.read(packetBuf) != 206){
nrBytesRead += socketChannel.read(packetBuf);
}
}catch(Exception e){
ApplicationLogger.log("Error at INIT_BYTE loop ", "ERROR");
}
Many thanks.
I think you have a problem here:
while(socketChannel.read(packetBuf) != 206)
{
nrBytesRead += socketChannel.read(packetBuf);
}
socketChannel.read(packetBuf) will return the number read this time, not the total, and I guess that's what 206 is, the total read.
You probably want to do that:
do
{
nrBytesRead += socketChannel.read(packetBuf);
}
while (nrBytesRead != 206)

Java SocketException Maximum number of datagram sockets reached

I have a java app program run on centos 6.3 and tomcat 7 as the app container, currently I meet one error : java.io.socketexception Maximum number of datagram sockets reached
we use MulticastSocket class to send message. when this error happened, I check the current server UDP socket count with command: ss -s
Total: 212 (kernel 248)
TCP: 70 (estab 15, closed 44, orphaned 0, synrecv 0, timewait 40/0), ports 22
Transport Total IP IPv6
* 248 - -
RAW 0 0 0
UDP 40 40 0
TCP 26 26 0
INET 66 66 0
FRAG 0 0 0
and I also check the
ulimits -n
The default setting is 32768, seem UDP socket count not exceed max count.
Any ideas for this error?
we use MulticastSocket class to send message.
Why? You only need a MulticastSocket to receive multicasts.
Obviously you are leaking MulticastSockets. Presumably you are creating a new one per message and never closing it.

Categories

Resources