I am trying to write a simple client/server in Java using NIO and Selectors. The server is very easy and it's the most typical implementation that you can find everywhere. Here's the code of the server (look at the start() method):
public final class MyServer {
private int port;
private String address;
public MyServer(String address, int port) {
this.address = address;
this.port = port;
}
public void start() throws IOException {
try {
Selector selector = Selector.open();
ServerSocketChannel socket = ServerSocketChannel.open();
InetSocketAddress addr = new InetSocketAddress(address, port);
socket.bind(addr);
socket.configureBlocking(false);
boolean isAlive = true;
SelectionKey selectKy = socket.register(selector, SelectionKey.OP_ACCEPT, null);
while (isAlive) {
selector.select();
Set<SelectionKey> keysList = selector.selectedKeys();
Iterator<SelectionKey> keys = keysList.iterator();
while (keys.hasNext()) {
SelectionKey theKey = keys.next();
if (theKey.isAcceptable()) {
SocketChannel clientSocket = socket.accept();
clientSocket.configureBlocking(false);
clientSocket.register(selector, SelectionKey.OP_READ);
}
if (theKey.isReadable()) {
SocketChannel clientSocket = (SocketChannel) theKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(9000);
clientSocket.read(buffer);
String result = new String(buffer.array()).trim();
System.out.println(" > SERVER: Request from " + clientSocket.getLocalAddress() + " [ toValidate = " + result + " ], sending tokens...");
//Now I send to the client a list
buffer.flip();
for (int i = 0; i < 5; ++i)
buffer.put((byte) getRandom(1, 10));
clientSocket.write(buffer);
buffer.clear();
System.out.println(" > SERVER: Response successfully sent");
}
keys.remove();
}
}
} catch (Exception e) {
System.out.println("[ SERVER ALERT: " + e.getMessage() + " ]");
}
}
}
As you can see, the code is pretty basic. Inside the if (theKey.isReadable()) I try to read a small string from the client, I print it to the console, then I flip the buffer and I send some data back to the client.
Here we have the client that sadly has a problem I cannot find:
public void something() throws IOException, InterruptedException {
ByteBuffer buffer = ByteBuffer.allocate(9000);
//Note that mRequests is a List of strings
try (SocketChannel client = SocketChannel.open(new InetSocketAddress(authIPAddr, authPort));) {
//For each transaction in mRequests, get the tokens from the server and verify them
for (String s : mRequests) {
//Write the string to the buffer and send the string
buffer.put(s.getBytes());
client.write(buffer);
buffer.rewind();
//Get response from the server
client.read(buffer);
buffer.clear();
Thread.sleep(1000);
}
}
}
The problem is exactly here:
buffer.put(s.getBytes());
client.write(buffer);
buffer.rewind();
I can say that the problem is there because the server should print
SERVER: Request from /127.0.0.1:2323 [ toValidate = {some_value} ], sending
tokens...
but instead it prints
SERVER: Request from /127.0.0.1:2323 [ toValidate = ], sending
tokens...
and from this I guess that data arent sent to the server. How can I solve this?
Related
I'm trying to send/recv a String using a Bytebuffer of fixed dimension, no matter what dimension is the string
Both client and server are composed by 2 classes, the main and the Client/Server, witch sould encapsulate the implementation of sending/receiving a string
For now the 2 main should work ad an echo client/server with the possibility of having multiple clients using a selector
Here is CLient side:
public class Client {
private SocketChannel clientChannel;
private int bufferDim;
public WinsomeClient(String host, int port, int bufferDim) throws IOException{
this.clientChannel = SocketChannel.open(new InetSocketAddress(host, port));
this.bufferDim = b.bufferDim;
System.out.println("Connected to " + b.serverHost + b.serverPort);
}
public String send(String request) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(bufferDim * Character.BYTES);
int iters = (int) Math.ceil(request.length / (float) bufferDim);
System.out.println("Iters: " + iters);
int offset = 0;
int length;
for(int i=0; i<iters; i++){
length = Math.min(bufferDim, request.length - offset);
buffer.put(request.getBytes(), offset, length);
buffer.flip();
while (buffer.hasRemaining()){
clientChannel.write(buffer);
}
buffer.clear();
offset += bufferDim;
}
System.out.println("Sent "+request.getMessage());
StringBuilder stringBuilder = new StringBuilder();
buffer.rewind();
while (clientChannel.read(buffer)>0){
buffer.flip();
while (buffer.hasRemaining()){
stringBuilder.append((char) buffer.get());
}
buffer.clear();
}
System.out.println("Recv" + stringBuilder.toString());
return stringBuilder.toString();
}
public class MainClient {
public static void main(String[] args) {
int port = 1919;
String host = "127.0.0.1";
int bufDim = 1024;
Client client ;
try{
client = new Client(host, port, bufDim);
for(String expected = "a"; ; expected+="a"){
System.out.println(expected);
String response = client.send(expected);
if(!response.equals(expected){
System.err.println("Expected " + expected + "; was " + response);
break;
}
System.out.println(response);
}
} catch (IOException e){
e.printStackTrace();
}
}
}
Server side:
public class Server {
private SocketChannel clientChannel;
private int bufferDim;
public WinsomeServer(SocketChannel clientChannel, int bufferDim){
this.clientChannel = clientChannel;
this.bufferDim = bufferDim;
}
public String recv() throws IOException{
ByteBuffer buffer = ByteBuffer.allocate(bufferDim * Character.BYTES);
buffer.rewind();
StringBuilder stringBuilder = new StringBuilder();
while (clientChannel.read(buffer)>0){
buffer.flip();
while (buffer.hasRemaining()){
stringBuilder.append((char) buffer.get());
}
buffer.clear();
}
System.out.println("Recv " + stringBuilder.toString());
return stringBuilder.toString();
}
public void send(String response) throws IOException{
ByteBuffer buffer = ByteBuffer.allocate(bufferDim * Character.BYTES);
buffer.rewind();
int iters = (int) Math.ceil(response.getMessageLength() / (float)bufferDim);
System.out.println("Iters: " + iters);
int offset = 0;
int length;
for(int i=0; i<iters; i++){
length = Math.min(bufferDim, response.getMessageLength()-offset);
buffer.put(response.getMessage().getBytes(), offset, length);
buffer.flip();
while (buffer.hasRemaining()){
clientChannel.write(buffer);
}
buffer.clear();
offset += bufferDim;
}
System.out.println("Sent "+ response.getMessage());
}
}
public class MainServer {
public static void main(String[] args) {
ServerSocketChannel serverChannel;
Selector selector = null;
ServerSocket serverSocket;
InetSocketAddress inetSocketAddress;
int port=1919;
System.out.println("Listening for connections on port " + port);
try{
serverChannel = ServerSocketChannel.open();
serverSocket = serverChannel.socket();
inetSocketAddress = new InetSocketAddress(port);
serverSocket.bind(inetSocketAddress);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e){
e.printStackTrace();
return;
}
while (true){
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
try {
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
}
else if(key.isReadable()){
System.out.println("Reading...");
SocketChannel client = (SocketChannel) key.channel();
Server server = new Server(client);
String request = server.recv();
SelectionKey key1 = client.register(selector, SelectionKey.OP_WRITE);
key1.attach(request);
}
else if(key.isWritable()){
System.out.println("Writing...");
SocketChannel client = (SocketChannel) key.channel();
Server server = new Server(client);
String request = (String) key.attachment();
request.append("Echoed by server");
server.send(request);
client.register(selector, SelectionKey.OP_READ);
}
}
catch (IOException e){
key.cancel();
try {
key.channel().close();
} catch (IOException ee){
}
}
iterator.remove();
}
}
}
}
However, after receiving the string and sending it back, the client is not recieving it.
Anyone can figure out where is the problem?
Server output:
Listening for connections on port 1919
Accepted connection from java.nio.channels.SocketChannel[connected local=/127.0.0.1:1919 remote=/127.0.0.1:51504]
Reading...
Recv a
Writing...
Writing... a1
Iters: 1
Sent a
Reading...
Client output:
Connected to 127.0.0.11919
a
Iters: 1
Sent a
I have two following classes (including only main methods for simplicity)
public class UDPServer {
public static void main(String[] args) throws IOException {
int serverPort = 9876;
DatagramSocket socket = new DatagramSocket(serverPort);
byte[] buffer = new byte[1024];
boolean isConnected = true;
while (isConnected) {
try {
DatagramPacket request = new DatagramPacket(buffer, buffer.length);
socket.receive(request);
DatagramPacket reply = new DatagramPacket(
request.getData(),
request.getLength(),
request.getAddress(),
request.getPort()
);
socket.send(reply);
} catch (IOException ex) {
isConnected = false;
Logger.getLogger(UDPServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
public class UDPClient {
public static void main(String[] args) {
String pathToQuery = getUserInput();
List<Packet> queried = UDPServer.getQueriedServerDataTypes(pathToQuery);
try {
if (args.length < 1) {
System.out.println("Usage: UDPCLient <msg> <server host name>");
System.exit(-1);
}
// Create a new datagram socket
DatagramSocket socket = new DatagramSocket();
// Preferred IPv4 address (cmd: ipconfig/all)
InetAddress host = InetAddress.getByName(args[0]);
int serverPort = 9876;
for (int i = 0; i < queried.size(); i++) {
Packet dataType = queried.get(i);
String requestFile = "data_type_request_v" + (i + 1) + ".txt";
UDPServer.saveToFile(
dataType,
"Server request.",
requestFile
);
// Serialize Packet object to an array of bytes
byte[] data = Tools.serialize(dataType);
System.out.println("Sent: " + Arrays.toString(data));
// Request datagram packet will store data query for the server
DatagramPacket request = new DatagramPacket(
Objects.requireNonNull(data),
data.length,
host,
serverPort
);
// Send serialized datagram packet to the server
socket.send(request);
// The reply datagram packet will store data returned by the server
DatagramPacket reply = new DatagramPacket(data, data.length);
System.out.println("Reply: " + Arrays.toString(reply.getData()));
// Acquire data returned by the server
socket.receive(reply);
// Store deserialized data in null Packet object.
Packet read = (Packet) Tools.deserialize(data);
System.out.println("Reading deserialized data...\n" + read.toString());
String responseFile = "data_type_response_v" + (i + 1) + ".txt";
UDPServer.saveToFile(
read,
"Server response.",
responseFile
);
boolean isResponseEqualRequest = UDPServer.isResponseEqualRequest(
requestFile,
responseFile
);
if (isResponseEqualRequest) {
System.out.println("Communication successful. Server response corresponds to the request.");
} else {
System.out.println("Communication unsuccessful. Server response does not correspond to the request.");
}
}
// Close the datagram socket
socket.close();
} catch (IOException | ClassNotFoundException ex) {
Logger.getLogger(UDPClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
In order for the UDPClient to work properly, UDPServer has to be running in the background. I want to run the UDPServer.main() in parallel thread and as soon as UDPClient.main() finishes, the thread should terminate as well.
How do I do that?
I'm trying to write a chat server and client that uses sockets. Connecting the client works properly and I get the correct output from the server:
Listening on port 8000
Got connection from Socket[addr=/127.0.0.1,port=50628,localport=8000]
But when I send something from the client I get IllegalBlockingModeException. Here's what I have:
public void listen()
{
try {
// Open a non-blocking socket channel
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
// Get the socket and bind it
ServerSocket ss = ssc.socket();
InetSocketAddress isa = new InetSocketAddress(this.getServerName(), this.getServerPort());
ss.bind(isa);
// Create the selector
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Listening on port " + this.getServerPort());
while (true)
{
// Wait for at least one channel to be selected
if (selector.select() == 0)
continue;
// Iterate the key set and dispatch messages
Set keys = selector.selectedKeys();
Iterator it = keys.iterator();
while (it.hasNext())
{
SelectionKey key = (SelectionKey)it.next();
if (key.isAcceptable()) {
Socket s = ss.accept();
System.out.println("Got connection from " + s);
SocketChannel sc = s.getChannel();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
}
else if (key.isReadable()) {
SocketChannel sc = null;
try {
sc = (SocketChannel)key.channel();
String message = processInput(sc);
if (message == null) {
key.cancel();
Socket s = null;
try {
s = sc.socket();
System.out.println("Closing connection to " +s);
s.close();
} catch( IOException ie ) {
System.err.println("Error closing socket " + s + ": " + ie);
}
}
else {
String response = getListener().process(sc, message);
ObjectOutputStream oos = new ObjectOutputStream(Channels.newOutputStream(sc));
//ObjectOutputStream oos = new ObjectOutputStream(sc.socket().getOutputStream());
oos.writeObject(response + "\n");
oos.close();
}
}
catch(IOException ie) {
key.cancel();
try {
sc.close();
} catch(IOException ie2) {
System.out.println(ie2);
}
System.out.println("Closed " + sc);
}
}
}
keys.clear();
}
}
catch (IOException ex) {
System.out.println(ex);
System.exit(1);
}
}
private String processInput( SocketChannel sc ) throws IOException {
buffer.clear();
sc.read( buffer );
buffer.flip();
if (buffer.limit()==0) {
return null;
}
return decoder.decode(buffer).toString();
}
Here's the stack trace:
java.nio.channels.SocketChannel[connected local=/127.0.0.1:8000 remote=/127.0.0.1:50656]
Exception in thread "main" java.nio.channels.IllegalBlockingModeException
at java.nio.channels.Channels.writeFully(Channels.java:97)
at java.nio.channels.Channels.access$000(Channels.java:61)
at java.nio.channels.Channels$1.write(Channels.java:174)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1870)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1779)
at java.io.ObjectOutputStream.<init>(ObjectOutputStream.java:247)
at ChatServer$Server.listen(ChatServer.java:171)
at ChatServer.run(ChatServer.java:231)
at ChatServer.main(ChatServer.java:247)
Java Result: 1
Does anybody know how to solve this?
You can't use Channels.newXXXStream() on a channel in non-blocking mode. See the Javadoc.
Ok, I managed to put it to work. Here's how:
String response = getListener().process(sc, message) + "\n";
ByteBuffer bb = ByteBuffer.wrap(response.getBytes("utf-8"));
sc.write(bb);
Instead of using an OutputStream, write directly to the SocketChannel.
I have a UDP multicast server listening on 2 sockets on 2 different ports. I achieved listening on these 2 sockets from clients. But i want to identify on which socket a client is sending a packet. Since my problem is that ; on the server if I listen on socket(9999) and if the client is sending on socket(8888) then at the server side I want it to identify the incoming packet is from which port.
public class MulticastReceiver
{
public static void main(String[] args)
{
MulticastSocket socket = null;
DatagramPacket packet = null;
MulticastSocket soc = null;
byte[] inBuf = null;
try
{
socket = new MulticastSocket(8888);
soc = new MulticastSocket(9999);
InetAddress address = InetAddress.getByName("224.2.2.3");
socket.joinGroup(address);
soc.joinGroup(address);
System.out.println("224.2.2.3 ready to receive packets");
while(true)
{
inBuf=new byte[256];
packet = new DatagramPacket(inBuf,inBuf.length);
System.out.println("port is: "+ packet.getAddress() + packet.getPort());
if(packet.getPort() == 9999)
{
soc.receive(packet);
//System.out.println("Data at 224.2.2.3:: " + new String(packet.getData()));
}
else
socket.receive(packet);
System.out.println("Data at 224.2.2.3:: " + new String(packet.getData()));
}
}
catch(Exception e)
{
}
}
}
public class MulticastSender {
public static void main(String[] args) {
DatagramSocket socket = null;
DatagramPacket outPacket = null;
byte[] outBuf;
final int PORT = 8888;
try {
socket = new DatagramSocket();
long counter = 0;
String msg;
msg = "This is multicast! ";
outBuf = msg.getBytes();
//Send to multicast IP address and port
InetAddress address = InetAddress.getByName("224.2.2.3");
outPacket = new DatagramPacket(outBuf, outBuf.length, address, PORT);
socket.send(outPacket);
System.out.println("Server sends : " + msg);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
} catch (IOException ioe) {
System.out.println(ioe);
}
}
}
public class AnotherSender {
public static void main(String[] args) {
DatagramSocket socket = null;
DatagramPacket outPacket = null;
byte[] outBuf;
final int PORT = 9999;
try {
socket = new DatagramSocket();
long counter = 0;
String msg;
msg = "This is another multicast! " + counter;
counter++;
outBuf = msg.getBytes();
//Send to multicast IP address and port
InetAddress address = InetAddress.getByName("224.2.2.3");
outPacket = new DatagramPacket(outBuf, outBuf.length, address, PORT);
socket.send(outPacket);
System.out.println("Server sends : " + msg);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
} catch (IOException ioe) {
System.out.println(ioe);
}
}
}
Your code doesn't make sense. The packet won't have a port number at all until you put one into it or receive() does, and at best this will just read alternately between the two sockets, blocking each time, possibly forever, receiving from one socket, and thus starving the other one.
You need a receiving thread for each non-blocking socket.
I try to implement a UDP client server detection via broadcast. The idea is the following: I have a server, which is bound to port 12344 and a client which is bound to port 12345. Now, the client sends a broadcast package to 255.255.255.255 12344. The server should reply to this package with a other package to IPClient:12345.
The implementation uses Java nio.
The problem is, that on windows, the server gets the packages but cannot(?) send an answer (I don't see the answer in wireshark).
I have the following example code:
Client
public final class ASyncUDPClient {
public static void main(String[] args) throws IOException {
InetSocketAddress hostAddress = new InetSocketAddress("255.255.255.255", 12344);
System.out.println(hostAddress);
// Create a non-blocking socket channel
DatagramChannel channel = DatagramChannel.open();
channel.socket().setBroadcast(true);
channel.socket().bind(new InetSocketAddress(getAddress(), 12345));
channel.configureBlocking(false);
// Kick off connection establishment
channel.connect(hostAddress);
ByteBuffer buffer = getBuffer();
Selector selector = Selector.open();
channel.write(buffer);
System.out.println("data send");
channel.register(selector, SelectionKey.OP_READ);
while (true) {
final int select = selector.select();
System.out.println("select " + select);
Iterator selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
System.out.println("key selected");
SelectionKey key = (SelectionKey) selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
}
if (key.isReadable()) {
System.out.println("read");
} else if (key.isWritable()) {
System.out.println("write");
}
}
}
}
private static ByteBuffer getBuffer() throws CharacterCodingException {
return Charset.forName("UTF-8").newEncoder().encode(CharBuffer.wrap("1234"));
}
private static InetAddress getAddress() throws SocketException {
final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
NetworkInterface networkInterfaceToUse = null;
while (networkInterfaces.hasMoreElements()) {
final NetworkInterface networkInterface = networkInterfaces.nextElement();
if (networkInterface.getDisplayName().contains("Virtual")) continue;
if (networkInterface.isVirtual()) continue;
if (networkInterface.isLoopback()) continue;
if (!networkInterface.isUp()) continue;
networkInterfaceToUse = networkInterface;
System.out.println(networkInterfaceToUse);
}
return networkInterfaceToUse.getInterfaceAddresses().get(1).getAddress();
}
}
Server example
public class ASyncUDPSvr {
static int BUF_SZ = 1024;
static int port = 12344;
static public void main(String[] args) {
ASyncUDPSvr svr = new ASyncUDPSvr();
svr.process();
}
private static InetAddress getAddress() throws SocketException {
final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
NetworkInterface networkInterfaceToUse = null;
while (networkInterfaces.hasMoreElements()) {
final NetworkInterface networkInterface = networkInterfaces.nextElement();
if (networkInterface.getDisplayName().contains("Virtual")) continue;
if (networkInterface.isVirtual()) continue;
if (networkInterface.isLoopback()) continue;
if (!networkInterface.isUp()) continue;
networkInterfaceToUse = networkInterface;
System.out.println(networkInterfaceToUse);
}
return networkInterfaceToUse.getInterfaceAddresses().get(1).getAddress();
}
private void process() {
try {
Selector selector = Selector.open();
DatagramChannel channel = DatagramChannel.open();
InetSocketAddress isa = new InetSocketAddress(getAddress(), port);
channel.socket().bind(isa);
channel.configureBlocking(false);
SelectionKey clientKey = channel.register(selector, SelectionKey.OP_READ);
clientKey.attach(new Con());
while (true) {
try {
selector.select();
Iterator selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
try {
SelectionKey key = (SelectionKey) selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
}
if (key.isReadable()) {
read(key);
key.interestOps(SelectionKey.OP_WRITE);
} else if (key.isWritable()) {
write(key);
key.interestOps(SelectionKey.OP_READ);
}
} catch (IOException e) {
System.err.println("glitch, continuing... " + (e.getMessage() != null ? e.getMessage() : ""));
}
}
} catch (IOException e) {
System.err.println("glitch, continuing... " + (e.getMessage() != null ? e.getMessage() : ""));
}
}
} catch (IOException e) {
System.err.println("network error: " + (e.getMessage() != null ? e.getMessage() : ""));
}
}
private void read(SelectionKey key) throws IOException {
DatagramChannel chan = (DatagramChannel) key.channel();
Con con = (Con) key.attachment();
con.sa = chan.receive(con.req);
System.out.println("sender address: " + con.sa + "rcv: " + new String(con.req.array(), "UTF-8"));
con.resp = Charset.forName("UTF-8").newEncoder().encode(CharBuffer.wrap("send string"));
}
private void write(SelectionKey key) throws IOException {
DatagramChannel chan = (DatagramChannel) key.channel();
Con con = (Con) key.attachment();
System.out.println("sending data: " + new String(con.resp.array(), "UTF-8") + " to " + con.sa);
chan.send(con.resp, con.sa);
System.out.println("data send");
}
class Con {
ByteBuffer req;
ByteBuffer resp;
SocketAddress sa;
public Con() {
req = ByteBuffer.allocate(BUF_SZ);
}
}
}
InetSocketAddress hostAddress = new InetSocketAddress("255.255.255.255", 12344);
// ...
channel.connect(hostAddress);
The problem is here. You can't connect to the broadcast address, and in any case it doesn't make sense. The broadcast address isn't sending to you, you are sending to it. The server is sending to you from its own bind-address. Just remove this line. You will have to use DatagramChannel.send() rather than write(), as you are unconnected.
The accepted answer is not true. You can "connect" the channel to the broadcast address when you first set:
channel.socket().setBroadcast(true);
Of course UDP is a connectionless protocol, but a "connected" DatagramChannel has some benefits, one of them is the ability to use the write(ByteBuffer[]) method.