What I'd like to achieve
bind a server to an ephemeral port for unit testing purposes.
My issue :
Using the 1.5.0_22 JDK I try to bind an InetSocketAddress on an ephemeral port using port 0 as per the javadoc but I can't find a way from the address object to know which port it has binded to, so I cannot have my clients configured accordingly:
InetSocketAddress address = new InetSocketAddress(0);
assertThat(address.isUnresolved(), is(false));
assertThat(address.getPort(), is(0));
I might not understand the javadoc sentence correctly :
A valid port value is between 0 and 65535. A port number of zero will
let the system pick up an ephemeral port in a bind operation.
But checking the port even after having my server listening to the socket (I'm assuming the binding had happened then) does not returns anything else but 0 (the following uses the http://simpleweb.sourceforge.net/ library) :
Container httpServer = new Container() {
public void handle(Request req, Response resp) {
}
};
SocketConnection connection = new SocketConnection(httpServer);
InetSocketAddress address = new InetSocketAddress(0);
connection.connect(address);
assertThat(address.isUnresolved(), is(false));
assertThat(address.getPort(), is(0));
Using nmap I don't even see a binded port so I'm assuming my understanding is incorrect. Any help?
The InetSocketAddress that initially contains port 0 is not updated by connect() to represent the actual port that was bound to. Call connection.getLocalPort() or ((InetSocketAddress)connection.getLocalSocketAddress()).getPort() instead to get the bound port.
Related
I have been testing the SCTP support on Java + lksctp.
I wrote a simple client in order to see just the inital setup of a SCTP association which is basically the "INIT" and "INIT ACK".
I have tested 2 ways for a Client to send the "INIT" to a SERVER which is basically:
create the SctpChannel object with "open(SocketAddress)"
try {
InetSocketAddress socketAddress = new InetSocketAddress("192.168.52.197", 2905);
SctpChannel sctpChannel = SctpChannel.open(socketAddress,1,1);
sctpChannel.bind(new InetSocketAddress("192.168.1.251",2906));
sctpChannel.connect(socketAddress, 1 ,1);
so in this way, I can see in Wireshark that I have the "IPv4 Address parameter" for all my network interfaces (3 as you can see bellow), but the Source Port is getting a aleatory port number instead the 2906 as I would like to have and it's in the bind.
So... once the bind of local IP/Port is happening after the "open"... so I have changed the code to:
create the SctpChannel object which just "open()"
binding the local client IP and Port
"connect" to the remote Server IP and Port
try {
InetSocketAddress socketAddress = new InetSocketAddress("192.168.52.197", 2905);
SctpChannel sctpChannel = SctpChannel.open();
sctpChannel.bind(new InetSocketAddress("192.168.1.251",2906));
sctpChannel.connect(socketAddress, 1 ,1);
In this way, I can see in wireshark that Source/Destination ports are expected (2906/2905), but the INIT does not have the "IPv4 Address parameter".
So does anyone know why the 2nd code I'm missing the "IPv4 address parameter" in the INIT ? Do I miss something?
Any help would be really welcome.
Thanks.
IP addresses within INIT/INIT_ACK chunks are optional parameters. In case your endpoints are signglehomed IP address might not be included in the INIT/INIT_ACK chunk. The remote end still can retrieve information about peer address from the IP header.
Fundamentally the reason of this behaviour is what parameters you pass to open(). Open() without any parameters and open() with remote address specified works in a different way.
If you call SctpChannel.open(socketAddress,1,1) with socket address for the remote end it effectively open channel and connects to remote end (see open documentation. Your bind() and connect() calls in this case are pretty useless. So since there were no bind() call prior to establishing the connection you are sort of using "default" endpoint with random port (56044) and IP addresses of all available interfaces.
In second case, when you don't specify socketAddress for open() it just open the channel but does not connect to remote end at this stage. So your bind() call successfully specify endpoint details (port and IP address) and when you call connect() it is actually using the endpoint you just created (192.168.1.251:2906) to setup connection with remote end.
I'm getting the above exception while trying to establish a server and create a client to another server using the netty framework.
I have 2 java files:
One is Server Acceptor meant to listen at a port say 9999
and another class file Client Connector to connect to another
server but bound with the port 9999.
Once both these files are executed I should be able to listen as a server in port 9999 and also send a message to remote server bound to same port 9999.
Since the remote server guy accepts and sends message to same port of my server.(9999 in this case)
My ServerAcceptor.class runs successfully and listens in port 9999. But my ClientConnector.class complains that the port is already bound.
Now how can i send and receive in the same port?
My Client code is as below.
ClientTest clientTest =new ClientTest();
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class).handler(new MyClientHandler(clientTest));
bootstrap.option(ChannelOption.TCP_NODELAY,true).option(ChannelOption.SO_REUSEADDR,true).option(ChannelOption.SO_KEEPALIVE, true);
System.out.println("Connecting to Server");
ChannelFuture channelFuture;
try {
channelFuture = bootstrap.connect(new InetSocketAddress("localhost", 8888),new InetSocketAddress("localhost", 9999)).sync();
channelFuture.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture channelFuture)
throws Exception {
System.out.println("Client");
if (channelFuture.isSuccess()) {
System.out.println("Client has connected successfully");
} else {
System.err.println("Client could not connect to the sever");
channelFuture.cause().printStackTrace();
}
}
} );
Once both these files are executed I should be able to listen as a server in port 9999 and also send a message to remote server bound to same port 9999.
No you shouldn't. The system will not permit it. It will only permit inbound connections to share the listening port. Your requirement, assuming you have interpreted it correctly, is infeasible.
Since the remote server guy accepts and sends message to same port of my server.(9999 in this case)
This doesn't begin to make sense. If the remote server accepts connections, he will communicate over those connections, whatever their source port may have been. He doesn't care. Possibly what is really happening I s that he also listens on port 9999. In any case none of this imposes a requirement for you to specify a local outbound port.
If I bind to a specific interface using a specific hostname and then use ServerSocketChannel.getLocalAddress() to retreive the bound address, the hostname is longer there.
Is this by design or just undefined behavior? Is there any way to fix that?
InetSocketAddress bindTo = new InetSocketAddress("my-hostname", 9999);
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(bindTo);
InetSocketAddress localAddr = (InetSocketAddress) serverSocketChannel.getLocalAddress();
System.out.println(bindTo);
System.out.println(localAddr);
> my-hostname/10.20.200.201:9999
> /10.20.200.201:9999
"my-hostname" in the above example is one of several hostnames that will resolve to the local IP. It is, however, not the hostname to which the IP resolves to when doing a reverse lookup.
The reason I ask is that I use a framework which will bind and then publish the service information to a central registry. However, it retrieves the bound address using .getLocalAddress() which ultimately ends up publishing the wrong hostname.
the hostname is longer there
You mean "the hostname is longer there" in the String returned by InetAddress.toString(). There's nothing that obliges that behaviour. The local address to which a socket is bound is primarily an IP address. If you want to map it to a hostname, that's up to you.
I don't find any information on this topic on the internet and asked here. For example I have server with IP 1.1.1.1 and 2.2.2.2 and two domain names pointing to it one.example.com and example2.net, and socke listening on port 1234 for incoming connections.
For example:
C/C++:
listenfd=socket(AF_INET, SOCK_STREAM, 0);
bind(...);
listen(...);
while(...) accept(...);
or Java:
ServerSocket socket = new ServerSocket(1234);
while(...) {
Socket connectionSocket = welcomeSocket.accept();
...
}
When client accepted on my socket I need to know what domain name/IP is used by the client to connect. It may be one.example.com or example2.net and/or IP 1.1.1.1 or 2.2.2.2 (if connected using IP only).
Apache somehow determined ip/domain of incoming reques, and I need to do such thing in pure socket code. C++ (main) or Java (or any other) accepted, I need to know mechaniics of this.
The IP is stored inside the IP packet header and you can read it from there. In order to get the host, you'll probably have to ask a DNS server by sending a request (or use a function which does it for you). You can find examples for both of the problems, even on this site
I have some code like this:
InetAddress bind = InetAddress.getByName("192.168.0.1")
MulticastSocket socket = new MulticastSocket(new InetSocketAddress(bind,0));
socket.setInterface(bind);
On windows 7 and windows XP with JDK6u17,I got a SocketException: Socket operation on non socket.
But if I change the line 2 to :
MulticastSocket socket = new MulticastSocket(0);
It's ok, and works find too with jdk6u14.
Why? thanks.
EDIT:
Why port 0 should be the matter?
MulticastSocket socket = new MulticastSocket(0);
Everything goes well with this code.But not
MulticastSocket socket = new MulticastSocket(new InetSocketAddress(bind,port));
Whatever the port is.
As you are binding to a specific interface, calling setInterface() to the same interface is redundant. Remove it. It's only needed when you bind to INADDR_ANY, or in Java an InetAddress of null (or unspecified as a parameter).
To address errors in some of the other answers, and their implications:
Port zero is legal. It means a system-assigned port.
You only need a MulticastSocket for receiving multicasts. For sending, you can just use a DatagramSocket.
If the multicast interface needs to be specified, which it doesn't in this case, it can be done either via MulticastSocket.setInterface() or when calling joinGroup() or leaveGroup(). The latter option gives you granularity at the group level, but both techniques work. That's why they're both provided.
If you don't bind to a specific interface you should definitely call setInterface(). If you are on a multi-homed host you must to call joinGroup()/leaveGroup() once per interface, if you want to receive via all of them.
And a question: is 192.168.0.1 an IP address of an NIC on the local machine? It needs to be.
According to the documentation, you are supposed to instantiate it with a port number (thus 0 would be valid).
I am not so sure.
What's the constructor MulticastSocket(SocketAddress bindaddr) for.
And why it works fine with jdk6u14,but not jdk6u17?
And why it ok on windows 2003 server with jdk6u17?
On RHEL5.2 jdk1.4+
http://www.sockets.com/err_lst1.htm
Berkeley description: An operation was attempted on something that is not a socket. The specified socket parameter refers to a file, not a socket.
WinSock description: Same as Berkeley. The socket input parameter is not a valid socket handle (either it never was valid, it's a file handle (not a socket handle), or if it was a socket handle, it has been closed).
Detailed description:
select(): fails with WSAENOTSOCK if any socket in an fd_set is an invalid socket handle.
Developer suggestions: Did you close a socket inadvertently in one part of an application without keeping another part notified? Use socket state in an application and/or handle this error gracefully as a non-fatal error.
when the MulticastSocket created,socket.isClosed()==true
I haven't used these classes before, but the Exception occurs on line 3 when you call the setInterface method.
I would guess it's something to the effect that you're using the same reference twice or something.
I found a snippet of code that looked like this, maybe this is how you should be doing it:
MulticastSocket ms = new MulticastSocket(new InetSocketAddress(0));
ms.setInterface(InetAddress.getByName("192.168.0.1"));
You should first create the Multicast socket with a well known port - something higher than 1024 and less than 65535 - as already stated 0 means the operating system will choose a port for you (but then its going to be kinda random - which I guess you don't want).
For multicast - you generally need to set the interface to use on joinGroup() not on creation - e.g:
MulticastSocket socket = new MulticastSocket(2121);
InetSocketAddress socketAddress = new InetSocketAddress("localhost", 2121);
if (networkInterfaceName != null){
NetworkInterface ni = NetworkInterface.getByName(networkInterfaceName);
socket.joinGroup(this.socketAddress, ni);
}else {
socket.joinGroup(socketAddress.getAddress());
}
According to the MulticastSocket documentation you should use
Class D IP addresses in the range
224.0.0.0 to 239.255.255.255, inclusive
to bind a MulticastSocket. Apparently, the "192.168.0.1" is out of the multicast range.