Hallo I need to write a very simple app that sends and retrieve data trough an UDP link (on Android OS, API level > 18):
This's a piece of code I used in an application I wrote sometime ago to connect trough TCP/IP (and doing the same thing):
class LinkReader implements Runnable
{
private final int mPort;
private final String mAddress;
private final Socket mLinkSocket;
public LinkReader(String address, int port, Socket link_socket)
{
mLinkSocket = link_socket;
mPort = port;
mAddress = address;
}
#Override
public void run()
{
try {
BufferedReader input = new BufferedReader(
new InputStreamReader(
mLinkSocket.getInputStream()
)
);
while (true)
{
Thread.sleep(100);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
class LinkThread implements Runnable {
private final int mPort;
private final String mAddress;
private Socket mLinkSocket;
public LinkThread(String address, int port, Socket socket)
{
mLinkSocket = socket;
mPort = port;
mAddress = address;
}
#Override
public void run()
{
try
{
mLinkSocket = new java.net.Socket(
InetAddress.getByName(mAddress),
mPort
);
new Thread(new LinkReader(mAddress,mPort,mLinkSocket)).start();
}
catch (UnknownHostException e) {
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
private void sendCommand(String cmd, Socket socket)
{
try
{
PrintWriter _out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()
)
),
false
);
_out.print(cmd);
_out.flush();
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
I've not been able to find an example about UDP connection on Android OS and I tought to reuse this code mainly because it was working pretty good (but I have to admint that I had not a very deep knowlede of Android neither I had very hight performances expectations from this piece of code).
Besides it was working in my past application my question is it correct now, in terms of code structure and aging?
How can I modify this to establish an UDP connection?
BTW Up to now I don't need really a R/W connection (I just need to send commands over there).
Related
Trying to write - distributive simulation framework, where program is represented by an array with moving objects, server send command to move, client answer objects out of array
Goal - server send text message to each connected client separately
- client answer
Problem - can not find a way how to implement server listening and writing to one choosed client
Is there anyone, please, who can help me or get some idea?
private ServerSocket serverSocket;
private ArrayList<BufferedReader> clientBufReaders;
private ArrayList<BufferedWriter> clientBufWriters;
public static void main(String[] args) {
Server server = new Server();
}
public Server() {
try {
this.serverSocket = new ServerSocket(23456);
this.clientBufReaders = new ArrayList<BufferedReader>();
this.clientBufWriters = new ArrayList<BufferedWriter>();
this.clients();
} catch (IOException e) {
e.printStackTrace();
}
}
private void clients() {
Thread acceptThread = new Thread(new Runnable() {
private Scanner in;
public void run() {
while (true) {
try {
Socket clientSocket = serverSocket.accept();
clientBufReaders.add(new BufferedReader(new InputStreamReader(clientSocket.getInputStream())));
clientBufWriters.add(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())));
this.in = new Scanner(System.in);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
);
acceptThread.start();
while (true) {
synchronized (clientBufReaders) {
for (BufferedReader in : clientBufReaders) {
try {
if (in.ready()) {
System.out.println(in.readLine());
} else {
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
I have been working with TCP server/client stuff for a while. I am actully good at UDP programming when it comes to connecting more than one user that is multiple clients. I tried to do the same on a TCP server that i made using Threads but whenever the Thread gets to this piece of code
String reader = (String)in.readObject();
an error is generated and the thread stops executing the code but the thread still runs the program keeping it alive.
Anyway here is the entire source code :
public class TestServer implements Runnable {
private Thread run, streams, connect, receive, send;
private ServerSocket socket;
private Socket conn;
private ObjectInputStream in;
private ObjectOutputStream out;
private boolean running, incomingMessage = false;
private int port;
public TestServer(int port) throws IOException {
this.port = port;
socket = new ServerSocket(port);
console("Server stated on : " + InetAddress.getLocalHost() + " : " + port);
run = new Thread(this, "Run");
run.start();
}
public void run() {
running = true;
connect();
receive();
}
private void connect() {
connect = new Thread("Connect") {
public void run() {
while(running) {
try {
conn = socket.accept();
} catch (IOException e) {
e.printStackTrace();
}
console("You are now connected" + conn.getInetAddress().toString() + " : " + conn.getPort());
try {
setupStreams();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}; connect.start();
}
private void setupStreams() throws IOException {
streams = new Thread("Streams") {
public void run() {
try {
console("Setting up Streams");
out = new ObjectOutputStream(conn.getOutputStream());
out.flush();
in = new ObjectInputStream(conn.getInputStream());
console("Streams are now setup");
incomingMessage = true;
receive.start();
} catch(IOException e) {
e.printStackTrace();
}
}
}; streams.start();
}
private void receive() {
receive = new Thread("Receive") {
public void run() {
while(incomingMessage) {
String message = "";
try {
message = (String) in.readObject();
//This is the only flaw the program
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
console("Client : " + message);
}
}
};
}
private void console(String message) {
System.out.println(message);
}
public static void main(String[] args) {
try {
new TestServer(1234);
} catch (IOException e) {
e.printStackTrace();
}
}
}
FYI am not new to this. The error is caused because the server starts receiving packets even when there are no packets to be received. But because the thread forces it to receive it, i generates the error in the thread and dont know any other way to counter this. So please help. Thanks in Advance.
You shouldn't need 2 threads per connection. One thread is all that's required. After the connection is accepted, pass it to a worker thread to start reading. This can be done in a while loop in the worker thread.
Even though the socket's input stream can be read, the ObjectInputStream() class is more sensitive. If there is any error, its state is corrupted and it can't be used.
while (true) {
try {
Object input = in.readObject();
message = (String) input;
} catch (IOException e) {
e.printStackTrace();
break; //unrecoverable
} catch (ClassNotFoundException e) {
e.printStackTrace();
break; //unrecoverable
}
console("Client : " + message);
}
It's a better design to use a specific message protocol instead of sending serialized Java objects. For example if you are sending Strings like your sample, an InputStreamReader can be used to convert bytes to characters more easily and with less error handling.
These resources would be helpful to you:
https://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html#later
Java - Listening to a socket with ObjectInputStream
ObjectInputStream(socket.getInputStream()); does not work
I have this Java code that sends string with Socket. I can use the same code for Android.
public class GpcSocket {
private Socket socket;
private static final int SERVERPORT = 9999;
private static final String SERVER_IP = "10.0.1.4";
public void run() {
new Thread(new ClientThread()).start();
}
public int send(String str) {
try {
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),
true);
out.println(str);
out.flush();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return str.length();
}
class ClientThread implements Runnable {
#Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
Now, I need to send binary information encoded in ByteArray.
What might be the best ways to do this? I'm considering converting the ByteArray into string to use the same method, but I guess one can send the byte array information directly using Java Sockets.
just call write(byte[] a) on the OutputStream the one you get from the socket.
I'm trying to communicate flash android app to java using sockets, but when the app is connected via 3G - many packages get lost (not received).
Here is my Java server:
public class SServer implements Runnable {
protected int serverPort = 9070;
protected ServerSocket serverSocket = null;
protected boolean isStopped = false;
protected List<ClientRunnable> clients = new ArrayList<ClientRunnable>();
protected int msgCounter = 0;
public static void main(String args[]) {
SServer server = new SServer();
new Thread(server).start();
}
public void run(){
//init spam timer
new Timer().schedule(new TimerTask() {
#Override
public void run() {
for (ClientRunnable cr : clients) {
cr.send("Message " + msgCounter++);
cr.send("Message " + msgCounter++);
cr.send("Message " + msgCounter++);
}
}
}, 0, 2000);
openServerSocket();
while(! isStopped()){
Socket clientSocket = null;
try {
clientSocket = this.serverSocket.accept();
} catch (IOException e) {
if(isStopped()) {
System.out.println("Server Stopped."); return;
}
throw new RuntimeException("Error accepting client connection", e);
}
ClientRunnable cr = new ClientRunnable(clientSocket);
clients.add(cr);
new Thread(cr).start();
}
System.out.println("Server Stopped.") ;
}
private synchronized boolean isStopped() {
return this.isStopped;
}
private void openServerSocket() {
try {
this.serverSocket = new ServerSocket(this.serverPort);
System.out.println("Server Started.") ;
} catch (IOException e) {
throw new RuntimeException("Cannot open port 8080", e);
}
}
}
And this is the client thread:
public class ClientRunnable implements Runnable{
protected Socket clientSocket = null;
protected Boolean connected = false;
protected BufferedReader in = null;
protected PrintStream os = null;
public ClientRunnable(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void run() {
connected = true;
try {
//InputStream input = clientSocket.getInputStream();
in = new BufferedReader (new InputStreamReader(clientSocket.getInputStream(), "UTF-8" ));
os = new PrintStream(clientSocket.getOutputStream());
//read
//..
} catch (IOException e) {
onError(e);
connected = false;
closeConnection();
}
}
private void closeConnection() {
os.close();
//other closing code..
}
public void send(String data) {
try {
byte[] dataBytes = data.getBytes("UTF-8");
//will contain all bytes plus zery byte flash delimiter
byte[] allBytes = new byte[dataBytes.length + 1];
System.arraycopy(dataBytes, 0, allBytes, 0, dataBytes.length);
allBytes[allBytes.length-1] = (byte)0;
synchronized (this) {
Thread.sleep(50); //non 3G workaround
os.write(allBytes);
os.flush();
}
} catch (Exception e) {
e.printStackTrace();
onError(e);
}
}
public void onError(Exception ex) {
ex.printStackTrace();
}
}
Please note that Thread.sleep(50); before every send write - fixes the problem on non regular non 3G connections. But when the app is runnung on 3G, this value must be much higher and there are still missed pachages sometimes.
Here is my flex client:
<?xml version="1.0" encoding="utf-8"?>
<fx:Script>
<![CDATA[
import spark.events.ViewNavigatorEvent;
private var _socket:Socket;
private var _host:String = "<MY HOST IP>";
private var _port:int = 9070;
protected function onViewActivate(event:ViewNavigatorEvent):void {
_socket = new Socket(_host, _port);
_socket.addEventListener(Event.CLOSE, function(e:Event):void{ ta.appendText("Close\n"); });
_socket.addEventListener(Event.CONNECT, function(e:Event):void{ ta.appendText("Connect\n"); });
_socket.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void{ ta.appendText("IO Error\n"); });
_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(e:IOErrorEvent):void{ ta.appendText("Security Error\n"); });
_socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
}
private function socketDataHandler(event:ProgressEvent):void {
var socket:Socket = event.target as Socket;
var str:String = socket.readUTFBytes(socket.bytesAvailable);
ta.appendText(str+"\n");
}
]]>
</fx:Script>
<s:VGroup width="100%" height="100%">
<s:TextArea id="ta" skinClass="spark.skins.mobile.TextAreaSkin" width="100%" height="100%" />
<s:Button label="Reconnect" click="_socket.connect(_host, _port)" />
</s:VGroup>
And this is how it finally looks like:
(note that the sequenced sendings are problematic, although there is 50ms delay)
As you can see many of the sequencial messages are not received. Increasing the delay helps, but not always (and is bad as solution).
The Flex project is uploaded HERE
The Java project is uploaded HERE
I recently had a similar problem on a socket connection running between AS and Java. I solved it by making a message queue in Java, adding a message ID to my messages and then making the ActionScript side respond with the messageID before the next message in queue is sent. That guaranteed that the same message went out over and over until AS responded that it got it.
I'm working on a game that uses local area network. Like most of multiplayer games, there is a server-client system. Computer A runs an instance of program, creates a server and wait; Computer B do the same thing. Now Computer C runs the program, what I want is that he can see computer A and B listed there as game servers. How can I do this?
In order to list all of the servers available, a simple solution might be this: I need to check all of the IP addresses in a particular range and see if they respond via my specific port or not. If yes, therefor an instance of game is running on it and should be listed in the servers list.
Is the solution described above a good one?
I've searched and get this piece of code:
public void checkHosts(String subnet){
int timeout=1000;
for (int i=1;i<254;i++){
String host=subnet + "." + i;
if (InetAddress.getByName(host).isReachable(timeout)){
System.out.println(host + " is reachable");
}
}
}
but is takes so much time and is useless.
If it's not the right solution, what are some other ways?
If you are running on a local network, your method might take a huge amount of time and is definitely not the best solution.
You can solve it by having your servers periodically broadcast their addresses in the network, and have all the clients listen for it. A good example can be found in the Java Tutorials.
Send a discover message using either:
a multicast (use java.netMulticast socket)
broadcast (use java.net.DatagramSocket) to the networks broadcast address
Have all servers listen for that and reply saying "I'm here" and possibly more information for further connection setup (server name, version, use port x, udp or tcp etc)
The best way to do this is with something like ZeroConf ( also known as Bonjour ).
This is what Apple uses for all its network discovery in iTunes and iOS devices so that they can find each other.
I have implemented it Linux, Windows and OSX in server side applications with great success.
And there is great support in all the major relevant languages as well.
There is no need to re-invent this wheel.
you could use udp for this; send out a broadcast if a server is up and let al nodes listen for udp packets.
As requested, here is some example code on utp; theses are 2 classes, one is the heart (wich beats) and the other is the listener.
public class Heart extends Observable implements Runnable {
private String groupName = "229.5.38.17";
private int port = 4567;
MulticastSocket multicastSocket;
DatagramPacket datagramPacket;
public Heart(int connectionListenerPort, Observer...observers) {
for(Observer observer : observers) {
this.addObserver(observer);
}
try {
multicastSocket = new MulticastSocket();
InetAddress group = InetAddress.getByName(groupName);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(new Beat(connectionListenerPort));
objectOutputStream.flush();
objectOutputStream.close();
byte[] buf = byteArrayOutputStream.toByteArray();
datagramPacket = new DatagramPacket(buf, buf.length, group, port);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
while(true) {
beat();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void beat() {
try {
multicastSocket.send(datagramPacket);
message(new Message(TYPE.INFO, KEY.MESSAGE, "Heart beat sent."));
} catch (IOException e) {
e.printStackTrace();
}
}
private void message(Message message) {
setChanged();
notifyObservers(message);
}
}
public class BeatListener extends Observable implements Runnable {
private boolean run = true;
private String groupName = "229.5.38.17";
MulticastSocket multicastSocket;
private Network network;
public BeatListener(Network network, Observer... observers) {
for(Observer observer : observers) {
addObserver(observer);
}
try {
multicastSocket = new MulticastSocket(4567);
multicastSocket.joinGroup(InetAddress.getByName(groupName));
} catch (IOException e) {
error(e);
e.printStackTrace();
}
this.network = network;
}
#Override
public void run() {
while(run) {
DatagramPacket datagramPacket = new DatagramPacket(new byte[1500], 1500);
try {
multicastSocket.receive(datagramPacket);
if(!isLocalhost(datagramPacket.getAddress().getHostAddress())) {
Beat beat = getBeat(datagramPacket);
if(beat != null) {
network.setPeer(new Peer(datagramPacket.getAddress(), beat.getConnectionListenerPort()));
message(new Message(TYPE.NETWORK, KEY.NETWORK, network));
}
}
} catch (IOException e) {
error(e);
e.printStackTrace();
}
}
}
private void message(Message message) {
setChanged();
notifyObservers(message);
}
private void error(Exception e) {
message(new Message(TYPE.ERROR, KEY.MESSAGE, e.getClass().getSimpleName()));
}
public void stop() {
run = false;
}
private boolean isLocalhost(String hostAddress) {
boolean isLocalhost = false;
Enumeration<NetworkInterface> networkInterfaces;
try {
networkInterfaces = NetworkInterface.getNetworkInterfaces();
if(networkInterfaces != null) {
OUTER:
while(networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
if(inetAddresses != null) {
while(inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if(hostAddress.equals(inetAddress.getHostAddress())) {
isLocalhost = true;
break OUTER;
}
}
}
}
}
} catch (SocketException e) {
error(e);
e.printStackTrace();
}
return isLocalhost;
}
private Beat getBeat(DatagramPacket datagramPacket) {
Beat beat = null;
byte[] data = datagramPacket.getData();
if(data != null) {
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(data));
beat = (Beat)objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return beat;
}
}