UDP : Skip the response of timeout request - java

I have a UDP process that required to define the receiving port and IP address.
Upon testing I figured out that having the same reception IP and port at the same time of requests might result to data loss/switching.
I came up with the idea that If there's an existing request with same data, the incoming/2nd request will wait for the first one to finish the transaction and then do the request.
Since this is a servlet and I'm worrying for multiple requests in different browser at the same time, I used Semaphore in a singleton pattern to return single instance. My DatagramSocket is in singleton pattern as well to avoid "already bind" error.
My code is doing well with no data switching in a happy path scenario where in it will just send a request and response with no error.
I set a socket timeout and execute a timeout exception scenario,here's what I got
First request =====> UDP server
Timeout exception <==== UDP server
Second Request=====> UDP server
First response <==== UDP server
I'm receiving the first response on my 2nd request and so on.
How will I reject/skip the response of the timeout request? Please note that I am not allowed to add an identifier on my response as per requirement.
Here's my code for your reference :
private void calludp(String ip, String targetIp, String port, String targetPort, String timeout, byte[] message)
throws IOException, SAXException, ParserConfigurationException, InterruptedException {
String key = targetIp + ":" + targetPort;
UDPReceptionData udpReceptionData = null;
udpReceptionData = getReceptionLock(key, receptionMap);
Semaphore semaphore = udpReceptionData.getSemaphore();
DatagramSocket datagramSocket = udpReceptionData.getDatagramSocket();
semaphore.acquire();
try {
send(ip, targetIp, port, targetPort, timeout, message, datagramSocket);
} finally {
semaphore.release();
}
}
private void populateUDPReceptionMap(String port, String target) {
if ((target != null && !target.isEmpty()) && (port != null && !port.isEmpty())) {
Semaphore semaphore = new Semaphore(1);
DatagramSocket datagramSocket = getSocketMap(port);
UDPReceptionData udpReceptionData = new UDPReceptionData(datagramSocket, semaphore);
this.receptionMap.put(target + ":" + port, udpReceptionData);
}
}
private DatagramSocket getSocketMap(String port) {
System.out.println(socketMap.toString());
DatagramSocket datagramSocket = null;
if (port != null && !port.isEmpty()) {
if (!socketMap.containsKey(port)) {
try {
datagramSocket = new DatagramSocket(Integer.parseInt(port));
this.socketMap.put(port, datagramSocket);
} catch (NumberFormatException | SocketException e) {
e.printStackTrace();
}
} else {
datagramSocket = socketMap.get(port);
}
}
return datagramSocket;
}
public UDPReceptionData getReceptionLock(String ipPort, Map<String, UDPReceptionData> receptionMap) {
System.out.println(receptionMap.toString());
return receptionMap.get(ipPort);
}
public byte[] send(String target, String receptionTarget, String port, String receptionPort, String timeout,
byte[] message, DatagramSocket clientSocket) throws IOException {
String messageResponse = ""; // Message response to be return to the
// caller
int intTimeout = Integer.parseInt(timeout);
DatagramPacket receivePacket = null;
try {
InetAddress ipAddress = InetAddress.getByName(target);
int intPort = Integer.parseInt(port);
byte[] receiveData = new byte[1024];
DatagramPacket sendPacket = new DatagramPacket(message, message.length, ipAddress, intPort);
clientSocket.send(sendPacket);
// receive the data
receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
clientSocket.setSoTimeout(intTimeout);
clientSocket.receive(receivePacket);
} catch (SocketTimeoutException e) {
throw new SocketTimeoutException("Socket Timeout Exception");
}
messageResponse = new String(receivePacket.getData());
String mes = new String(message, "UTF-8");
System.out.println("FROM SERVER:" + messageResponse + " :::: " + mes);
} finally {
// commented out the close since this is a single instance DatagramSocket
// clientSocket.close();
}
return receivePacket.getData();
}
UDPReceptionData.java
import java.net.DatagramSocket;
import java.util.concurrent.Semaphore;
public class UDPReceptionData {
private Semaphore semaphore;
private DatagramSocket datagramSocket;
public UDPReceptionData(DatagramSocket datagramSocket, Semaphore semaphore) {
this.datagramSocket = datagramSocket;
this.semaphore = semaphore;
}
public DatagramSocket getDatagramSocket() {
return this.datagramSocket;
}
public Semaphore getSemaphore() {
return this.semaphore;
}
}
UDPServer.java
private void udpSender(DatagramSocket serverSocket) throws IOException, InterruptedException {
while (true) {
byte[] responseMessage = new byte[1024];
byte[] requestMessage = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(responseMessage, responseMessage.length);
serverSocket.receive(receivePacket);
String receivedMessage = new String(receivePacket.getData());
System.out.println("RECEIVED: " + receivedMessage);
InetAddress IPAddress = receivePacket.getAddress();
int port = receivePacket.getPort();
requestMessage = receivedMessage.getBytes();
// Set the sending packet to designated IP Address and port
DatagramPacket sendPacket = new DatagramPacket(requestMessage, requestMessage.length, IPAddress, port);
//delay for 4 seconds for timeout testing
Thread.sleep(4000);
serverSocket.send(sendPacket);
}
}
main method
public static void main(String[] args) {
UDPProcess m = new UDPProcess();
m.populateUDPReceptionMap("9090", "localhost");
String request = "localhost:9090:message1";
String request2 = "localhost:9090:message2";
new Thread() {
public void run() {
try {
m.calludp("localhost", "localhost", "7979", "9090", "2000", request.getBytes());
} catch (IOException | SAXException | ParserConfigurationException | InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
public void run() {
try {
m.calludp("localhost", "localhost", "7979", "9090", "60000", request2.getBytes());
} catch (IOException | SAXException | ParserConfigurationException | InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
Result :
java.net.SocketTimeoutException: Socket Timeout Exception
at com.comp.proj.connector.UDPProcess.send(UDPProcess.java:221)
at com.comp.proj.connector.UDPProcess.calludp(UDPProcess.java:135)
at com.comp.proj.connector.UDPProcess.access$0(UDPProcess.java:118)
at com.comp.proj.connector.UDPProcess$1.run(UDPProcess.java:51)
FROM SERVER:localhost:9090:message2 :::: localhost:9090:message1

You need sequence numbers in your requests and responses. You need to ignore responses from a prior sequence number. That can happen even without a timeout, as UDP datagrams can be duplicated (or more).

Related

Receive message from multicast socket on another host (from aws server instance)

I want to send/receive messages by the multicast socket.
This is my code
'''
public class MulticastSender {
public static final String GROUP_ADDRESS = "230.0.0.1";
public static final int PORT = 7766;
public static void main(String[] args) throws InterruptedException {
DatagramSocket socket = null;
try {
// Get the address that we are going to connect to.
InetAddress address = InetAddress.getByName(GROUP_ADDRESS);
System.out.println("GROUP_ADDRESS: " + address);
// Create a new Multicast socket
socket = new DatagramSocket();
DatagramPacket outPacket = null;
long counter = 0;
while (true) {
String msg = "Sent message No. " + counter;
counter++;
outPacket = new DatagramPacket(msg.getBytes(), msg.getBytes().length, address, PORT);
socket.send(outPacket);
System.out.println("Server sent packet with msg: " + msg);
Thread.sleep(1000); // Sleep 1 second before sending the next message
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (socket != null) {
socket.close();
}
}
}
'''
'''
public class MulticastReceiver {
public static final byte[] BUFFER = new byte[4096];
public static void main(String[] args) {
MulticastSocket socket = null;
DatagramPacket inPacket = null;
try {
// Get the address that we are going to connect to.
SocketAddress address = new InetSocketAddress(MulticastSender.GROUP_ADDRESS, MulticastSender.PORT);
// Create a new Multicast socket
socket = new MulticastSocket(address);
socket.joinGroup(address, NetworkInterface.getByName("eth0"));
while (true) {
// Receive the information and print it.
inPacket = new DatagramPacket(BUFFER, BUFFER.length);
socket.receive(inPacket);
// System.out.println(socket.getInterface());
String msg = new String(BUFFER, 0, inPacket.getLength());
System.out.println("From " + inPacket.getAddress() + " Msg : " + msg);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
'''
It is working on my local host. however, I want to send messages from my AWS server instance (with public ipv4) and receive them in the local client. How to do it?
if yes, please give me an example!
Tks all

How to run 2 main methods in parallel threads?

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?

UDP Multicast with Java: Message cannot be received by entire multicast group

I am trying to understand how multicast works, thus I am experimenting with it a bit.
Situation
I built a simple server which has a MulticastSocket, listening on port 1250. It simply echoes the message it receives.
Next I built a simple client, also with a MulticastSocket, listening on port 4711. It sends a String-Message to the server and waits for any message that comes back.
Expected behaviour
I want two or more clients send their own unique message to the server and receive all responses the server sends back to the multicast group listening on port 4711.
Observed behaviour / Problem
As soon as I start more than one instance of the client, all responses from the server are only received by the first client that joined the group. All other clients that joined the multicast group on port 4711 do not receive anything. Why is this and how can I solve the problem?
The result looks like this (you can see that only the process MulticastEchoClient2 receives the server's response):
Code
Server-Code
public class MulticastEchoServer
{
public static void main(String[] args)
{
if (args.length != 2)
{
System.out.println("Wrong usage of parameters! <MulticastAddress><id>");
return;
}
UDPMulticastSocket socket = null;
String id = args[1];
try
{
socket = new UDPMulticastSocket(1250);
System.out.println("Socket created...");
socket.join(args[0]);
while(true)
{
String msg = socket.receive(1024);
System.out.println("Message received: " + msg + " from " + socket.getSenderAddress() + ":" + socket.getSenderPort());
if (msg.toLowerCase().equals("quit"))
{
System.out.println("Shutting down...");
break;
}
socket.reply("Reply from " + id + " -> " + msg);
}
socket.leave(args[0]);
}
catch(Exception e)
{
e.printStackTrace();
}
if (socket != null)
{
socket.close();
System.out.println("Shutting down...");
}
}
}
Client Code
public class MulticastEchoClient
{
public final static int MESSAGES = 10000;
public static void main(String[] args)
{
if (args.length != 3)
{
System.out.println("Wrong usage of parameters: <Multicast-Address><Address of Server><id>");
return;
}
UDPMulticastSocket socket = null;
String id = args[2];
try
{
socket = new UDPMulticastSocket(4711);
socket.setTimeout(1000);
System.out.println("Socket created...");
socket.join(args[0]);
InetAddress srvrAddress = InetAddress.getByName(args[1]);
for (int i = 0; i < MESSAGES; i++)
{
socket.send(id + " sending: " + i, srvrAddress, 1250);
try
{
while(true)
{
String msg = socket.receive(1024);
System.out.println("Message received: " + msg + " from " + socket.getSenderAddress() + ":" + socket.getSenderPort());
}
}
catch (IOException e)
{
System.out.println("All messages received...");
}
}
socket.leave(args[0]);
}
catch(Exception e)
{
e.printStackTrace();
}
if (socket != null)
{
socket.close();
System.out.println("Shutting down...");
}
}
}
Utility Classes
public class UDPSocket
{
protected DatagramSocket socket;
private InetAddress senderAddress;
private int senderPort;
//constructors
protected UDPSocket(DatagramSocket socket)
{
this.socket = socket;
}
public UDPSocket() throws SocketException
{
this(new DatagramSocket());
}
public UDPSocket(int port) throws SocketException
{
this(new DatagramSocket(port));
}
//getters
public InetAddress getSenderAddress()
{
return senderAddress;
}
public int getSenderPort()
{
return senderPort;
}
//setters
public void setTimeout(int timeout) throws SocketException
{
socket.setSoTimeout(timeout);
}
//methods
public void send(String s, InetAddress rcvrAddress, int rcvrPort) throws IOException
{
byte[] data = s.getBytes();
DatagramPacket outPacket = new DatagramPacket(data, 0, data.length, rcvrAddress, rcvrPort);
socket.send(outPacket);
}
public String receive(int maxBytes) throws IOException
{
byte[] data = new byte[maxBytes];
DatagramPacket inPacket = new DatagramPacket(data, 0, data.length);
socket.receive(inPacket);
senderAddress = inPacket.getAddress();
senderPort = inPacket.getPort();
//return new String(data, 0, data.length);
return new String(data, 0, inPacket.getLength());
}
public void reply(String s) throws IOException
{
if (senderAddress != null)
{
send(s, senderAddress, senderPort);
}
else
{
throw new IOException("ERROR: No one to reply to!");
}
}
public void close()
{
socket.close();
}
}
public class UDPMulticastSocket extends UDPSocket
{
public UDPMulticastSocket(int port) throws IOException
{
super(new MulticastSocket(port));
}
public UDPMulticastSocket() throws IOException
{
super(new MulticastSocket());
}
public void join(String mcAddress) throws IOException
{
InetAddress ia = InetAddress.getByName(mcAddress);
((MulticastSocket) socket).joinGroup(ia);
}
public void leave(String mcAddress) throws IOException
{
InetAddress ia = InetAddress.getByName(mcAddress);
((MulticastSocket) socket).leaveGroup(ia);
}
}
You're replying to the sending address, so only the sending address is receiving the reply. If you want to reply to the multicast group, reply to the multicast group, not the sending address.

same server listening on different sockets

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.

Implementing a simple UDP network that continuously broadcasts to its neighbours

I have 3 nodes A, B and C with their respective port numbers.
I'm trying to write a java program that takes in 3 arguments:
its node name and its 2 neighboring node's ports and broadcasts a string "Hello I'm A" to them (so A would broadcast to B and C). It will do this every 3 seconds.
This program will be run in 3 separate instances.
Upon receiving a string it will print what node it has received it from "Received string" (example for Port B).
I have difficulties implementing this, I have heard of something called multicasting with UDP though. Here is my work so far, what am I doing wrong?
class UDP {
public static void main(String[] args) throws Exception {
String nodeName = args[0];
int neighbourPort1 = Integer.valueOf(args[1]);
int neighbourPort2 = Integer.valueOf(args[2]);
while(true) {
Thread.sleep(3000); //every 3 seconds
//Continously broadcast and listen to neighbour1
DatagramSocket socket1 = null;
try {
//CREATE SOCKET TO NEIGHBOUR1
InetAddress host = InetAddress.getByName("localhost");
socket1 = new DatagramSocket();
socket1.connect(host, neighbour1);
//CREATE DATAGRAMS FOR SENDING
String message = "Hello I'm " + nodeName;
byte[] sendData = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, host, port);
socket1.send(sendPacket);
//CREATE DATAGRAMS FOR RECEIVING
byte[] receiveData = new byte[100]; //is there a way to determine the needed space?
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket1.receive(receivePacket);
System.out.println("Received string");
} catch(Exception e) { }
//Do the same for neighbour2, code is basically identical except for variables
DatagramSocket socket2 = null;
try {
//CREATE SOCKET TO NEIGHBOUR2
InetAddress host = InetAddress.getByName("localhost");
socket2 = new DatagramSocket();
socket2.connect(host, neighbour2);
//FOR SENDING DATAGRAMS
String message = "Hello I'm " + nodeName;
byte[] sendData = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, host, port);
socket2.send(sendPacket);
//FOR RECEIVING DATAGRAMS
byte[] receiveData = new byte[100]; //is there a way to determine the needed space?
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket2.receive(receivePacket);
System.out.println("Received string");
} catch(Exception e) { }
}
}
}
I know I'm close to the solution. I'm able to broadcast properly but it's the constantly listening part that gets me.
I think its best to use a separate Thread to listen for data on your own port.
A sends data to B and blocks until it gets a packet from B.
B sends data to C and blocks until it gets a packet from C.
C sends data to A and blocks until it gets a packet from A.
Every node is waiting for each other. Just send the packets and wait 3 seconds.
The other thread is going to listen only.
public class UDP {
public static void main(String[] args) throws Exception {
final String nodeName = args[0];
final int ownPort = Integer.valueOf(args[1]);
final int neighbourPort1 = Integer.valueOf(args[2]);
final int neighbourPort2 = Integer.valueOf(args[3]);
// Don't create a new socket every time
DatagramSocket neighbour1 = new DatagramSocket();
DatagramSocket neighbour2 = new DatagramSocket();
neighbour1.connect(InetAddress.getLocalHost(), neighbourPort1);
neighbour2.connect(InetAddress.getLocalHost(), neighbourPort2);
// You have to LISTEN
new Thread() {
#Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket(ownPort);
byte[] buffer = new byte[socket.getReceiveBufferSize()];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
// Blocks until it gets a packet
socket.receive(packet);
System.out.println("Received string");
}
// socket.close();
} catch (final Exception e) {
e.printStackTrace();
}
}
}.start();
while (true) {
Thread.sleep(3000);
sendPacket(neighbour1, nodeName);
sendPacket(neighbour2, nodeName);
}
// If you're not using an infinite loop:
// neighbour1.close();
// neighbour2.close();
}
private static void sendPacket(DatagramSocket to, String from) throws Exception {
String message = "Hello I'm " + from;
byte[] data = message.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length);
to.send(packet);
}
}
Here is a simple frame for a server with two threads, one writing and one reading, place your network code at the right places.
package testing;
import java.util.Scanner;
public class ThreadTest {
public class MyListenerThread extends Thread {
#Override
public void run() {
/*
* Open Datagram ...
*/
while (true) {
/*
* Read data ...
*/
Scanner scanner = new Scanner(System.in);
System.out.println("read: " + scanner.next());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MySenderThread extends Thread {
#Override
public void run() {
/*
* Open Multicast ...
*/
while (true) {
/*
* Send ...
*/
System.out.println("Send ...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void start() {
MyListenerThread listener = new MyListenerThread();
MySenderThread sender = new MySenderThread();
listener.start();
sender.start();
}
public static void main(String[] args) {
ThreadTest server = new ThreadTest();
server.start();
}
}

Categories

Resources