Bluetooth-connection; can't send strings properly
Asked Answered
B

1

6

I have troubles with my program when i need to send Strings from my server bluetooth-socket to my client bluetooth-socket. Everything works fine as long as I am only sending one String at a time (for example chatting) but if I need to write more Strings at a short period of time (to interchange informations), the Strings will not get seperated from the client code. For example if I'm sending "FirstUser" and right after that "SecondUser" the client does not read "FirstUser" and then "SecondUser". It will read "FirstUserSecondUser". How can I avoid this behaviour?

Edit: If I let the Thread sleep before it is able to send a new message, it reads the right strings but this solution is not working fine for my need.

Server-Code: sending to all clients(edited)

   public synchronized void sendToAll(String message)
{   
    try {
        Thread.sleep(100);
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }

    publishProgress(message);
    for(OutputStream writer:outputList) {
        try {
            writer.write(message.getBytes());
            writer.flush();
        } catch (IOException e) {
            System.out.println("Some-Error-Code");
        }
    }
}

Server-Code: reading from a client:

   public void run() {
    String nachricht;
    int numRead;
    byte[] buffer = new byte[1024];
    while (runningFlag) 
    {
        try {
            if((numRead = inputStream.read(buffer)) >= 0) { 
                nachricht = new String(buffer, 0, numRead);
                serverThread.handleMessage(nachricht); 
            }
            }
             catch (IOException e) {
                this.cancel();
                e.printStackTrace();
            }
    } 
} 

Client-Code: reading from server(edited)

@Override
    protected Void doInBackground(Integer... ints) {    
        String nachricht = new String();
        byte[] buffer = new byte[1024];
        int numRead;
        while (runningFlag) 
        {
            try {
                if(((numRead = inputStream.read(buffer)) >= 0)) { 
                    nachricht = new String(buffer, 0, numRead);
                    publishProgress(nachricht);
                }
            }
             catch (IOException e) {
                clientGame.finish();
                e.printStackTrace();
            }                      
        }
        return null;
}

Client-Code: writing to server

public synchronized void write(String nachricht)
    {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        try {
            outputStream.write(nachricht.getBytes());
            outputStream.flush();
        } catch (IOException e) {
            this.cancel();
            e.printStackTrace();
        }
    }

I appreciate every little help :) .

Blakney answered 6/1, 2012 at 0:13 Comment(13)
Does your server send a message to the clients or vice-versa?Stringer
Both ways 'round. I temporary "solved" the problem with Thread.sleep(100) before the client or server is able to send another string. But that's not really a neat solution.Blakney
are you flushing all sent messages?Stringer
yes i do. It seems like it writes the messages so fast that the read() function only sees one incoming string.Blakney
It is not a human, it is a machine. There isn't a thing called "so fast". I can't help you with the info you have given so far. Research about how the streams work and Make sure you're flushing everything. Maybe, post some more code too.Stringer
I added some more code. I still can't see why it does not seperate the Strings without a sleep().Blakney
How many servers do you have?Stringer
I will think of a solution, I think I might have something in mind. I will get back to you before monday unless someone else answersStringer
Ok, thanks. I will also keep on searching.Blakney
I had this same problem. In my case, sometimes strings were split between sends. It seems like flushing the Java stream doesn't do what it's supposed to, so I ended up having to treat the stream like an old-school stream. Every time I send, I had to pad the beginning 4 bytes with nulls (0x00), the next 4 bytes with how many valid bytes were being sent, then pad the last four bytes with nulls. On the receiving end, parse out the pattern. I'm not going to put this as official answer since I (hope) there's a better way, but it may be what you have to do.Crafty
@Crafty this sounds really complicated. Isn't it possible just to add a seperator after each message and split them after read()? Adel Boutros: Did you find a solution?Blakney
I suppose it would work in this case since you're working with text. My case was different as there was no way of determining what a separator was vs. actual data.Crafty
I'm such an idiot! now i know why it does not work android api:" flush(): Flushes this stream. Implementations of this method should ensure that any buffered data is written out. This implementation does nothing." ... flush() does only work wirth bufferedstreams! oh my god... is it possible to use bufferedstreams with bluetooth?Blakney
S
8

You need to encapsulate your data item to avoid concatenation. It means that you have to write and read a whole data item before continuing.

You should have some utility methods to do that instead of directly using methods of OutputStream and InputStream :

public static void writeItem(OutputStream out, String s) throws IOException
{
    // Get the array of bytes for the string item:
    byte[] bs = s.getBytes(); // as bytes
    // Encapsulate by sending first the total length on 4 bytes :
    //   - bits 7..0 of length
    out.write(bs.length);      // modulo 256 done by write method
    //   - bits 15..8 of length
    out.write(bs.length>>>8);  // modulo 256 done by write method
    //   - bits 23..16 of length
    out.write(bs.length>>>16); // modulo 256 done by write method
    //   - bits 31..24 of length
    out.write(bs.length>>>24); // modulo 256 done by write method
    // Write the array content now:
    out.write(bs); // Send the bytes
    out.flush();
}

public static String readItem(InputStream in) throws IOException
{
    // first, read the total length on 4 bytes
    //  - if first byte is missing, end of stream reached
    int len = in.read(); // 1 byte
    if (len<0) throw new IOException("end of stream");
    //  - the other 3 bytes of length are mandatory
    for(int i=1;i<4;i++) // need 3 more bytes:
    {
        int n = in.read();
        if (n<0) throw new IOException("partial data");
        len |= n << (i<<3); // shift by 8,16,24
    }
    // Create the array to receive len bytes:
    byte[] bs = new byte[len];
    // Read the len bytes into the created array
    int ofs = 0;
    while (len>0) // while there is some byte to read
    {
        int n = in.read(bs, ofs, len); // number of bytes actually read
        if (n<0) throw new IOException("partial data");
        ofs += n; // update offset
        len -= n; // update remaining number of bytes to read
    }
    // Transform bytes into String item:
    return new String(bs);
}

Then you use these methods both for server & client to read and write your String items.

Sidekick answered 13/1, 2012 at 15:15 Comment(10)
About the documentation of OutputStream.flush() saying "This implementation does nothing." it is normal because OutputStream is only an abstract class. The actual class (FileOutputStream, SocketOutputStream, ...) override this method to do something.Sidekick
I can't understand your code. Can you please try to explain it? for example I have never seen the '>>>' and the '|=' operators before. And why is there a concatenation? Why does flush() does not solve the problem?Blakney
The flush() cannot warranty the time you receive data because you're using socket, and there is multiple intermediate buffers between OSI layers and systems.Sidekick
The >>> operator is shifting bits to the right, without copying sign bit. Whereas >> would copy sign bit. Ex: 1000..0100 >> 2 == 111000..01 Whereas 1000..0100 >>> 2 == 001000..01Sidekick
A |= B is a shortcut for A = A | B (the or operator).Sidekick
the read() method of sockets and any input stream can return less bytes than requested (except for read() with no parameters which wait until one byte is available). So your current code may read random number of bytes (getting random String items) depending on network current load, system buffer sizes...Sidekick
hmm okey... I still does not understand it on the whole but I will give your code a try tomorrow and reply if it works or not.Blakney
The flush() method is needed only to send the data remaining in system buffer to the output stream so the receiver can read it. But the receiver need to use a loop to fully read the buffer otherwise it get partial data.Sidekick
I tried your code today and it seemed to worked fine. I will test it more the following days and report my experiences with it. Thanks mate :)Blakney
I don't know why I have -1 for this proposition which seems to solve the problem and still not selected as the solution...Sidekick

© 2022 - 2024 — McMap. All rights reserved.