I am trying to encode/decode a network packet combining the sender IP address, destination IP address, sender port, destination port and payload data all together.
I've been reading the different ways to do this and everywhere suggests using the Socket class - however, we can't use real network classes like this in our program because it's just a simulated application with no real networking components.
I'm confused on how else I would create a network packet WITHOUT using classes like DatagramPacket or DatagramSocket. The payload data is in byte[] form and the IP addresses are InetAddress. I'm assuming I might have to use some kind of ByteArrayOutputStream to combine all these elements into one network packet of type byte[].
Any suggestions for how I could do this would be really appreciated.
Your use case is confusing. You are creating a networked application but you will never put your application on a network? That makes no sense to me.
In Java the SocketFactory creates Sockets (http://docs.oracle.com/javase/7/docs/api/javax/net/SocketFactory.html). You could implement your own SocketFactory to produce custom subclasses of Socket to create all the non-networky Socket things you need. Without an actual network layer, though, I have no idea how you would get these non-network Sockets to communicate so that you can test stuff like "did the server receive the packet I sent".
Further, I don't think you need an actual Network to do Socket stuff. localhost loopback serves this purpose.
I guess I'm just confused by your question.
i think you just want to create/edit a byte[] or read a byte[]
because a datagram-packet is nothing else than a byte[]!!!
you might think of methods like:
byte[] package; //it sould be at least 20 byte and conform to TCP/IP IPv4
public static byte[] getSourceIp(byte[] package){
byte[] sourceIp = new byte[4];
//System.arraycopy(package, 96, sourceIp, 4); //way shorter
for (int i = 0; i < 4; i ++){
sourceIp [i] = package[i+12]; //12=offset for source ip #ipv4
}
return sourceIp;
}
public static void setSourceIp(byte[] sourceIp , byte[] package){
for (int i = 0; i < 4; i ++){
package[i+12] = sourceIp[i]; //12=offset for source ip #ipv4
}
}
let's assert you byte[] package is conform to the TCP/IP IPv4 protocol and at least 160 bit (20byte) long...
http://en.wikipedia.org/wiki/IPv4
You can use DatagramPacket. It doesn't do anything to the network.
But it seems to me you should be rolling your own packet class here.
Related
I have 02 applications, one is written in java and the another one is written in C#. I need to exchange data (Strings) between them thus I use a socket connection.
Currently the C# application acts as server and java application acts as client. The C# application will continuously listen on port "7777" and the Java application will make a socket to "localhost:7777" as start up and use that socket to send data to C# server. I don't close the connection between them since I need to exchange data frequently.
Everything is great until my C# server application sometimes needs to send some Strings to the Java client application. I think about using another port to make my java application becomes a server and the C# application will also become a client. Yet I don't feel that it is a good solution.
P/S: Yes, socket is Bi-direction, in my current application I can send string data from my C# application back to Java application, but only when the Java application first sent a string to the C# application and now it is waiting for the respond data from the C# application.
Thus I want to ask if there is another better solution ? Is it possible if we only use 1 port in total ? Thank you very much.
Your socket works both ways (bi-directional), so you won't need to create another. You can just get the output stream and input stream of the socket, on both sides of the connection, and use those for sending/receiving.
To send a string, the easiest way would probably be to use a DataOutputStream as you can write UTF-8 strings with a simple function.
Java side:
String blah = "hey";
DataOutputStream dataOs = new DataOutputStream(socket.getOutputStream());
dataOs.writeUTF(blah);
The C# side is slightly trickier as you need to account for the fact that the first two bytes sent from the java function writeUTF() will actually be the length (in bytes) of the string that follows. You can grab those 2 bytes first and then throw them into an int, shifting the bits as you go along. Then you can use that int as the size of the buffer when you request the string from the socket. Should look something like this.
C# side:
int length = 0;
byte[] sizeBuffer = new byte[2];
socket.Receive(sizeBuffer);
for (int i = 0; i < sizeBuffer.Length; i += 1)
{
length = length << 8;
length += sizeBuffer[i];
}
byte[] stringBuffer = new byte[length];
socket.Receive(stringBuffer);
string myString = Encoding.UTF8.GetString(stringBuffer, 0, stringBuffer.Length);
Backstory:
I have a wireless device which creates it's own SSID, assigns itself an IP address using auto-ip, and begins broadcasting discovery information to 255.255.255.255. (unfortunately, it does not easily support multicast)
What I'm trying to do:
I need to be able to receive the discovery information, then send configuration information to the device. The problem is, with auto-ip, the "IP negotiation" process can take minutes on Windows, etc (during which time I can see the broadcasts and can even send broadcast information back to the device).
So I enumerate all connected network interfaces (can't directly tell which will be used to talk to the device), create a DatagramSocket for each of their addresses, then start listening. If I receive the discovery information via a particular socket, I know I can use that same socket to send data back to the device. This works on Windows.
The problem:
On Linux and OSX, the following code does not receive broadcast packets:
byte[] addr = {(byte)169, (byte)254, (byte)6, (byte)215};
DatagramSocket foo = new DatagramSocket(new InetSocketAddress(InetAddress.getByAddress(addr), PORT_NUM));
while (true)
{
byte[] buf = new byte[256];
DatagramPacket pct = new DatagramPacket(buf, buf.length);
foo.receive(pct);
System.out.println( IoBuffer.wrap(buf).getHexDump() );
}
In order to receive broadcast packets (on Linux/OSX), I need to create my DatagramSocket using:
DatagramSocket foo = new DatagramSocket(PORT_NUM);
However, when I then use this socket to send data back to the device, the packet is routed by the OS (I'm assuming) and since the interface of interest may be in the middle of auto-ip negotiation, fails.
Thoughts on the following?
How to get the "working" Windows behavior to happen on Linux/OSX
A better way to handle this process
Thanks in advance!
I do not think this is the problem with the code. Have you checked if OSX/Linux has correctly allowed those address / port number through their firewalls? I had this simple problem too in the past =P..
FYI, there is a nice technology called Zero-configuration which was built to solve this problem. It is very easy to learn so I recommend you to having a look at that as well.
Good luck.
While working on a Java application under Mac OS X (Lion, 10.7.2, Java version "1.6.0_29"), I'm running into a strange problem while trying to send multicast UDP datagrams. The packets are only being sent on one NIC, and I have no control over which one.
The following example code illustrates what I am trying to do:
public class MCast {
public static void main(String[] args) throws IOException {
InetAddress multicastAddr = InetAddress.getByName("224.0.0.1");
int port = 58680;
byte[] data = "test".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, multicastAddr, port);
MulticastSocket socket = new MulticastSocket();
socket.joinGroup(multicastAddr);
socket.setNetworkInterface(NetworkInterface.getByName("en0"));
socket.send(packet);
System.out.println("Packet sent.");
}
}
Whilst executing this code, I am using Wireshark to examine all the traffic going out on en0. No packets are sent on the port specified. However, said packets do appear on the trace for en1.
When I disable en1, the packets go out on en0 properly.
I'm at a loss here. Does anyone know what's going on?
Multicast output interface is decided by the current routing table at the time you do the group join. In most cases that means default route, hence the en0. Adding an explicit route would help, but you can just reverse the two lines in your code to be:
socket.setNetworkInterface(NetworkInterface.getByName("en0"));
socket.joinGroup(multicastAddr);
Also, you don't need to join (nor bind()) the group if you are only sending and not listening to that multicast traffic. Instead, connect() to the group address.
You can pass a an address to the constructor of MulticastSocket. You can use it to bind it to the address you want.
I am trying to write a simple multicast trial.
I used a standard code (sender and reciever).
I tried a few different standard pieces of code. it appears that the receiving code is stuck on receive (as if it's not receving anything).
receive side:
byte[] b = new byte[3];
DatagramPacket dgram = new DatagramPacket(b, b.length);
MulticastSocket socket =
new MulticastSocket(4545); // must bind receive side
socket.joinGroup(InetAddress.getByName("226.100.100.125"));
while(true) {
socket.receive(dgram); // blocks until a datagram is received
System.err.println("Received " + dgram.getLength() +
" bytes from " + dgram.getAddress());
dgram.setLength(b.length); // must reset length field!
}
sending side:
DatagramSocket socket = new DatagramSocket();
byte[] b = new byte[]{(byte)1,(byte)5,(byte)3};
DatagramPacket dgram;
dgram = new DatagramPacket(b, b.length,
InetAddress.getByName("226.100.100.125"), 4545);
System.err.println("Sending " + b.length + " bytes to " +
dgram.getAddress() + ':' + dgram.getPort());
while(true) {
System.err.print(".");
socket.send(dgram);
Thread.sleep(1000);
}
What is wrong with my code?
*I tried alot of different IPs also*
thanks for the help.
Try receiving in from the same IP but sending to localhost. If this works, then it's your router that is the problem as it doesn't support multicasting. If this still doesn't work then it's the IP address. try something in the 233.x.x.x - 239.x.x.x range.
I ran your code on my computer and it works fine as is, and also works if I changed the send address to localhost. Sounds like this is a problem with your router and not your code.
The network 239.0.0.0/8 is designated for administrator multi-cast traffic. If all of your machines are on the same network segment, you can use an ip in this network to play around with multi-casting.
Here is the RFC defining these:
https://www.rfc-editor.org/rfc/rfc2365
As far as sending goes... Your code should look something like this:
DatagramPacket p = ...
MulticastSocket s = new MulticastSocket(LISTENPORT);
InetAddress group = InetAddress.getByName(LISTENIP);
s.joinGroup(group);
s.send(p);
s.leaveGroup(group);
Multicast usually (in a real network) depends on the router's support. From what i know in general you can't really count on it being supported properly. I would try to send packets from a different client (command line or something else) to see if the server side is binding properly or not.
On the other side if you look here: http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xml it sais that the 226 address block is marked as reserved. The referenced RFC sais:
Use of IANA Reserved Addresses
Applications MUST NOT use
addressing in the IANA reserved
blocks.
That might have something to do with it also.
You shall not use same port, try different port and create one socket for receiving and second one for sending.
When this happens on my Linux boxes I check to make sure that
1) there is a route for 224.0.0.0/4 on the correct interfaces
2) the source IP address matches one of the routes for that interface
#2 is the stickiest in my lab. If my eth1 only has a route for 10.77.4.0/24 and some box is transmitting from 10.78.5.15, then linux discards it as a "martian packet".
Has anyone out there done the work of sitting on top of a packet capture interface (like jpcap) with an implementation of UDPSocket (for UDP datagrams) and InputStream (for TCP streams)?
I suppose it wouldn't be too hard to do given the callback API in jpcap, but has anyone out there already done it? Are there any issues with doing this (do I have to figure out how to reassemble a TCP stream myself, for example?)
I have not done this particular thing, but I do do a lot of work with parsing captured packets in C/C++. I don't know if there exist Java libraries for any of this.
Essentially, you need to work your way up the protocol stack, starting with IP. The pcap data starts with the link-level header, but I don't think there's much in it that you're concerned about, other than ignoring non-IP packets.
The trickiest thing with IP is reassembling fragmented datagrams. This is done using the More Fragments bit in the Flags field and the Fragment Offset field, combined with the Identification field to distinguish fragments from different datagrams Then you use the Protocol field to identify TCP and UDP packets, and the Header Length field to find the start of the corresponding header.
The next step, for both TCP and UDP, is demultiplexing, separating out the various connections in the captured packet stream. Both protocols identify connections (well, UDP doesn't have connections per se, but I don't have a better word handy) by the 4-tuple of the source and destination IP address and the source and destination port, so a connection would be a sequence of packets that matches on all 4 of these values.
Once that's done, for UDP, you're just about finished, unless you want to check the checksum. The Length field in the UDP header tells you how long the packet is; subtract 8 bytes for the header and there's your data.
TCP is somewhat more complicated, as you do indeed have to reassemble the stream, This is done using the sequence number in the header, combined with the length. The sum of these two tells you the next sequence number in the stream. Remember that you're keeping track of the traffic in two directions.
(This is a lot easier than writing an actual TCP implementation, as then you have to implement the Nagle algorithm and other minutiae.)
There's a lot of information on the net about the header formats; google "IP header" for starters. A network analyzer like Wireshark is indispensable for this work, as it will show you how your captured data is supposed to look. Indeed, as Wireshark is open source, you can probably find out a lot by looking at how it does things
Tcp reassembly can be done with JNetPcap. Here is a complete example:
final String SOME_PORT = 8888;
StringBuilder errbuf = new StringBuilder();
Pcap pcap = Pcap.openOffline("/dir/someFile.pcap", errbuf); //Can be replace with .openLive(...)
if (pcap == null) {
System.err.printf("Error: "+errbuf.toString());
return;
}
//Handler that receive Tcp Event one by one
AnalyzerListener<TcpStreamEvent> handler = new AnalyzerListener<TcpStreamEvent>() {
#Override
public void processAnalyzerEvent(TcpStreamEvent evt) {
JPacket packet = evt.getPacket();
Tcp tcp = new Tcp();
if (packet.hasHeader(tcp)) {
//Limiting the analysis to a specific protocol
if (tcp.destination() == SOME_PORT || tcp.source() == SOME_PORT) {
String data = new String(tcp.getPayload());
System.out.println("Capture data:{"+data+"}");
}
}
}
};
TcpAnalyzer tcpAnalyzer = JRegistry.getAnalyzer(TcpAnalyzer.class);
tcpAnalyzer.addTcpStreamListener(handler, null);
//Starting the capture
pcap.loop(Pcap.LOOP_INFINATE, JRegistry.getAnalyzer(JController.class), null);