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
}
Related
I'm sending a file and its name through Socket to a ServerSocket.
It works "partially" -- the server gets the file and saves it to disk but
it does not exit the loop in the copy() method in the ClientSession class.
public class Client{
DataOutputStream dos =null;
DataInputStream dis=null;
File f =new File("c:/users/supernatural.mp4");
public static void main(String[]ar) throws Exception{
try {
System.out.println("File upload started");
Socket socc = new Socket("localhost",8117);
dos = new DataOutputStream(socc.getOutputStream());
//send file name
dos.writeUTF(f.getName());
//send the file
write(f,dos);
//Files.copy(f.toPath(),dos);
//this prints
System.out.println("Data has been sent...waiting for server to respond ");
dis = new DataInputStream(socc.getInputStream());
//this never reads; stuck here
String RESPONSE = dis.readUTF();
//this never prints prints
System.out.println("Server sent: "+RESPONSE);
} catch(Exception ex) {
ex.printStackTrace();
} finally {
//close the exceptions
clean();
}
}
private static void write(File f,DataOutputStream d) throws Exception{
int count;
DataInputStream din = new DataInputStream(new BufferedInputStream(new FileInputStream(f)));
byte array[] = new byte[1024*4];
while((count =din.read(array)) >0){
d.write(array,0,count);
}
d.flush();
//this prints
System.out.println(" done sending...");
din.close();
}
}
//Server
public class MySocket implements Runnable{
int worker_thread=2;
volatile boolean shouldRun =false;
ServerSocket server;
String port = "8117";
//ExecutorService services;
static ExecutorService services;
public MySocket() {
this.server = new ServerSocket(Integer.valueOf(port));
services = Executors.newFixedThreadPool(this.worker_thread);
}
//A METHOD TO RUN SERVER THREAD
#Override
public void run(){
while(this.shouldRun){
Socket client =null;
try{
client = server.accept();
}catch(Exception ex){
ex.printStackTrace();
}
//hand it over to be processed
this.services.execute(new ClientSessions(client));
}
}
public static void main(String[]ar) throws Exception{
Thread t = new Thread(new MySocket());
t.start();
}
}
//the ClientSession
public class ClientSessions implements Runnable{
Socket s;
public ClientSessions(Socket s){
this.s = s;
}
DataInputStream dis=null;
DataOutputStream dos=null;
boolean success =true;
#Override
public void run(){
//get the data
try{
//get inside channels
dis = new DataInputStream(this.s.getInputStream());
//get outside channels
dos = new DataOutputStream(this.s.getOutputStream());
//read the name
//this works
String name=dis.readUTF();
String PATH_TO_SAVE ="c://folder//"+name;
//now copy file to disk
File f = new File(PATH_TO_SAVE);
copy(f,dis);
//Files.copy(dis,f.toPath());
//this doesnt print, stuck in the copy(f,dis) method
System.out.println("I am done");
success =true;
}catch(Exception ex){
ex.printStackTrace();
}finally{
//clean resources...
clean();
}
}
//copy from the stream to the disk
private void copy(File f,DataInputStream d)throws Exception{
f.getParentFile().mkdirs();
f.createNewFile();
int count =-1;
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
byte array[] = new byte[1024*8];
count =d.read(array);
while(count >0){
out.write(array,0,count);
count =d.read(array);
System.out.println("byte out: "+count);
}
//this never prints
System.out.println("last read: "+count);
out.flush();
out.close();
if(success)dos.writeUTF("Succesful");
else dos.writeUTF("error");
}
}
//for the clean method i simply have
void clean(){
if(dis!=null)dis.close();
if(dos!=null)dos.close();
}
I commented this //Files.copy(dis,f.toPath()); from server
because it does not go to next line after writing file to disk, sometimes even stuck there.
Could some pls point me in the right path, I believe i am doing something very wrong here
dont know if this is helpful but the client runs in eclipse and server in netbeans
Think about your procotol:
The Client sends the file name, then sends the binary file, then waits for the server response.
The Server reads the file name, then the binary file until the stream is closed, then sends the success message.
But the stream is never closed since the client is waiting for the response, hence you have a deadlock in your protocol.
This is usually solved by sending the file size first and having the server read exactly that many bytes.
Alternatively you can use the TCP's one-way shutdown feature to send a signal to the server that the output stream of the socket is closed. That can be done with socc.shutdownOutput();
And please use try-with-resources to avoid resource leaks (you must close the Socket, too).
Fixed Client:
try {
System.out.println("File upload started");
try (Socket socc = new Socket("localhost", 8117);
DataOutputStream dos = new DataOutputStream(socc.getOutputStream());
DataInputStream dis = new DataInputStream(socc.getInputStream())) {
// send file name
dos.writeUTF(f.getName());
// send the file
Files.copy(f.toPath(), dos);
dos.flush();
System.out.println("Data has been sent...waiting for server to respond ");
// signal to server that sending is finished
socc.shutdownOutput();
String RESPONSE = dis.readUTF();
// this never prints prints
System.out.println("Server sent: " + RESPONSE);
}
} catch (Exception ex) {
ex.printStackTrace();
}
Server:
public class MySocket implements Runnable {
int worker_thread = 2;
volatile boolean shouldRun = true;
ServerSocket server;
int port = 8117;
ExecutorService services;
public MySocket() throws IOException {
this.server = new ServerSocket(port);
services = Executors.newFixedThreadPool(this.worker_thread);
}
// A METHOD TO RUN SERVER THREAD
#Override
public void run() {
while (this.shouldRun) {
Socket client = null;
try {
client = server.accept();
} catch (Exception ex) {
ex.printStackTrace();
}
// hand it over to be processed
this.services.execute(new ClientSessions(client));
}
}
public static void main(String[] ar) throws Exception {
new MySocket().run();
}
}
class ClientSessions implements Runnable {
Socket s;
public ClientSessions(Socket s) {
this.s = s;
}
#Override
public void run() {
// get the data
try (DataInputStream dis = new DataInputStream(this.s.getInputStream());
DataOutputStream dos = new DataOutputStream(this.s.getOutputStream())) {
// read the name
// this works
String name = dis.readUTF();
String PATH_TO_SAVE = name;
// now copy file to disk
File f = new File("c://folder", PATH_TO_SAVE);
Files.copy(dis, f.toPath());
dos.writeUTF("Succesful");
System.out.println("I am done");
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The issue with your code is, that you read from an input stream of a socket, that is never closed.
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
byte array[] = new byte[1024*8];
count =d.read(array);
while(count >0){
out.write(array,0,count);
count =d.read(array);
System.out.println("byte out: "+count);
}
//this never prints
System.out.println("last read: "+count);
d.read(array) is actively trying to read from the socket, blocking until it receives something. Since the InputStream is actively blocking, it never returns a value less than or equal to 0. This is because the stream awaits the next package from the other end of the Socket.
Closing the Socket after sending the File should help you. In that case, the end of the Stream is reached and the InputStream returns.
Note: The InputStream you are reading from will (if the socket is closed) return a -1, as you can see within the JavaDoc.
In your case, this however might not be viable!
You want to answer the Client with "okay", or "error". If you close the socket, you cannot answer through the same Socket. The solution to this can be complex.
This situation is a bit tricky. Most frameworks out there have a Thread that reads from the SocketInputStream and passes the return value to some sort of handler (in blocking IO). Your while loop basically is this main reading loop inside the Thread. This loop will only exit, if the connection is lost and therefor the System.out.println("last read: "+count); could be changed to System.out.println("disconnected");
To keep it simple: You could give an estimation on how big the file will be and (just for testing purposes) write something like this:
DataOutputStream out = new DataOutputStream(new
BufferedOutputStream(new FileOutputStream(f)));
byte array[] = new byte[/* Big enough */ 1024 * 1024 * 8];
d.read(array); // Read the file content
out.write(array); // Write to the file
//this never prints
System.out.println("last read: "+count);
I have left out every error check here! This means that you only read one package from the server, which has to be the File.
I'm working on Client-Server application and I'v hit the wall with one issue. I got ServerWorker that is responsible for one connected client, it creates 2 threads, 1 to listen for incoming data from this client and 1 to send data to him.
class ServerWorker {
private DataProcessor dataProcessor;
private ObjectInputStream inputStream;
private ObjectOutputStream outputStream;
private Thread receiverThread;
private Thread senderThread;
private Optional<DataPacket> dataToSend;
private ServerWorker(Socket socket) {
try {
dataToSend = Optional.empty();
dataProcessor = new DataProcessor();
receiverThread = new Thread(this::readAndProcessData);
senderThread = new Thread(this::sendData);
inputStream = new ObjectInputStream(socket.getInputStream());
outputStream = new ObjectOutputStream(socket.getOutputStream());
} catch (IOException e) {
//TODO
}
}
static ServerWorker create(Socket socket) {
return new ServerWorker(socket);
}
void start() {
receiverThread.start();
senderThread.start();
}
void stop() {
receiverThread.interrupt();
senderThread.interrupt();
}
private void readAndProcessData() {
DataPacket dataPacket;
try {
while((dataPacket = (DataPacket)inputStream.readObject()) != null) {
System.out.println("incoming message: " + dataPacket.getContent());
dataToSend = Optional.of(dataProcessor.process(dataPacket));
}
} catch (ClassNotFoundException | IOException e) {
//TODO
}
}
private void sendData() {
while(true) { //TODO
dataToSend.ifPresent(data -> {
try {
outputStream.writeObject(data);
outputStream.flush();
dataToSend = Optional.empty();
} catch (IOException e) {
//TODO
}
});
}
}
}
And DataProcessor is just a small class for now
public class DataProcessor {
public DataPacket process(DataPacket packet){
packet.setContent(packet.getContent().toUpperCase());
return packet;
}
}
and ofcourse, DataPacket which is the same for both client and server
public class DataPacket implements Serializable {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
DataProcessor and DataPackets are just kind of POCs now, this will grow into much larger and more complicated classes, long story short, ServerWorker will recieve data and pass it to process, then after some logic is done, returning data will be stored inside dataToSend variable and removed after sending. Problem is, code I'v posted above works only sometimes. 90% of the time when I run my server app and client one (code below) nothing happens, uppercased "hello world" isnt going back to client. What's funny, when I run my server in debug mode (even without any breakpoints!), it works... Any ideas what the heck went wrong?
public static void main(String... args) throws Exception {
Socket socket = new Socket("localhost", 9999);
DataPacket dataPacket = new DataPacket();
dataPacket.setContent("hello world");
ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream());
os.writeObject(dataPacket);
os.flush();
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
while((dataPacket = (DataPacket)inputStream.readObject()) != null) {
System.out.println(dataPacket.getContent());
}
}
edit#
adding one more class, ConnectionDispatcher that is responsible for creating ServerWorker objects
class ConnectionDispatcher implements Runnable {
private ServerSocket serverSocket;
private List<ServerWorker> serverWorkers;
private volatile boolean isReceiving;
private ConnectionDispatcher(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverWorkers = new ArrayList<>();
isReceiving = false;
}
static ConnectionDispatcher create(int port) throws IOException {
return new ConnectionDispatcher(port);
}
#Override
public void run() {
isReceiving = true;
while(isReceiving) {
acceptIncomingConnections();
}
}
private void acceptIncomingConnections() {
try {
ServerWorker worker = ServerWorker.create(serverSocket.accept());
serverWorkers.add(worker);
worker.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
It seems that you're modifying dataToSend from one thread while simultaneously reading its value in another. This isn't thread-safe, and the thread that's reading its value may never see its updated value set by the other thread. For this reason, I'd declare dataToSend as volatile.
private volatile Optional<DataPacket> dataToSend;
I have not yet had the chance to test this out myself, but I can in about an hour (assuming this change doesn't fix your problem).
You could uses a Array Blocking Queue, to simulate a producer and consumer pattern.
Let the receiver thread, put new DataPacket into the queue, and let the sender take from the queue and process it and send it.
This will eliminate threading issues and acts as a buffer.
With your current code, you might loose packets,when they arrive at higher rate.
And i agree with user930, private Optional<DataPacket> dataToSend; should be volatile.
Also you can make your code much scalable with JavaNIO, you could look into Apache Mina project.
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 had problem to transfer data over TCP. So i was writing a UDP server, but its not working, shows this following error, how can i fix it?
My error:
run:
UDP Server started
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:657)
at socket.UDPHandler.start(UDPHandler.java:25)
at socket.UDPServer.waitForConnections(UDPServer.java:27)
at socket.UDPServer.main(UDPServer.java:46)
BUILD STOPPED (total time: 14 seconds)
UDPServer.java
package socket;
import java.net.*;
import java.io.*;
public class UDPServer
{
private int serverPort = 0;
private DatagramSocket serverSock = null;
//private Socket sock = null;
public UDPServer(int serverPort) throws IOException
{
this.serverPort = serverPort;
serverSock = new DatagramSocket(serverPort);
System.out.println("UDP Server started");
}
public void waitForConnections()
{
while (true)
{
try {
//sock = serverSock.accept();
//System.err.println("Accepted new socket");
UDPHandler handler = new UDPHandler(serverSock);
handler.start();
}
catch (IOException e){
e.printStackTrace(System.err);
}
}
}
public static void main(String argv[])
{
int port = 8889;
UDPServer server = null;
try {
server = new UDPServer(port);
}
catch (IOException e){
e.printStackTrace(System.err);
}
server.waitForConnections();
}
}
UDPHandler.java
package socket;
import java.io.*;
import java.net.*;
import main.*;
public class UDPHandler implements Runnable
{
private DatagramSocket sock = null;
private DatagramPacket sockInput = null;
private DatagramPacket sockOutput = null;
private Thread myThread = null;
public UDPHandler(DatagramSocket sock) throws IOException
{
this.sock = sock;
//sockInput = new DatagramPacket();
//sockOutput = sock.getOutputStream();
this.myThread = new Thread(this);
}
public void start()
{
myThread.start();
}
public void run()
{
while(true)
{
byte[] buf=new byte[1024];
int bytes_read = 0;
try {
// Incoming - Test
sockInput = new DatagramPacket(buf, buf.length);
sock.receive(sockInput);
bytes_read = sockInput.getLength();
String data = new String(sockInput.getData());
System.err.println("DATA: " + bytes_read + " bytes, data=" +data);
// IP - Test
InetAddress IPAddress = sockInput.getAddress();
int port = sockInput.getPort();
// Sending - Test
sockOutput = new DatagramPacket(data.getBytes(), data.length(), IPAddress, port);
sock.send(sockOutput);
} catch (Exception e) {
e.printStackTrace(System.err);
break;
}
}
try {
System.err.println("Closing socket.");
sock.close();
} catch (Exception e) {
System.err.println("Exception while closing socket, e="+e);
e.printStackTrace(System.err);
}
}
}
You are creating infinite number of threads in while(true) loop in waitForConnection() method. You should call DatagramSocket receive() method within your server (it is blocking operation) and if datagram is received, then delegate its processing to some handler (for example retrieved from thread pool).
I find that the receive() method causes a memory leak. While java waits for UDP packet, my memory usage slowly increases when no other thread is operating. Seems to be increasing memory usage at approx 20kb per hour while running the .receive() method.
Not sure why that would be.
I did not run/debug the code, but the java.lang.OutOfMemoryError makes me think there is something going on inside those while(true) loops, either too many handlers are being created in waitForConnections() (less likely) or too many byte[] buffers are being allocated in run() (more likely).
For the poster, try adding a print statement as the first line of each of those while(true) loops to see how many times they get executed.
It seems odd for the JVM to run out of memory with just a listening UDP server and a simple test application. I hope someone can debug into it and confirm, I can't run the code at the moment.
I am facing a probleme with threads and sockets I cant figure it out, if someone can help me please i would really appreciate.
There are the facts :
I have a service class NetworkService, inside this class I have a Socket attribute.
I would like it be at the state of connected for the whole lifecycle of the service.
To connect the socket I do it in a thread, so if the server has to timeout, it would not block my UI thread.
Problem is, into the thread where I connect my socket everything is fine, it is connected and I can talk to my server, once this thread is over and I try to reuse the socket, in another thread, I have the error message Socket is not connected.
Questions are :
- Is the socket automatically disconnected at the end of the thread?
- Is their anyway we can pass back a value from a called thread to the caller ?
Thanks a lot,
Here is my code
public class NetworkService extends Service {
private Socket mSocket = new Socket();
private void _connectSocket(String addr, int port) {
Runnable connect = new connectSocket(this.mSocket, addr, port);
new Thread(connect).start();
}
private void _authentification() {
Runnable auth = new authentification();
new Thread(auth).start();
}
private INetwork.Stub mBinder = new INetwork.Stub() {
#Override
public int doConnect(String addr, int port) throws RemoteException {
_connectSocket(addr, port);
_authentification();
return 0;
}
};
class connectSocket implements Runnable {
String addrSocket;
int portSocket;
int TIMEOUT=5000;
public connectSocket(String addr, int port) {
addrSocket = addr;
portSocket = port;
}
#Override
public void run() {
SocketAddress socketAddress = new InetSocketAddress(addrSocket, portSocket);
try {
mSocket.connect(socketAddress, TIMEOUT);
PrintWriter out = new PrintWriter(mSocket.getOutputStream(), true);
out.println("test42");
Log.i("connectSocket()", "Connection Succesful");
} catch (IOException e) {
Log.e("connectSocket()", e.getMessage());
e.printStackTrace();
}
}
}
class authentification implements Runnable {
private String constructFirstConnectQuery() {
String query = "toto";
return query;
}
#Override
public void run() {
BufferedReader in;
PrintWriter out;
String line = "";
try {
in = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
out = new PrintWriter(mSocket.getOutputStream(), true);
out.println(constructFirstConnectQuery());
while (mSocket.isConnected()) {
line = in.readLine();
Log.e("LINE", "[Current]- " + line);
}
}
catch (IOException e) {e.printStackTrace();}
}
}
Define the output stream as a member variable, attach it in your thread, and only close that stream when you're done...
Currently you're opening (and implicitly closing) the output stream within the thread. As the thread dies, it will close that output stream, which in turn may be killing the socket/connection.
If you define the stream outside of the thread, you can attach it within the thread, and close it at a later time such as when the service is asked to terminate.