Java [Peer-To-Peer]: Runnable stops/blocks unexpectedly - java

I am developing a simple distributed ledger. I want to be able to start nodes on different ports, that could communicate with eachother. Each programme then would have a file, where it would write newly discovered nodes.
At first, only the most reliable nodes are hardcoded into that file.
Here is procedurally what happens:
1) I start a new node, which starts a HTTP server (I use com.sun.HttpServer). The server has a GetAddress handler, which listens to requests that go to the specified URI. It then gets the IP and PORT (which is specified in URI query params), acquires a semaphore for a known_nodes.txt file, and writes the newly received peer address to that file, if it's not already there, and send the contents of newly updated file as a json list back to the requester.
2) Within my Node class (which, as mentioned earlier, starts a HTTPServer on a separate thread), I create a ScheduledExecutorService and give it a runnable to be run every few seconds, whose job will be to connect to the URLs present in the known_nodes.txt file, and ask them for their known_nodes. If we received nodes that were not previously present in our known_nodes file, we overwrite our file.
NOW!
If I start a node, and try to request it from the browser, everything goes as planned - we receive a request, write it to our file, then our runnable will try to connect to the address specified in request. If we caught a SocketTimeoutException, we remove the address from our known_nodes.txt file.
The problem arises, when I start two nodes, running let's say on port 8001 and 8002. Please note, that each node has its own known_nodes file.
What happens, is that one of the nodes will stop running the DiscoverAddresses task, the other one won't. So effectively, one nodes stopped receiving requests.
NB! The node that will stop its scheduledtask will STILL send at least ONE discovering request, and then will die/block(?).
Here is the code for the runnable task:
#Override
public void run() {
log.info("still running ");
PeerAddressesHolder inactiveNodes = new PeerAddressesHolder();
ApplicationConfiguration appConf = ApplicationConfiguration.getInstance();
for (PeerAddress peerAddress : knownNodes.getAddresses()) {
if (isSameNode(peerAddress)) {
continue;
}
String urlString = String.format("http://%s:%s%s?myport=%d", peerAddress.getIP(), peerAddress.getPort(), Constants.GET_ADDRESS, myPort);
try {
StringBuilder result = new StringBuilder();
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
try (InputStream connInputStream = conn.getInputStream();
InputStreamReader ir = new InputStreamReader(connInputStream);
BufferedReader br = new BufferedReader(ir)){
String line;
while ((line = br.readLine()) != null) {
result.append(line).append('\n');
}
} catch (Exception e) {
log.warn("Couldn't read from connection input stream",e);
}
PeerAddressesHolder peerAddressesHolder = gson.fromJson(result.toString(), PeerAddressesHolder.class);
boolean fetchedNew = false;
for (PeerAddress fetchedAddress : peerAddressesHolder.getAddresses()) {
if (!isValidAddress(peerAddress)) {
log.warn("Peer has sent us a null-address. It will be ignored.");
return;
}
if (!knownNodes.contains(fetchedAddress)) {
knownNodes.addAddress(fetchedAddress);
fetchedNew = true;
}
}
if (fetchedNew) {
FileUtils.writeToFile(appConf.getKnownNodesFilePath(), gson.toJson(knownNodes), false);
}
} catch (SocketTimeoutException e) {
if (appConf.getMostReliableNodes().contains(peerAddress)) {
log.warn("Most reliable node not available: " + peerAddress);
} else {
inactiveNodes.addAddress(peerAddress);
log.warn("Connection timeout from " + peerAddress + ". It will be removed.");
}
} catch (Exception e) {
log.warn("Couldn't discover new addresses." + e);
}
}
try {
knownNodes.removeAll(inactiveNodes.getAddresses());
FileUtils.writeToFile(appConf.getKnownNodesFilePath(), gson.toJson(knownNodes), false);
} catch (IOException ioe) {
log.warn("Couldn't write to file after deleting dead node", ioe);
}
}
And here is how I start it upon Node creation.
public NetworkNode(int port) {
this.appConf = ApplicationConfiguration.getInstance();
this.port = port;
log.info("Starting a new node on port " + port);
try {
this.knownNodes = FileUtils.createPeerAddressesList(appConf.getKnownNodesFilePath());
} catch (Exception e) {
log.error("Error while trying to construct a list of peer addresses from file content on path: " + appConf.getKnownNodesFilePath());
}
scheduledExecutorService = Executors.newScheduledThreadPool(4);
scheduledExecutorService.scheduleAtFixedRate(new DiscoverAddressesTask(knownNodes, this.port), 3, 4, TimeUnit.SECONDS);
Methods dealing with file reading/writing are all done using try-with-resources construct, so my initial idea that the runnable stops because of some unclosed streams is probably not valid.

Related

Mulitple clients with a server and sockets? [duplicate]

I did some different tutorials but nothing works, can someone see what i'm doing wrong?
private volatile boolean keepRunning = true;
public FileSharedServer() {
}
#Override
public void run() {
try {
System.out.println("Binding to Port " + PORT + "...");
// Bind to PORT used by clients to request a socket connection to
// this server.
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("\tBound.");
System.out.println("Waiting for Client...");
socket = serverSocket.accept();
System.out.println("\tClient Connected.\n\n");
if (socket.isConnected()) {
System.out.println("Writing to client serverId " + serverId
+ ".");
// Write the serverId plus the END character to the client thru
// the socket
// outStream
socket.getOutputStream().write(serverId.getBytes());
socket.getOutputStream().write(END);
}
while (keepRunning) {
System.out.println("Ready");
// Receive a command form the client
int command = socket.getInputStream().read();
// disconnect if class closes connection
if (command == -1) {
break;
}
System.out.println("Received command '" + (char) command + "'");
// decide what to do.
switch (command) {
case LIST_FILES:
sendFileList();
break;
case SEND_FILE:
sendFile();
break;
default:
break;
}
}
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
// Do not close the socket here because the readFromClient() method
// still needs to
// be called.
if (socket != null && !socket.isClosed()) {
try {
System.out.println("Closing socket.");
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* This method sends the names of all of the files in the share directory.
*
* #throws IOException
*/
private void sendFileList() throws IOException {
File serverFilesDir = new File("serverFiles/");
if (!serverFilesDir.exists() || serverFilesDir.isFile()) {
System.out.println("'serverfiles' is not an existing directory");
throw new IOException("'serverfiles' directory does not exist.");
}
File[] files = serverFilesDir.listFiles();
for (File file : files) {
socket.getOutputStream().write(file.getName().getBytes());
// Even the last one must end with END and then finally with
// END_OF_LIST.
socket.getOutputStream().write(END);
}
socket.getOutputStream().write(END_OF_LIST);
}
/**
* this methods sends a particular file to the client.
*
* #throws IOException
*/
private void sendFile() throws IOException {
StringBuilder filename = new StringBuilder();
int character = -1;
while ((character = socket.getInputStream().read()) > -1
&& character != END && (char) character != END_OF_LIST) {
filename.append((char) character);
}
System.out.println(filename);
File file = new File(System.getProperty("user.dir")
+ System.getProperty("file.separator") + "serverfiles",
filename.toString());
String totalLength = String.valueOf(file.length());
socket.getOutputStream().write(totalLength.getBytes());
socket.getOutputStream().write(END);
FileInputStream fileInputStream = new FileInputStream(file);
int nbrBytesRead = 0;
byte[] buffer = new byte[1024 * 2];
try {
while ((nbrBytesRead = fileInputStream.read(buffer)) > -1) {
socket.getOutputStream().write(buffer, 0, nbrBytesRead);
}
} finally {
fileInputStream.close();
}
}
public static void main(String[] args) throws InterruptedException {
// Create the server which waits for a client to request a connection.
FileSharedServer server = new FileSharedServer();
System.out.println("new thread");
Thread thread = new Thread(server);
thread.start();
}
}
Do I need another class or just a couple of lines in main? on the very bottom.
It's over a wifi network and all I need is two clients at once, or more :)
The problem here is that you are running only a single thread on the server. This thread accepts a connection, writes the server ID to the connection, then reads from the connection. The thread then continues to read from the connection until a -1 is received, at which point the thread exits. At no point does the thread try to accept a second connection; ServerSocket.accept() is called only once. As a result, you can only handle one client.
What you need is to split your class into two separate classes. In the first class, the run() method goes into a loop, calling ServerSocket.accept(), and each time that method returns a socket, creates an instance of the second class, hands it the socket, and starts it, after which it loops back to the ServerSocket.accept() call.
The second class is almost identical to the class you've already written, except that it doesn't contain the ServerSocket.accept() call. Instead, socket is a member variable which is initialized, by the first class, before it is started. It can do all the handling of the socket, sending the server ID, receiving and handling commands, etc., just as your existing code does.

file transfer between server and client input stream

I'm working on a java server-client based file transfer over socket project, I'll sum up the project shortly, I have text files related to server and client, server related text contains which ports are going to be opened and client text contains the IP and port to be connected on(server side is like 4444 and client side is like 4444 localhost) The file transfer on a single client is running pretty ok, now I'm working on second client connection and transfer, what I'm trying to do is; when a second client is run, it will read the first line of the text file (which is already in use by the first client), I thought a recursion will solve the problem but seems I couldn't figure out what I've done wrong, below are the code snippet from client side
boolean connected = false;
private void connection() {
while (!connected) {
try {
FileReader fr = new FileReader("c_input.txt");
BufferedReader br = new BufferedReader(fr);
String line = br.readLine();
String delims = "[ ]";
String[] elements = new String[8];
elements = line.split(delims);
serverPort = Integer.parseInt(elements[portIndex]);
hostIP = elements[ipIndex];
clientSocket = new Socket(hostIP, serverPort);
is = clientSocket.getInputStream();
if (is != null) {
connected = true;
System.out.println("connected to " + hostIP + " from port "
+ serverPort);
br.close();
fr.close();
} else {
System.out.println("The port " + serverPort
+ " is occupied, now trying another port.");
portIndex = portIndex + 2;
ipIndex = ipIndex + 2;
connection();
}
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
System.exit(0);
}
}
}
I used recursion there, because if a port is bound by another client it has to read another line from text file and split and retry connection with the new line's inputs.(in terms short the whole method will run again) But when it comes to running, the first client connects and when second one tries to connect from same port with client1 the code still gets in if loop instead of getting in else block (I get the message from the if check's println on the console and by the way is in the if check stands for InputStream) which means there is a stream coming from server, is this normal? if so how can I achieve the whole thing connection method does all over again if the port is bound by another client?

How do I serve Flash policy files from an Eclipse plugin?

I have an Eclipse plugin that needs to open a pair of sockets to a flash application running on the local machine. Flash requires a policy file (blob of XML) giving permissions to access the ports in question. Flash prefers to get this policy file over port 843, Java treats ports < 1024 as privileged ports and Mac OS X and Linux similarly restrict access to ports < 1024. I don't want to run my Eclipse plugin with root permissions, so serving up the policy file on port 843 is not an option. According to Adobe documentation, if Flash can't get the policy file on port 843, it falls back to requesting the policy file on the port to which it's trying to connect. The ActionScript code looks like this:
/**
* Connecting to some port to communicate with the debugger. We initiate the
* connection because Flex doesn't allow us to listen to any ports.
*/
private function initSockets():void
{
requestSocket = new Socket();
requestSocket.addEventListener(Event.CONNECT, requestConnected);
requestSocket.addEventListener(Event.CLOSE, closed);
requestSocket.addEventListener(ProgressEvent.SOCKET_DATA, processRequestData);
requestSocket.addEventListener(IOErrorEvent.IO_ERROR, ioError);
requestSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
requestSocket.connect("localhost", SCConstants.DEBUG_LESSON_REQUEST_PORT);
eventSocket = new Socket();
eventSocket.addEventListener(Event.CONNECT, eventConnected);
eventSocket.addEventListener(Event.CLOSE, closed);
eventSocket.addEventListener(ProgressEvent.SOCKET_DATA, processEventData);
eventSocket.addEventListener(IOErrorEvent.IO_ERROR, ioError);
eventSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
eventSocket.connect("localhost", SCConstants.DEBUG_LESSON_EVENT_PORT);
}
On the Eclipse plugin side I've inherited some code that works most of the time on OS X, but sometimes fails on Windows. Running on Wi-Fi rather than wired ethernet also tends to fail, although I have no idea why this should matter.
public Boolean connect() throws DebugException {
try {
try {
// connection code
fRequestServerSocket = new ServerSocket(requestPort);
fRequestServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
fEventServerSocket = new ServerSocket(eventPort);
fEventServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
TWBLogger.logInfo("Open socket request server:" + fRequestServerSocket);
TWBLogger.logInfo("Open socket event server:" + fEventServerSocket);
String policy = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<cross-domain-policy>\n" +
"<allow-access-from domain=\"*\" to-ports=\"5000,5001\" secure=\"false\" />\n" +
"</cross-domain-policy>\0";
// Because of the Flash security policy the first thing
// that will accept on the socket will be the Flash Player
// trying to verify us. The Flash player will request security
// policy file with the following string: <policy-file-request/>\0
// We will serve back the above policy file and then close the socket
// The next thing to accept is our process in the VM.
fRequestSocket = fRequestServerSocket.accept();
fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fRequestSocket.getInputStream()));
// Wait some time before giving flash the policy file. Otherwise they don't get it. ;(
// 3 is too much ... ;(
Thread.sleep(100);
fRequestWriter.print(policy);
fRequestWriter.flush();
fRequestSocket.close();
// this should be the real connection
fRequestSocket = fRequestServerSocket.accept();
TWBLogger.logInfo("Open socket request:" + fRequestSocket);
fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fRequestSocket.getInputStream()));
// the same situation for the EventSocket
fEventSocket = fEventServerSocket.accept();
fEventReader = new BufferedReader(new InputStreamReader(fEventSocket.getInputStream()));
TWBLogger.logInfo("Open socket event:" + fEventSocket);
} catch (SocketTimeoutException e) {
TWBLogger.logWaring("Connection to the Client Timed out.");
cleanSockets();
return false;
requestFailed("Connection to the VM timed out. Please close any other running lessons that you debug and try again", e);
} catch (SocketSecurityException e) {
requestFailed("Security error occured when connecting to the VM", e);
} catch (Exception e) {
if (!fTerminated)
requestFailed("Error occured when connecting to the VM. Please close any other running lessons that you debug.", e);
}
} catch (DebugException e) {
// close the sockets so that we can debug another application
cleanSockets();
throw e;
}
// our VM is single threaded
fThread = new TWBThread(this);
fThreads = new IThread[] {fThread};
// start listening for events from the VM
fEventDispatch = new EventDispatchJob();
fEventDispatch.schedule();
// start listening for breakpoints
IBreakpointManager breakpointManager = getBreakpointManager();
breakpointManager.addBreakpointListener(this);
breakpointManager.addBreakpointManagerListener(this);
return true;
}
This code looks wrong. It doesn't wait for the message from Flash and instead just jams the policy response into the port. As I said, it works most of the time, but it fails sometimes and doesn't seem to comply with Adobe's documentation.
I tried listening for request packets on each port and sending a port specific response. I watched socket traffic using WireShark on the loopback interface (Mac OS X). I saw policy requests coming in and responses getting sent, but Flash still gave me Security Sandbox Violation on both ports.
I also tried adding this line at the beginning of initSockets shown above:
Security.loadPolicyFile("xmlsocket://localhost:5002");
Then I added code in my plugin to listen on port 5002 and send the following master policy file content:
private final static String FLASH_POLICY_RESPONSE =
"<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\n" +
"<cross-domain-policy>\n" +
"<site-control permitted-cross-domain-policies=\"master-only\"/>\n" +
"<allow-access-from domain=\"*\" to-ports=\"5000,5001\"/>\n" +
"</cross-domain-policy>\0";
Again I saw the request come in and the response go out, but Flash didn't seem to respond to it. I didn't get the Security Sandbox Violation errors, but there was also no traffic over the ports.
Can anyone enlighten me on the correct approach to opening sockets between Java and Flash?
I found the solution to this. I made a mistake early on and used BufferedReader.readLine to read the policy request. This isn't appropriate since policy requests are null terminated, not new line terminated. This was confusing since it does return when the underlying stream closes. Thus I got the request and sent a response, but the response was sent after the ActionScript code had already decided that the request had failed.
On the Java side I used the following code to establish communication on the ports:
// Create server sockets.
fRequestServerSocket = new ServerSocket(REQUEST_PORT);
fRequestServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
TWBLogger.logInfo("Open socket request server:" + fRequestServerSocket);
fEventServerSocket = new ServerSocket(EVENT_PORT);
fEventServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
TWBLogger.logInfo("Open socket event server:" + fEventServerSocket);
// Serve up the Flash policy file.
serveFlashPolicy();
// Connect request socket.
fRequestSocket = fRequestServerSocket.accept();
TWBLogger.logInfo("Open socket request:" + fRequestSocket);
fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fRequestSocket.getInputStream()));
// Connect event socket.
fEventSocket = fEventServerSocket.accept();
TWBLogger.logInfo("Open socket event:" + fEventSocket);
fEventReader = new BufferedReader(new InputStreamReader(fEventSocket.getInputStream()));
Serving up the policy file is handled as follows:
private void serveFlashPolicy() {
ServerSocket serverSocket = null;
Socket socket = null;
TWBLogger.logInfo("Waiting for flash policy request on port " + FLASH_POLICY_PORT);
try {
serverSocket = new ServerSocket(FLASH_POLICY_PORT);
serverSocket.setSoTimeout(ACCEPT_TIMEOUT);
socket = serverSocket.accept();
PrintWriter writer = new PrintWriter(socket.getOutputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder request = new StringBuilder();
int c;
while (0 < (c = reader.read())) {
request.append((char) c);
}
String policyRequest = request.toString();
if (policyRequest.startsWith(FLASH_POLICY_REQUEST)) {
writer.print(FLASH_POLICY_RESPONSE);
writer.print("\0");
writer.flush();
}
} catch (IOException e) {
TWBLogger.logWaring("IOException on port " + FLASH_POLICY_PORT + ": " + e.toString());
e.printStackTrace();
} finally {
if (null != socket) {
try {
socket.close();
} catch (Exception e) {
// Ignore
}
}
if (null != serverSocket) {
try {
serverSocket.close();
} catch (Exception e) {
// Ignore
}
}
}
TWBLogger.logInfo("Flash policy complete on port " + FLASH_POLICY_PORT);
}
The Flash policy response looks like this:
private final static String FLASH_POLICY_RESPONSE =
"<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\n" +
"<cross-domain-policy>\n" +
"<allow-access-from domain=\"*\" to-ports=\"5000,5001\"/>\n" +
"</cross-domain-policy>";
The site-control tag I had previously been sending is only allowed in master policy files served from port 843.

cannot get server socket to close

I'm making a simple chat server and just made it so each connection runs on a new thread.
The old version started a single thread for the server, it did a while loop, which would stop when a stop message was sent then close the socket.
The new version loops forever and create a new thread for each new connection. Now I cannot close the socket connection.
If you press a key and the main thread stops, the socket stays open. Thus when I run the program again I need to change the socket number.
code of server
while(true)
{
///////////////////////////////////////////////////
// get a new connection
///////////////////////////////////////////////////
System.out.println("Aceepting connections on port 1030 \r");
try{
// Get New Connection
// wait for ever on accepting new connections
server.setSoTimeout(0);
connection=server.accept();
cConnection thread = new cConnection("thread3", connection);
} catch(IOException ec)
{
System.out.println(ec.getMessage());
}
}
code that starts server
Now each message comes in on a new thread, so I cannot tell it to stop and close the socket.
You need to provide a flag that must be globally accesible, so when some client wants to stop the server then change the variable ans stops the bucle. By example:
class YourServer {
private static boolean execute = true;
public static synchronized void stop() {
execute = false;
}
public void yourMethod() {
while(execute) {
// implement your server here
}
}
}
When a client send the command STOP you must be do
YourServer.stop();
If you want a stop command to stop the server you can call System.exit() to force the program to store or just closing server is likely to be all you need.
Looking into your problem, I understood one thing, that since you are putting
while (true), so your control always gets stuck at connection=server.accept(); listening for a new connection. So in order to stop the sockets you need to first find a way to stop looping in that while loop. Either you can set a Variable, like (int clientsConnected) to check the number of Clients, when that comes to zero stop that while loop. So you can stop your sockets.
Below is my sample code for clients which is doing the same thing for closing the Sockets.
Hopefully this solves your problem.
class GetNamesFromServer implements Runnable
{
private Socket sForName, sForId;
private BufferedReader in, inForName, inForId;
private PrintWriter outForName, outForId;
private static String clientNames;
public GetNamesFromServer(Socket s1, Socket s2)
{
sForName = s1;
sForId = s2;
}
public void run()
{
try
{
outForName = new PrintWriter(sForName.getOutputStream(), true);
outForName.println(Client.clientName);
System.out.println("Send Name : " + Client.clientName);
outForName.flush();
}
catch(IOException e)
{
System.err.println("Error sending Name to the Server.");
}
try
{
inForId = new BufferedReader(new InputStreamReader(sForId.getInputStream()));
Client.clientId = (inForId.readLine()).trim();
System.out.println("Client ID is : " + Client.clientId);
}
catch(IOException e)
{
System.err.println("Error Receiving ID from Server.");
}
try
{
inForName = new BufferedReader(new InputStreamReader(sForName.getInputStream()));
while (true)
{
clientNames = inForName.readLine();
if (clientNames != null && clientNames != "")
{
clientNames = clientNames.substring(1, clientNames.length() - 1);
System.out.println("Names Received : " + clientNames);
String[] names = clientNames.split(", ");
Client.nameClients.clear();
for (String element: names)
Client.nameClients.add(element);
Client.nPane.setText("");
int size = Client.nameClients.size();
System.out.println("Size of list : " + size);
for (int i = 0; i < size; i++)
{
String name = Client.nameClients.get(i);
String colour = Character.toString(name.charAt(0));
name = name.substring(1, name.length()) + "\n";
appendToNamePane(name, ReceiveMessages.getColour(Integer.parseInt(colour)), "Lucida Console");
}
System.out.println("Clients Online : " + Client.nameClients);
}
int index = Client.nameClients.indexOf(Client.clientId + Client.clientName);
**if (index == -1)
{
sForName.close();
break;
}**
}
}
catch(IOException e)
{
System.err.println("Error Receiving Names of Clients from Server");
}
}
NEW EDITION :
You can add a cap to maximum number of clients that can connect, once that reaches your while loop will not go to connection = server.accept(); and hence when they are done chatting (after some time) i.e. totalClients = 0, you can stop your sockets as well, to stop the program.
if (totalClients == 0)
{
socket.close();
serverSocket.close();
}
Regards

java TCP socket message breaks

i have a java client-server app in java, both using the same connection class that contains both send/receive messages.
for some reason, some of the messages i send are received in a malformed order:
here's the code
//set up
_in = new BufferedReader(new InputStreamReader(this._socket.getInputStream()));
_out = new BufferedWriter(new OutputStreamWriter(this._socket.getOutputStream()));
this._socket.setSoTimeout(S_TIMEOUT);
public synchronized boolean send(String message){
try {
_out.write(message);
_out.write(Connection.DELIMITER);
_out.flush();
return true;
} catch (IOException e) {
}
return false;
}
public String receive(){
int c;
try {
String message = "";
System.out.println("Getting message:");
c = _in.read();
while(c != -1 && c != Connection.DELIMITER) {
message += (char) c;
c = _in.read();
}
if (c == -1) {
return null;
}
return message;
} catch (IOException e) { }
return null;
}
some messages, for example "new_order" will might return with "ew_ord".
some characters are lost, others are sent separately. this seems odd as its TCP
could this be an encoding related issue?
Delimiter is (char) 0
socket timeout is 20000 (ie 20 senconds). every 10 seconds i send an empty message to make sure socket does not close
EDIT:
although it was solved using the Scanner, i must say that the original code worked fine for many messages/various machines for a very long time (a few weeks), and then suddenly failed to work with one specific message on one specific machine (other messages went through just fine). i've done socket data transfer in java MANY times and i've written many read/write methods to handle the sockets. it's the first time i ran into this.
although in the original code i set the encoding (in the posted code i didn't), i believe that the problem was encoding related. at one point, the message that was received had every second character missing. afterwards i changed it a bit, and the first/second character of the message were received in a separate message. from my understanding, it's either an encoding issue or some firewall/other security program that was running on the message sender machine, that decided to filter outgoing packets.
Try replacing your receive with a Scanner and let it do the work for you.
// in your setup
Scanner sc = new Scanner(_in).useDelimiter(Connection.DELIMETER);
public String receive() {
try {
return sc.next();
} catch(IOException e) {
return "";
}
}
For starters, I would make sure you're printing exceptions in those catch blocks.
Then, you're using the platform default encoding for converting characters to bytes. If these two processes are running on different machines, it's possible they're using different encodings. I would make sure you're specifying an encoding when you set up the Reader and Writer.
You can use UTF encoding for getting Full String of Message.
U can try this code and I am Sure About this code because i used it in My Chat Application.
String data=" ";
socket = new Socket("localhost",999);
while(true)
{
dos = new DataOutputStream(socket.getOutputStream());
dis = new DataInputStream(socket.getInputStream());
data = dis.readUTF();
jta.append(data +"\n");
}
Where jta is JTextArea.
It's for Client Side
Now For Server Side:
try
{
server = new ServerSocket(999);
Socket soc = server.accept();
while(true)
{
String data="";
try
{
dis = new DataInputStream(soc.getInputStream());
dos = new DataOutputStream(soc.getOutputStream());
data = dis.readUTF();
}
catch(Exception e)
{ }
jta.append(data + "\n");
}
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
System.exit(-1);
}

Categories

Resources