в протоколе UDP нет такого состояния «соединения», однако вы можете создать свою собственную функцию, чтобы иметь список подключенных клиентов.
ниже я представлю вам некоторый код, который я создал для извлечения клиентов UDP и поддержания их в списке подключенных клиентов,
Когда вы создаете сервер, вы можете ждать входящих соединений (UDP-клиенты, отправляющие сообщения «connect»), затем, когда сервер получает эти запросы, проверяют список клиентов, если этот клиент уже существует, если нет, он создает новый клиент, он назначает идентификатор и отправляет ответ клиенту с назначенным идентификатором и сообщением, что-то вроде:
«1001 # подключен», после того как клиент отправляет запрос, ожидает ответа, когда ответ приходит, тогда ID извлекается и устанавливается в свойство идентификатора клиента, и выполняется socket.connect (ip, port), чтобы разрешить только запрос / ответ от / к серверу
/ ** @ TODO рассмотрим добавление переменной для указания количества клиентов
* этот класс содержит соединение с главным сервером со всеми клиентами
* подключено к игре, это соединение использует UDP, и оно действительно
* просто, если вам нужно использовать другой вид соединения, вы свободны
* создать свой собственный
* @author PavulZavala
* /
Сервер публичного класса
реализует 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 incomming 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;
}
} //
Класс клиента:
как вы теперь клиент был подключен к серверу, свойство простого идентификатора должно отличаться от cero, в моей реализации все идентификаторы являются числовыми, начиная с 1001, и этот идентификатор создается только сервером, единственный пробел, который у меня сейчас есть, как сервер сейчас, если клиент все еще активен, я думаю создать другой поток, где я мог бы периодически отправлять сообщения от клиента к серверу, чтобы гарантировать, что мы все еще общаемся, если сервер не получает, например, запрос от клиента в 5 минут, сервер отключает клиента или он может игнорировать отправку широковещательных сообщений клиенту, пока он не получит новое сообщение (я сейчас работаю над этим)
Клиент публичного класса
реализует 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();
}
} //
Реализация сервера, это можно сделать в основном приложении, которое будет клиентом
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);
}
Реализация клиента:
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);
}
Я надеюсь, что это может быть полезно для вас.
Консольные сообщения от сервера:
> Task :run
starting server
1001 Connected, response Sent
Консольные сообщения от клиента:
> Task :run
waiting to connect to the server
getting DATA: 1001#connected