i am using a single port to receive both files and messages in a messenger app. i wanted to ask how can i differenntiate between the files and the messages? i have researched and found that m going to have to make a protoco, i tried but i cant seem to make one. is there any way to accomplish this?
right now i am receiving the connection like this :
public class ReceiveConnection extends Thread {
Socket clientSocket = null;
public ReceiveConnection(Socket socket)
{
this.clientSocket = socket;
SocketOperator.this.sockets.put(socket.getInetAddress(), socket);
}
#Override
public void run() {
try {
BufferedReader in = new BufferedReader(newInputStreamReader(clientSocket.getInputStream()));
while ((inputLine = in.readLine()) != null) {
if (inputLine.contains("TEXT") == true)
{
Log.i("SocketOP","text");
appManager.messageReceived(inputLine);
}
}
if (inputLine.contains("TEXT") == false)
{
InputStream is=clientSocket.getInputStream();
while(is!= null){
Log.i("SocketOP","filee");
appManager.fileReceived(is);
}
}
i have concatinated the string "TEXT" with my text message so it is coming through. putting an ELSE statement isnt working. how can i add a notifier with the file so that i know when the file is being received?
You should create a message header that contains the following data:
Starts with unique byte sequence (2-4 bytes long) that acts as a message separator. This should be a unique non-text sequence that is not very common ( e.g. not CRLF or other control sequence).
Contains message type byte, so that you know if this is text message or file.
Contains length of the message, which helps while extracting the message.
Related
I have a python server and about 10 android clients, using sockets. It is really important that when the server sends a message, all clients receive it at the same time (say 1/10th of a second of difference).
But the connection is over Wifi, and some devices get the message later than others, which gives a very messy result. I don't want to get the latency of every device because this is a very unreliable approach. I want something as accurate as possible.
For example, in FPS games, it is common to have a countdown at the start of the round, and every player can start playing at the same time. What kind of logic lies behind this?
As for what my code currently looks like:
I use a BufferedReader in android to read every line sent by the server. The server is a console application in which you can type a message, and when you press enter, every listed client receives it with a new thread for every client.
java method receiving messages:
private void readMessage() throws IOException {
String data;
while ((data = mBufferedReader.readLine()) != null) {
data = data.toUpperCase();
if (data.startsWith("POSITION")) {
String[] splitData = data.split("/");
Log.d(Constants.TAG, splitData[1]);
mMainActivity.setDevicePosition(Integer.parseInt(splitData[1]));
} else {
String message = data.substring(data.indexOf('/') + 1, data.length());
int devices = Integer.parseInt(data.substring(0, data.indexOf('/')));
if (message.length() >= devices) {
message += " ";
} else {
int difference = devices - message.length();
for (int i = 0; i < difference; i++) {
message += " ";
}
}
mMainActivity.printMessage(message);
}
}
}
python line :
for cl in clients_list:
start_new_thread(send_message_thread, (cl, message,))
Currently I am playing around with the new Smack 4.1 that offers full support for android. Sending and receiving messages is no problem, works fine. But now, I get stuck on sending and receiving Files. For example:
Send File
public void sendFile(String fileName,String to){
if(transferManager==null) {
transferManager = FileTransferManager.getInstanceFor(mConnection);
}
OutgoingFileTransfer transfer = transferManager.createOutgoingFileTransfer(to);
try {
transfer.sendFile(new File(fileName), "This is a Test!");
} catch (SmackException e) {
e.printStackTrace();
}
}
Receive files
public void setReceiveFileListener(){
if(transferManager==null) {
transferManager = FileTransferManager.getInstanceFor(mConnection);
}
transferManager.addFileTransferListener(new FileTransferListener() {
#Override
public void fileTransferRequest(FileTransferRequest request) {
IncomingFileTransfer transfer = request.accept();
try {
File file = new File(Environment.getExternalStorageDirectory() + File.separator +"TEST"+File.separator+ "new.txt");
transfer.recieveFile(file);
} catch (SmackException | IOException e) {
e.printStackTrace();
}
}
});
}
In this scenario, I just send a text file and want to save it as a new text file at sd card as "new.txt". The problem is not with the file itself, the file exists. Also, the receiver id is correct, it´s like "user#host.com/Smack" . That´s the same receiver which I can send normal messages successfully.
The error code I get from Smack:
error 503 - service unavailable
This error respone I get directly from smack after sending file and it seems the file is not transferred, because the receiver listener shows nothing (I made Logs on both, sending and receiving), but listener is surely registerred.
But I am totally sure, that File Transfer is supported by the server, it´s declared at their website www.coderollers.com . I´ve read many questions about here in SO and also at the Smack Developer Community. Nothing helped, so my question here is:
What could be the cause of this problem?
Could it be because I have to change the Port?
How to change the port of an existing connection in smack?
Any alternatives to file transport?
My normal port is 5222, that works fine to send messages. I hope someone get it work and can lead me to the right directions....thanks for helping!
SOLUTION
For all who are interested: Devendra Singhs answer is correct, with a little note by myself. It seems to be important, which resource is used. From mobile to mobile, You have to use "mobile". Wether "Smack" nor any other resource identifier would work here. So it is important to initialize the OutgoingFileTransfer correctly like this:
OutgoingFileTransfer oft = ftm1.createOutgoingFileTransfer
(XmppStringUtils.completeJidFrom(USER, SERV, "mobile"));//important resource "mobile"
I got it after very research.
FileTransferManager ftm1 = FileTransferManager.getInstanceFor(connection);
FileTransferManager ftm2 = FileTransferManager.getInstanceFor(connection2);
ftm2.addFileTransferListener(new FileTransferListener() {
#Override
public void fileTransferRequest(FileTransferRequest request) {
IncomingFileTransfer ift = request.accept();
try {
InputStream is = ift.recieveFile();
ByteArrayOutputStream os = new ByteArrayOutputStream();
int nRead;
byte[] buf = new byte[1024];
while ((nRead = is.read(buf, 0, buf.length)) != -1) {
os.write(buf, 0, nRead);
}
os.flush();
dataReceived = os.toByteArray();
} catch (SmackException | IOException | XMPPErrorException e) {
e.printStackTrace();
}
if (Arrays.equals(dataToSend, dataReceived)) {
System.out.println("Received data matches send data. \\o/");
} else {
System.err.println("Recieved data DOES NOT match send data. :(");
}
}
});
OutgoingFileTransfer oft = ftm1.createOutgoingFileTransfer(XmppStringUtils.completeJidFrom(USER, SERV, "resourse"));
oft.sendStream(new ByteArrayInputStream(dataToSend), "hello.txt", dataToSend.length, "A greeting");
outerloop: while (!oft.isDone()) {
switch (oft.getStatus()) {
case error:
System.out.println("Filetransfer error: " + oft.getError());
break outerloop;
default:
System.out.println("Filetransfer status: " + oft.getStatus() + ". Progress: " + oft.getProgress());
break;
}
Thread.sleep(1000);
}
connection.disconnect();
connection2.disconnect();
Thread.sleep(1000);
}
the one connection is sending file and another connection is receiving this is working code.
I had same problem, I investigated the stanza and solved it this way.
Many people use "/Smack" or "/Resource" as resource part in jid, but that can be done another way.
Resource path is changing with every presence changed of user. Lets say we want to send image to this user:
"user1#mydomain"
You must add "/Resource" or "/Smack" part to this jid and it become this:
user1#mydomain/Resource
But resource path is changing with presence so you must follow every presence change to update resource path.
Best way is to get user presence is in roster listener and in presencheChanged() method you get last user resource part like this:
Roster roster=getRoster();
roster.addRosterListener(new RosterListener() {
#Override
public void entriesAdded(Collection<Jid> addresses) {
Log.d("entriesAdded", "ug");
context.sendBroadcast(new Intent("ENTRIES_ADDED"));
}
#Override
public void entriesUpdated(Collection<Jid> addresses) {
Log.d("entriesUpdated", "ug");
}
#Override
public void entriesDeleted(Collection<Jid> addresses) {
Log.d("entriesDeleted", "ug");
}
#Override
public void presenceChanged(Presence presence) {
Log.d("presenceChanged", "ug");
//Resource from presence
String resource = presence.getFrom().getResourceOrEmpty().toString();
//Update resource part for user in DB or preferences
//...
}
});
}
Resource string will be some generated string like "6u1613j3kv" and jid will become:
user1#mydomain/6u1613j3kv
That means that you must create your outgoing transfer like this:
EntityFullJid jid = JidCreate.entityFullFrom("user1#mydomain/6u1613j3kv");
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(jid)
In your case this resource part is "mobile". Just change it with resource part from presence in roster listener.
And that is how i have solved my problem with file transfer on smack and Openfire.
Also to mention you must add following properties in your Openfire server:
xmpp.proxy.enabled - true
xmpp.proxy.externalip - MY_IP_ADDRESS
xmpp.proxy.port -7777
Just to mention, I am using Openfire 4.0.2 and Smack 4.2.2.
Also this can be configured the easy way, just set the resource on
XMPPTCPConnectionConfiguration.Builder .
like
XMPPTCPConnectionConfiguration.Builder configurationBuilder =
XMPPTCPConnectionConfiguration.builder();
configurationBuilder.setResource("yourResourceName");
I want to send UTF-8 text that is stored with ElasticSeach to an application via sockets.
I have a ThreadedTCPServer Implemented, here is the class that should handle the reply.
I have implemented basic string based handshaking to share some info like query was sent and that response will be sent.
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
es = Elasticsearch()
#receive query from the client
query = self.request.recv(1024)
#Cut off the characters that aren't recognized
query=query[2:]
#for testing
query=query.lower().strip().replace(' ','_')
print query
#Send response that query was received
self.request.send("200...OK\n")
res = es.search(index="painters",body={"query": { "match" : {"title" : query}},"size":1 })
if res['hits']['hits']:
response = res['hits']['hits'][0]['_source']['text']
self.request.send("201...RE\n")
print response
response=response.encode('utf-8')
self.request.sendall(response)
On the android side I have two functions one for reading responses and one for reading bytes.
private String getResponse(InputStream is){
String line="";
BufferedReader rd = new BufferedReader(new InputStreamReader(is),8);
try{
line=rd.readLine();
}
catch (Exception e){
Toast.makeText(MainActivity.this, "Stream Exception", Toast.LENGTH_SHORT).show();
}
return line;
}
private String convertStreamToString(InputStream is) {
BufferedInputStream bi = new BufferedInputStream(is);
byte[] b = new byte[1024];
StringBuilder total = new StringBuilder();
try {
while (bi.read(b,0,1024)!=-1)
{
total.append(decodeUTF8(b));
Log.d("TOTAL",decodeUTF8(b));
}
} catch (Exception e) {
e.printStackTrace();
}
return total.toString();
}
And here is the function that should decode the string:
String decodeUTF8(byte[] bytes) {
return new String(bytes, UTF8_CHARSET);
}
Problem is Sometimes not the whole string is shown on the android Side,
and when the whole thing goes through some UTF-8 Characters end up deformed (totally different character than sent)
AsyncTask post execute that starts new Activty:
protected void onPostExecute(String s) {
//super.onPostExecute(s);
if (s.contains("ECONNREFUSED")){
Toast.makeText(MainActivity.this,"Connection Failed",Toast.LENGTH_LONG).show();
return;
}
Intent intent = new Intent(MainActivity.this,ReplyActivity.class);
intent.putExtra(EXTRA_MESSAGE,s);
startActivity(intent);
}
New Intent getting the string:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//get message
Intent intent = getIntent();
String summary = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
Example ouput:
Early life (1928–1949)
Andy Warhol ("né" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889–1942) and Júlia ("née" Zavacká, 1892–1972), w
As you can see even when sending the query from android to python I get some crap that I need to cut off.
here:
#Cut off the characters that aren't recognized
query=query[2:]
repr(response):
<h2>Early life (1928\xe2\x80\x931949)</h2>\nAndy Warhol ("n\xc3\xa9" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889\xe2\x80\x931942) and J\xc3\xbalia ("n\xc3\xa9e" Zavack\xc3\xa1, 1892\xe2\x80\x931972), whose first child was born in their homeland and died before their move to the U.S.
Terminal print:
<h2>Early life (1928–1949)</h2>
Andy Warhol ("né" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889–1942) and Júlia ("née" Zavacká, 1892–1972), whose first child was born in their homeland and died before their move to the U.S.
Is there a way to programmatically configure an Android application to filter the log messages sent to logcat? I do understand, that logcat can be configured to filter out stuff, but I want to do the same inside the Android application.
Use case - I am actually using robolectric for my test cases, that I can run directly on my host machine and not on an emulator. This is actually extremely convenient for non-GUI stuff. Some of my code emits Android logs. I cannot attach logcat to see the output of that. I can redirect logs to regular stdout. But at this point I don't have filtering, so it's either grep or similar or sieving through thousands of lines of irrelevant stuff.
That's what I did:
public class CustomPrintStream extends PrintStream {
private String regexFilter;
private Pattern pattern;
public IonPrintStream(#NonNull File file) throws FileNotFoundException {
super(file);
}
public String getRegexFilter() {
return regexFilter;
}
public void setRegexFilter(String regexFilter) {
this.regexFilter = regexFilter;
if (regexFilter != null && !regexFilter.isEmpty()) {
pattern = Pattern.compile(regexFilter);
} else {
pattern = null;
}
}
#Override
public void println(String x) {
if (x != null && pattern != null && !pattern.matcher(x).find()) {
return;
}
System.out.println(x);
}
}
Usage on Robolectric (you can do it on your #Before method):
File file = new File("log.txt");
if (!file.exists() && !file.createNewFile()) {
throw new RuntimeException("Log file could not be created");
}
CustomPrintStream printStream = new CustomPrintStream(file);
printStream.setRegexFilter("[your regex here]");
ShadowLog.stream = printStream;
In your case, as you don't want to show some logs, you could filter something like this:
//I don't want to log CursorWindowStats and SQLiteCursor tags:
printStream.setRegexFilter("^((?!CursorWindowStats|SQLiteCursor).)*$");
I successed connection bitween client in android and server.
but, when I want to send message like "hello" or whatever, message was disappear.
this is my client code:
group = new OioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group);
b.channel(OioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(handler);
}
});
Channel ch = null;
ChannelFuture f = null;
try {
f = b.connect(new InetSocketAddress(host, port)).sync();
ch = f.channel();
} catch (InterruptedException e) {
e.printStackTrace();
}
ch.writeAndFlush("hello!");
and this is my server code:
#Override
public void channelActive(ChannelHandlerContext ctx){
channels.add(ctx.channel());
ctx.channel().writeAndFlush("Welcome My Server");
System.out.println(ctx.channel().remoteAddress());
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) {
System.out.print((char) in.readByte());
System.out.flush();
}
} finally {
ReferenceCountUtil.release(msg);
}
}
when I connect, Server was printing 'connected client ip address'
but after that, 'hello' message is not printed in my server.
what is wrong? server? client?
I think encode, decode is not problem, cuz nothing received
please let me know how to do for that?
If you want to write a String you need to put StringEncoder in the ChannelPipeline (on the client side). If you check the returned ChannelFuture of writeAndFlush(...) you will see it was failed.