I've written a simple asynchronous tcp server with the nio.
The server should be able to read and write at the same time for each client.
This I've implemented with a simple packet queue.
public class TcpJobHandler {
private BlockingQueue<TcpJob> _packetQueue = new LinkedBlockingQueue<TcpJob>();
private Thread _jobThread;
private final ReentrantLock _lock = new ReentrantLock();
public TcpJobHandler(){
_jobThread = new Thread(new Runnable() {
#Override
public void run() {
jobLoop();
}
});
_jobThread.start();
}
private void jobLoop(){
while(true){
try {
_lock.lock();
TcpJob job = _packetQueue.take();
if(job == null){
continue;
}
job.execute();
} catch (Exception e) {
AppLogger.error("Failed to dequeue packet from job queue.", e);
}finally{
_lock.unlock();
}
}
}
public void insertJob(TcpJob job){
try{
_packetQueue.put(job);
}catch(InterruptedException e){
AppLogger.error("Failed to queue packet to the tcp job queue.", e);
}
}
}
What this code do, is just checking for a new packet. If a new packet is available, this packet will be send to the client.
In the class tcp job, there is just the packet to send and a write class whichs writes the packet into the client stream.
As you can see, only one thread should be able to write a packet into a client stream.
This is the point, why I dont understand, why I'm getting this error? If I'm right, this exception says, that I try to send data into a stream, but there is already a thread which is writing data into this stream. But why?
//Edit:
I'm getting this exception:
19:18:41.468 [ERROR] - [mufisync.server.data.tcp.handler.TcpJobHandler] : Failed to dequeue packet from job queue. Exception: java.nio.channels.WritePendingException
at sun.nio.ch.AsynchronousSocketChannelImpl.write(Unknown Source)
at sun.nio.ch.AsynchronousSocketChannelImpl.write(Unknown Source)
at mufisync.server.data.tcp.stream.OutputStreamAdapter.write(OutputStreamAdapter.java:35)
at mufisync.server.data.tcp.stream.OutputStreamAdapter.write(OutputStreamAdapter.java:26)
at mufisync.server.data.tcp.stream.BinaryWriter.write(BinaryWriter.java:21)
at mufisync.server.data.tcp.TcpJob.execute(TcpJob.java:29)
at mufisync.server.data.tcp.handler.TcpJobHandler.jobLoop(TcpJobHandler.java:40)
at mufisync.server.data.tcp.handler.TcpJobHandler.access$0(TcpJobHandler.java:32)
at mufisync.server.data.tcp.handler.TcpJobHandler$1.run(TcpJobHandler.java:25)
at java.lang.Thread.run(Unknown Source)
The TcpJob looks like this:
public class TcpJob {
private BasePacket _packet;
private BinaryWriter _writer;
public TcpJob(BasePacket packet, BinaryWriter writer){
_packet = packet;
_writer = writer;
}
public void execute(){
try {
if(_packet == null){
AppLogger.warn("Tcp job packet is null");
return;
}
_writer.write(_packet.toByteArray());
} catch (IOException e) {
AppLogger.error("Failed to write packet into the stream.", e);
}
}
public BasePacket get_packet() {
return _packet;
}
}
The BinaryStream is just coupled to a AsynchronousSocketChannel which calls the write(byte[]) method from the socket channel.
You are using asynchronous NIO2. When you use asynchronous IO you cannot call write() until the last write has completed. From the Javadoc
* #throws WritePendingException
* If a write operation is already in progress on this channel
e.g. if you have used
public abstract Future<Integer> write(ByteBuffer src);
you cannot write again until this Future.get() returns.
If you use
public abstract <A> void write(ByteBuffer src,
long timeout,
TimeUnit unit,
A attachment,
CompletionHandler<Integer,? super A> handler);
You cannot write again until the CompletionHandler is called.
Note: you cannot be performing two reads at once either.
In your case you want something like
ByteBuffer lastBuffer = null;
Future<Integer> future = null;
public void execute(){
try {
if(_packet == null){
AppLogger.warn("Tcp job packet is null");
return;
}
// need to wait until the last buffer was written completely.
while (future != null) {
future.get();
if (lastBuffer.remaining() > 0)
future = _writer.write(lasBuffer);
else
break;
}
// start another write.
future = _writer.write(lastBuffer = _packet.toByteArray());
} catch (IOException e) {
AppLogger.error("Failed to write packet into the stream.", e);
}
}
Related
I want to communicate as a TCP Server on Port 2000 and 2001 with my TCP Client (Machine which sends Bytestreams).
Therefore I programmed a Spring Boot Application in Java.
This Question is only for Port 2001:
I use Camunda as BPMN-Engine for executing and orchestrating.
I start Threads like this:
package com.example.workflow;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
public class StartTCPServersDelegate implements JavaDelegate {
#Override
public void execute(DelegateExecution delegateExecution) throws Exception {
Runnable serverZyklisch = new ServerZyklisch();
Runnable serverAzyklisch = new ServerAzyklisch((String) delegateExecution.getVariable("param"));
Thread t1 = new Thread(serverZyklisch);
t1.start();
System.out.println("Thread Zyklisch gestartet");
Thread t2 = new Thread(serverAzyklisch);
t2.start();
System.out.println("Thread Azyk. gestartet");
String val1 = (String) delegateExecution.getVariable("param");
int valueParam = Integer.parseInt(val1);
System.out.println("Param ist: "+valueParam);
}
}
This is my ServerAzyklisch Class:
public class ServerAzyklisch implements Runnable, JavaDelegate {
private ServerSocket ssocket;
String param;
HexToByteConverter hexToByteConverter = new HexToByteConverter();
public ServerAzyklisch(String Pparam) throws IOException {
ssocket = new ServerSocket(2000);
param = Pparam;
}
public void run() {
System.out.println(param+"Paraaam");
InputStream in;
OutputStream out = null;
Socket socket;
while(true){
try {
socket = ssocket.accept();
in = socket.getInputStream();
out = socket.getOutputStream();
byte []data = new byte[132];
int numBytes = 0;
byte[]durch = hexToByteConverter.hexStringToByteArray("333333330041006400040000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
byte[]durchlauf = hexToByteConverter.hexStringToByteArray("333333330041006400040000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
byte[]Pressen1hexdump111 = hexToByteConverter.hexStringToByteArray("33333333003d0064000600000004004001c9c78900010000006f00000000000000000000000000010000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005");
byte[]Pressen1hexdump110 = hexToByteConverter.hexStringToByteArray("33333333003d0064000600000004004001c9c78900010000006e0000000000000000000000000001000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"+param);
byte[]Pressen2hexdump = hexToByteConverter.hexStringToByteArray("3333333300400065000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
byte[]Pressen3hexdump = hexToByteConverter.hexStringToByteArray("3333333300400065001400000000003d01c9c7890001000000c9000000000000000000000000000100000000001e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
byte[]Pressen3hexdumpNextBohrer = hexToByteConverter.hexStringToByteArray("3333333300400065001400000000003f01c9c789000100000078000000000000000000000000000100000000001e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002");
byte[]Pressen4hexdumpNextRSCIDBohrer = hexToByteConverter.hexStringToByteArray("33333333003f0065001400000000003d01c9c78900010000007a000000000000000000000000000100000000001e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
//gleichen Stream zurückschicken, der angekommen ist, für Durchlauf
while((numBytes = in.read(data)) != -1){
System.out.println(Arrays.toString(data));
out.write(Pressen1hexdump110);
out.write(Pressen2hexdump);
out.write(Pressen3hexdumpNextBohrer);
//out.write(durchlauf);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void execute(DelegateExecution delegateExecution) throws IOException {
}
}
I get everytime a different Result to my Client, so the behaviour is always another. But I want to send once all three bytearrays to my Client. I think something is wrong with my while loop.
Do you have any idea ?
By the comments, the communication is based on request-response pairs. You need to read 3 messages from the client, and return a response for each message. To do this, replace the while loop with:
readMessage(in, data);
out.write(Pressen1hexdump110);
readMessage(in, data);
out.write(Pressen2hexdump);
readMessage(in, data);
out.write(Pressen3hexdumpNextBohrer);
where the readMessage method is a new method you must add, that reads a complete request from the client.
If the client requests are always 128 bytes, there is a convenient method in DataInputStream that you can use:
void readMessage(InputStream in, byte[] buffer) throws IOException {
new DataInputStream(in).readFully(buffer, 0, 128);
}
In the general case the readMessage method would have to look something like this in pseudo-code:
void readMessage(InputStream in, byte[] buffer) {
// Read a message
while message is not complete:
read from "in" into "buffer"
if "in" was closed: throw an exception because the connection was closed mid-request
else: incorporate newly read data from "buffer" in message
done
}
I am developing an Android app in which there is going to be a lot of network communication between the app and my server.
To Accomplish this, I am using SocketChannel with Selector, to do the non-blocking IO.
The design I have chosen is, that there will be a BlockingQueue on which a 'NetworkIOManager' thread will be waiting. Other threads of the app will be posting messages to that BlockingQueue and the NetworkIOManager will pickup those messages and send it to the other thread AsyncRequestHandlerThread.
Therefore the main responsibility of NetworkIOManager thread is to pick the messages from BlockingQueue and delegate them to the AsyncRequestHandlerThread to send the requests and receive the responses.
Code for NetworkIOManager.java :
public class NetworkIOManager implements Runnable
{
private AsyncRequestHandlerThread handlerThread = null;
/*
*
* some code here
*
*/
private void vSendRequestUsingSocketChannel(String pTargetURL, int pTargetPort, String pRequestXML, boolean pUseSameConn) {
// if thread is not created, initialize the thread
if(handlerThread == null) {
handlerThread = new AsyncRequestHandlerThread();
}
// create a channel to send the request and register it with the selector
AsyncRequestHandlerThread.createChannelWithSelector(pTargetURL, pTargetPort, pRequestXML);
// if thread is not started, start it.
if(!handlerThread.isAlive())
handlerThread.start();
}
}
The AsyncRequestHandlerThread basically creates a SocketChannel for each request to be send, with a Non-Blocking configuration and registers it with a single Selector associated with this thread.
Code for AsyncRequestHandlerThread.java :
public class AsyncRequestHandlerThread extends Thread {
private static Selector selector = null;
public AsyncRequestHandlerThread() {
if(selector == null)
vSetSelector();
}
private static void vSetSelector()
{
try {
selector = Selector.open();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static Selector getSelector()
{
return selector;
}
public static void createChannelWithSelector(String pTargetURL, int pTargetPort, String pRequestXML) {
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(pTargetURL, pTargetPort));
socketChannel.register(selector, SelectionKey.OP_CONNECT, pRequestXML);
}
catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
// Wait for events with TIMEOUT : 30 secs
while (selector.select(30000) > 0) {
try {
// Get list of selection keys with pending events
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
// Process each key at a time
while (iterator.hasNext()) {
// Get the selection key
SelectionKey selKey = (SelectionKey)iterator.next();
// Remove it from the list to indicate that it is being processed
iterator.remove();
if (selKey.isValid() && selKey.isConnectable()) {
// Get channel with connection request
SocketChannel sChannel = (SocketChannel)selKey.channel();
boolean success = sChannel.finishConnect();
if (success) {
sChannel.register(selector, SelectionKey.OP_WRITE, selKey.attachment());
}
else {
// An error occurred; handle it
// Unregister the channel with this selector
selKey.cancel();
}
}
else if(selKey.isValid() && selKey.isWritable()) {
SocketChannel sChannel = (SocketChannel)selKey.channel();
// See Writing to a SocketChannel
ByteBuffer requestBuffer = null;
requestBuffer = ByteBuffer.wrap(selKey.attachment().toString().getBytes(Charset.forName("UTF-8")));
sChannel.write(requestBuffer);
sChannel.register(selector, SelectionKey.OP_READ);
}
else if (selKey.isValid() && selKey.isReadable()) {
// Get channel with bytes to read
SocketChannel sChannel = (SocketChannel)selKey.channel();
// See Reading from a SocketChannel
ByteBuffer responseBuffer = ByteBuffer.allocate(15);
while(sChannel.read(responseBuffer) > 0) {
String responseString = new String(responseBuffer.array(), Charset.forName("UTF-8"));
Log.d("STATS", responseString);
}
sChannel.close();
}
}
}
catch(IOException e)
{
e.printStackTrace();
}
catch(CancelledKeyException e) {
e.printStackTrace();
}
}
}
catch(IOException ex) {
ex.printStackTrace();
}
}
}
The issue I am facing is when I ran the application on my device I got an exception java.lang.IllegalThreadStateException on the line handlerThread.start(); in the class NetworkIOManager. When I am debugging this, the application works fine.
I am not able to understand where the problem lies and how can it be resolved ?
Any suggestions ?
You're trying to start the thread after it has exited. You need to rethink your logic. It presently assumes there can only be one such thread and that it never exits, which isn't true.
I'm trying to implement something which is a bit of a variation of UDP.
The server thread receives datagram packets, parses them, and passes them to the appropriate thread. If it receives a message, it replies with an acknowledgement and passes it to the ReceiveMessage Thread which prints it onscreen.
I have a SendMessage Thread and a ReceiveMessage thread.
I want the SendMessage thread to send a packet, and wait for an acknowledgement for a specific timeout period. I want to send a notify() to SendMessage, if the server receives an acknowledgement, and if it doesn't, I want the SendMessage thread to timeout and execute different code in both cases. How can I achieve this?
public class ListenThread extends Thread{
protected DatagramSocket socket = null;
protected Boolean on = true;
protected String id;
protected HashMap <String,Contact> people = null;
protected String user;
public ListenThread(String macadd, String user, HashMap <String, Contact> people) throws SocketException
{
super("ListenThread");
this.socket = new DatagramSocket(3333);
this.id=macadd;
this.people = people;
this.user = user;
}
#Override
public void run()
{
while (on)
{
byte[] buf = new byte[256];
try{
// receive request
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
String packdetails[] = new String(packet.getData(), 0, packet.getLength()).split(":");//Important part of receiving request. Tool used to parse the request
InetAddress address = packet.getAddress();
if(packdetails[0].equals("D")) // if it's a Detection Packet
{/* Handle what to do with Detection packets */
}// end of small if
}//end of big if
else if(packdetails[0].equals("M"))// implies, Message type packet
{
Timestamp t =new Timestamp(new Date().getTime());
//Send Acknowledgement
String PString = new String("A:"+id);
buf = PString.getBytes();
packet = new DatagramPacket(buf, buf.length, address, 3333);
new ReceiveMessage(packdetails, address, people, t).start();
}
else// if it's an acknowledgemnt
{
//notify the sendmessage thread
}
}//end of try
catch (UnknownHostException e)
{
System.err.print("Unable to find IP of current machine");
}
catch (IOException except)
{
System.err.print("Network Problem : Unable to send packets!");
}
}//end of while
}//end of run
}//end of class
public class SendMessage extends Thread{
protected Contact person = null;
protected String Message = null;
public SendMessage(Contact person, String Message)
{
this.person=person;
this.Message= Message;
}
#Override
public void run()
{
System.out.println(person.getusername()+": "+Message);
try
{
person.SendMessage(Message);
Thread.currentThread().wait(500);// If i get notify => received acknowledgement
}
catch(IOException e)
{
System.err.println("Unable to send message!");
}
catch (InterruptedException e)
{
System.err.print("Woken before receiving notify");
}
}
Use Condition.await(timeout,...) instead of Object.wait()
There are many to achieve this, You could opt for CountDownLatch or CyclicBarrier where the Sender thread would wait for the receiver to act on the barrier. Another alternative would be for the receiver thread to put the ack message in a blockingQueue and the sender could consume the ack from the queue while waiting on it.
I started learning networking with the main networking package in JDK, it's pretty simple and easy after a few examples. But now I am interested into making multi-client applications like a chat system.
My structure idea so far is like this:
Connection handler class, which handles incoming connections, and holds the list of clients.
If new connection was found, create a new client object, start it's thread (Client object will implement runnable, so it will start it's own looping service, it will loop for new packets received), and add it to the list.
I create a new thread for each client instead of looping through all clients because the reading from client process stops the whole execution and will wait for the client to send data, which is kinda annoys me and this is my issue there.
I have created a simple console app that receives messages from the client, but now I want to detect disconnections. I read that bufferedReader .read() method returns -1 if user is not connected, so I thought I could loop and do that every number of seconds to every client, but the thing is, the client must send a packet in order to .read() it, so let's say if you do .read() it will wait & stop the whole thread until packet is received, (I think).
This is my current code which gets messages from client:
public boolean isConnected() {
try {
this.in.read();
this.lastCheck = System.currentTimeMillis();
return true;
} catch (IOException e) {
if (!inConnection()) {
System.out.println("User disconnected");
try {
this.destruct();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
private boolean inConnection() {
return System.currentTimeMillis() - lastCheck < this.maxTime;
}
public void startClientService() throws IOException {
while(!this.session.isClosed()) {
if (System.currentTimeMillis() - this.checkTime > 600) {
System.out.println(System.currentTimeMillis() - this.checkTime);
if (this.isConnected()) {
int packetType = this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct() throws IOException {
this.session.close();
this.connection.removeClient(this);
System.out.println("Session killed");
}
Basically what happens here, I am sending a integer packed from the client, I might have many things to do so therefore I can set many unique packet ID's, so if I want to receive and process a chat message, the packet id is 216, the client sends a int 216, server reads the packet, enters the switch loop of all packet ids and detects if its really 216, if yes it gets the instance of the packed class that handles messages & gets the bytes of the received message like this:
public class Chat implements Packet {
#Override
public void processPacket(Session c) {
String message = readMessage(c);
System.out.println("Message: " + message);
}
private String readMessage(Session c) {
byte[] data = c.readBytes();
String message = null;
try {
message = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return message;
}
}
And this is how I read bytes:
public byte[] readBytes() {
int len;
byte[] data = null;
try {
len = this.dataIn.readInt();
data = new byte[len];
if (len > 0) {
this.dataIn.readFully(data);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
Okay my problem:
after adding the is disconnected detection, when I send my message, nothing happens. This is probably due to the .read() it stops and is waiting for a response. BUT if I write a message again, I will get the message in server.
This is my temporary, ugly client:
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 43594);
Scanner r = new Scanner(System.in);
PrintWriter out = new PrintWriter(socket.getOutputStream());
String input;
while(true) {
input = r.next();
if (input != null) {
sendMessage(input, out);
}
}
}
public static void sendMessage(String message, PrintWriter out) {
byte[] encoded = encode(message);
out.write(0);
out.println(encoded + "\n");
out.flush();
}
public static byte[] encode(String s) {
return DatatypeConverter.parseBase64Binary(s);
}
public static String decode(byte[] s) {
return DatatypeConverter.printBase64Binary(s);
}
}
My question is: What is a better way of reading data from client without making the application wait for it and actually loop everytime? OR maybe should I have a new thread for checking if user is online so it's 2 threads per 1 client?
If someone needs my session object (client object):
public class Session extends Thread implements Runnable {
private Socket session;
private Client client;
private PrintWriter out;
private BufferedReader in;
private PacketHandler packets;
private DataInputStream dataIn;
private ConnectionHandler connection;
private final int checkTime = 1600;
private final int maxTime = 22000;
private long lastCheck;
public Session(Socket session) {
this.session = session;
this.client = new Client(this);
try {
this.setStream();
} catch (IOException e) {
e.printStackTrace();
}
this.packets = new PacketHandler(this);
System.out.println("[New session created]: " + session.getRemoteSocketAddress());
}
public void setConnectionHandler(ConnectionHandler c) {
this.connection = c;
}
public void run() {
try {
this.startClientService();
} catch (IOException e) {
e.printStackTrace();
}
}
public void setStream() throws IOException {
this.out = new PrintWriter(this.session.getOutputStream());
this.in = new BufferedReader(new InputStreamReader(this.session.getInputStream()));
this.dataIn = new DataInputStream(this.session.getInputStream());
}
public Client getClient() {
return this.client;
}
public byte[] readBytes() {
int len;
byte[] data = null;
try {
len = this.dataIn.readInt();
data = new byte[len];
if (len > 0) {
this.dataIn.readFully(data);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
public String readMessage() {
try {
return this.in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean isConnected() {
try {
this.in.read();
this.lastCheck = System.currentTimeMillis();
return true;
} catch (IOException e) {
if (!inConnection()) {
System.out.println("User disconnected");
try {
this.destruct();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
private boolean inConnection() {
return System.currentTimeMillis() - lastCheck < this.maxTime;
}
public void startClientService() throws IOException {
while(!this.session.isClosed()) {
if (System.currentTimeMillis() - this.checkTime > 600) {
System.out.println(System.currentTimeMillis() - this.checkTime);
if (this.isConnected()) {
int packetType = this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct() throws IOException {
this.session.close();
this.connection.removeClient(this);
System.out.println("Session killed");
}
}
Thanks!
While I don't have time to look over all the code, here are two things that could help you out.
1) Use a defined message header. Define X number of bytes of each message that the client will send to the server. Use these bytes to define how long the message will be, and what type of message it is. The server knows the length and layout of this header, and uses it to process the message in a particular way. Example could be a header of one byte. A value of 1 could be a I'm connected message. 2 could be I'm about to disconnect. 3 could be I'm currently away, and 4 could be an incoming chat message.
2) There are 2 ways you can handle the input. First is to use blocking IO, and create a separate thread to receive messages from each client. I believe this is what you are currently doing. The second is to use non-blocking IO, and have a separate thread iterate over the open sockets and do a read. Non-blocking will check if there is data to read, but if there is not, the thread will continue executing.
I am trying to keep a connection open for a multithreaded server program. When I hit a button, I want it to send a test message to all clients that are connected.
public void run() {
try {
Scanner in = new Scanner(socket.getInputStream());
PrintWriter out = new PrintWriter(socket.getOutputStream());
readUpdate(out, in);
while(true){sendUpdate(out);}
} catch (Exception e) {
e.printStackTrace();
}
}
Uses way to much CPU.
This is my sendUpdate method.
private void sendUpdate(final PrintWriter out) {
new Thread(new Runnable() {
public void run() {
if(Server.send) {
try {
if (Server.command != "idle") {
System.out.println("Sending");
out.println("!msg#" + Server.command);
out.flush();
Server.send = false;
Thread.sleep(100);
}
} catch (Exception ex) {
}
}
}
}).start();
}
If somebody can help me keep the connection open, and ready to send data, I would appreciate it.
If your server can initiate messages and so can your client, you probably want a separate thread reading and writing. One thread makes sense for request-response style communication, where you can block on the next client request, do some server-side processing, respond to the client, and then block again.
But if you need to block on two separate conditions (receiving a message from the client and you clicking the button on the server) then you should have two separate threads. Otherwise, you will find yourself needing to repeatedly wake your thread up to check if either of the conditions are true.
So create two threads, and give one your Scanner (that does the readUpdate logic) and the other your PrintWriter. This is what your output handler could look like:
public class WriteHandler implements Runnable {
private final PrintWriter out;
private final BlockingQueue<String> messageQueue = new LinkedBlockingQueue<String>();
//initialize the above in a constructor;
public void run() {
while(true) {
String nextMessageToWrite = messageQueue.poll();
out.println(nextMessageToWrite);
}
}
public void send(String message) {
messageQueue.add(message);
}
}
This uses a blocking queue, which is a much better concurrency mechanism than a check-sleep loop. Then when the button is clicked, you can just have something like this:
public void actionPerformed() {
for ( WriteHandler handler : handlers ) {
handler.send("PING!");
}
}