|
Implement Raw Sockets
A rise in Java-based network monitoring apps attests to a penchant for writing low-level functionality in Java
by Daniel F. Savarese
April 14, 2005
Raw sockets. Oft asked for, yet much maligned. Since the first days of the java.net package, some programmers have wanted to use them, and other programmers have condemned their desire. Raw sockets provide the ability for user processes to send and receive Internet Control Message Protocol (ICMP) packets as well as network packets belonging to protocols not processed by the operating system. This functionality is considered by many to be too low level for Java. However, with each release of the Java platform, we see more low-level functionality introduced. The fact of the matter is that many programmers prefer to program in Java even when it comes to low-level systems programming. The abundance of network monitoring and management applications written in Java provides compelling evidence for this phenomenon.
Until J2SE 1.4, the Java socket API lacked many basic features. With the release of J2SE 1.4, long-missing socket options were added, support for IPv6 was introduced, and nonblocking I/O made its debut. Still, the ability to implement custom socket implementations and communicate with IP protocols other than TCP and UDP was not facilitated. Sockets are not abstracted into an interface. Instead, they are implemented as concrete classes and disjoint I/O interfaces (java.io versus java.nio). Therefore, it remains difficult to implement custom sockets that remain compatible with core APIs. Our implementation of raw sockets will not be derived from core API classes. Instead, it will focus on providing the basics: create a socket, read packets, and write packets.
Given the limited operations we intend to perform, we will require only four native methods. We need a method to create a socket, a method to close a socket, a method to read from the socket, and a method to write to the socket. These native methods will be wrapped by normal Java methods, which will check return values and error codes, possibly throwing an exception (see Listing 1). We could throw exceptions from native code, but it's simpler to do as little as possible in native code and handle as much as you can from Java itself. We will want to include error messages in our exceptions. Therefore, we also will need a native method that retrieves system error messages (see __getErrorMessage() in Listing 1).
Back to top
|