Reputation: 39
I am doing a UDP program in Java language. I wish to send a message from the server to the client. However, as I am using UDP protocol.
How do I ensure that the client is connected before the datagram packet is sent?
buf = stringMessage.getBytes();
serversocket.send(new DatagramPacket(buf, stringMessage.length(), ia, cport));
// how to ensure that client is connected before sending?
Upvotes: 2
Views: 5581
Reputation: 425
there is no such 'connected' state in UDP protocol, however, you can create your own function to kinda have a list of connected clients.
Below I will present you some code I created to retrieve UDP clients and maintain them in a connected list of clients,
When you create the server you can wait for incoming connections, ( UDP clients sending "connect" messages ), then when the server receive those request check on the client list if that client is already there, if not, it creates a new client, it assigns an ID and send a response to the client with the ID assigned and message connected something like: "1001#connected", after client send the request is waiting for the response, when the response arrives then ID is retrieved and set to the ID property of the client, and execute socket.connect( ip, port ) in order to only allow request/response from/to server
/**@TODO consider add variable to specify number of clients
* this class contains main server connection with all clients
* connected to a game, this connection is using UDP and it is really
* simple, if you need to use other kind of connection you are free
* to create your own
* @author PavulZavala
*/
public class Server
implements Conectable
{
protected DatagramSocket serverSocket;
protected boolean isAccepting;
protected List<Client> clientList;
protected String ip;
protected int port;
private Thread connectThread;
/**
*
* @param port
* @throws IOException
*/
public Server( int port ) throws IOException
{
serverSocket = new DatagramSocket( port );
this.port = port;
this.isAccepting = true;
clientList = new ArrayList<>();
}//
/**
* Accept UDP connections and store in clientList
* ----------------------------------------------
* this method is used to receive packages from UDP clients,
* and store their IP and ADDRESS in the client list,
* - you can change isAccepting to false to no receive more
* client connections or simple, call stopIsAcception to finish
* the Tread.
* @TODO it can be changed to accept like server socket
*/
@Override
public void connect()
{
connectThread = new Thread( ()->
{
while( isAccepting )
{
try {
//datagram packet to receive incoming request from client
DatagramPacket request =
new DatagramPacket( new byte[ Config.UDP_BUFFER_SIZE ], Config.UDP_BUFFER_SIZE );
serverSocket.receive( request );
//get Port and Address from client
//and check if exists in clientList
Client c = clientList
.stream()
.filter( client ->
{
return client.getIp().equals( request.getAddress().getHostAddress() );
}).findFirst().orElse( null );
//if no exists add it and send response
if( null == c )
{
Client client = new Client();
client.setIp( request.getAddress().getHostAddress() );
client.setPort( request.getPort() );
client.setId( generateId() );
//adding new client to the list
clientList.add( client );
byte[] bufResp = (client.getId() + "#connected").getBytes( "UTF-8" );
DatagramPacket resp =
new DatagramPacket(bufResp, bufResp.length,
InetAddress.getByName( client.getIp() ),
client.getPort());
System.err.println( client.getId()+ " Connected, response Sent" );
serverSocket.send( resp );
}//
} //
catch (IOException ex)
{
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
});//.start();
connectThread.start();
}//
/**
* this stops thread to accepts client socket connections
* @throws java.lang.InterruptedException
*/
public void stopAccepting() throws InterruptedException
{
connectThread.join();
}
/**
* this closes the DatagramSocket that is acting
* as server
*/
public void closeServer()
{
serverSocket.close();
}
/**
* used to receive UDP packets from clients
* this method creates its own Thread so it can
* receive packages without blocking the game
* @param r
*/
public void receive( Requestable r)
{
new Thread(()->
{
while( true )
{
r.receiveData();
}
}).start();
}//
/**
* used to generate id for connected clients
* @return
*/
private int generateId()
{
return ++Config.SOCKET_ID_COUNTER;
}
/**
* used to send UDP packets to clients
* @param r
*/
public void send( Responsable r )
{
r.sendData();
}
public DatagramSocket getServerSocket() {
return serverSocket;
}
public void setServerSocket(DatagramSocket serverSocket) {
this.serverSocket = serverSocket;
}
public boolean isIsAccepting() {
return isAccepting;
}
public void setIsAccepting(boolean isAccepting) {
this.isAccepting = isAccepting;
}
public List<Client> getClientList() {
return clientList;
}
public void setClientList(List<Client> clientList) {
this.clientList = clientList;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}//
Client class:
how do you now client was connected with the server, simple id property must be different than cero, in my implementation all ID are numeric starting on 1001, and that ID is only created by server, the only gap i have right now is how the server now if the client is still active, i am thinking to create another Thread where i can send messages periodically from client to server in order to ensure we are still communication, if the server does not receive for example a request from a client in 5 minutes, servers disconnects the client or it can ignore send broadcast messages to the client until it receives a new message ( i am currently working on this )
public class Client
implements Conectable
{
protected DatagramSocket socket;
protected String ip;
protected int port;
protected int id;
private Thread connectThread;
/**
* constructor without arguments to use with getters and setters
* @throws java.net.SocketException
*/
public Client() throws SocketException
{
this.socket = new DatagramSocket();
id = 0;
//id set after increasement
//id = ++Config.SOCKET_ID_COUNTER;
}
/**
* this constructor creates a client indicating the ip and port
* where the server
* @param ip
* @param port
* @throws SocketException
*/
public Client( String ip, int port ) throws SocketException
{
this();
this.setIp( ip );
this.setPort(port);
}
public DatagramSocket getSocket() {
return socket;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
/**
* this method send a request to the server to connect
*/
@Override
public void connect()
{
try
{
//send connect request to server
send( "connect" );
}
catch (IOException ex)
{
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
connectThread =
new Thread( ()->
{
while( id == 0 )
{
DatagramPacket response =
new DatagramPacket(new byte[ Config.UDP_BUFFER_SIZE], Config.UDP_BUFFER_SIZE );
try
{
socket.receive( response );
String resp = new String( response.getData(), "UTF-8" );
resp = resp.trim();
System.err.println("getting DATA: "+resp);
if( resp.trim().contains( "connected" ) )
{
id = Integer.parseInt( resp.trim().split( "#" )[0] ) ;
socket.connect( InetAddress.getByName( ip ), port );
stopConnecting();
}
}
catch ( IOException | InterruptedException ex)
{
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
connectThread.start();
}
/**
* method used to receive responses from server,
* every time this method is called a new Thread is created
* be careful not to call many times this
* @param r
*/
public void receive( Requestable r )
{
new Thread(()->
{
while( true )
{
r.receiveData();
}
}).start();
}
/**
* this method is used to send requests to server
* @param r
*/
public void send( Responsable r )
{
r.sendData();
}
/**
* this method will send a request to the server to
* the specific IP and port set by this class
* @param data request data
* @throws UnknownHostException
*/
public void send( String data ) throws UnknownHostException, IOException
{
byte[] dataBuf = data.getBytes();
DatagramPacket request =
new DatagramPacket(dataBuf,
dataBuf.length, InetAddress.getByName( ip ), port );
socket.send( request );
}
/**
* this method kills Thread used that is created
* when we attempt to connect to the server
* @throws InterruptedException
*/
public void stopConnecting() throws InterruptedException
{
connectThread.join();
}
}//
Server implementation, this can be done in the main of the app that will be the client
try
{
System.err.println("starting server");
Server s = new Server( 24586 );
//accept incoming conenctions
s.connect();
}
catch (IOException ex)
{
Logger.getLogger(DemoLevel.class.getName()).log(Level.SEVERE, "::: error con algo", ex);
}
Client implementation:
try
{
client = new Client( "127.0.0.1" , 24586 );
System.err.println("waiting to connect to the server");
client.connect();
}
catch ( SocketException ex )
{
Logger.getLogger(DemoLevel.class.getName()).log(Level.SEVERE, "::: error with server connection", ex);
}
i hope this can be usefull for you.
Console Messages from server:
> Task :run
starting server
1001 Connected, response Sent
Console Messages from client:
> Task :run
waiting to connect to the server
getting DATA: 1001#connected
Upvotes: 0
Reputation: 20455
UDP protocol doesn't have state, so there is no "connection".
You either use TCP or have to make your server respond to confirm that message is received.
Upvotes: 3