The idea here is to make a socket connection over the local network, send some data, and close the connection immediately. The only thing that should remain running is the serversocket. So far, everything works as expected except one thing:
When the activity that starts the serversocket gets closed with the back button, and then reopened, the data being sent from the client no longer makes it to the serversocket.
DMActivity:
public class DMActivity extends AppCompatActivity {
String ipAddress;
boolean dmListenRunning = false;
DMListen dmListen = new DMListen();
Thread dmListenThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dm);
if (!(dmListenRunning)) {
dmListenThread = new Thread(dmListen);
dmListenRunning = true;
dmListenThread.start();
}
}
#Override
public void onBackPressed()
{
try {
dmListen.KillThread(true);
dmListenThread.interrupt();
}
catch (Exception e)
{
Log.i("LOG", e.toString());
}
finish();
}
}
DMListen:
public class DMListen implements Runnable {
ServerSocket serverSocket;
boolean started = false;
boolean closeSockets = false;
boolean killed = false;
public void run() {
ReceivePlayerData();
}
public void ReceivePlayerData() {
try {
while (!(Thread.interrupted())) {
if (!(started)) {
int port = 8080;
serverSocket = new ServerSocket(port);
started = true;
}
Socket clientSocket = serverSocket.accept();
DataInputStream dataIn = new DataInputStream(clientSocket.getInputStream());
String name;
int init;
name = dataIn.readUTF();
init = dataIn.readInt();
dataIn.close();
clientSocket.close();
}
}
catch (Exception e) {
Log.i("LOG", e.toString();
}
if(killed)
{
try {
serverSocket.close();
if (Thread.currentThread().isInterrupted())
{
try {
Thread.currentThread().join();
}
catch (Exception e)
{
Log.i("LOG", e.toString());
}
}
}
catch(IOException e)
{
Log.i("LOG", e.toString());
}
}
}
public void KillThread(boolean k)
{
boolean killed = k;
}
}
PlayerActivity:
public class PlayerActivity extends AppCompatActivity {
EditText hostIPBox;
Button submit;
String hostIPString;
InetAddress hostIP;
boolean denied = false;
boolean started = false;
PlayerConnect playerConnect;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
hostIPBox = (EditText) findViewById(R.id.ipaddress);
hostIPBox.setRawInputType(Configuration.KEYBOARD_12KEY);
hostIPBox.setSingleLine();
submit = (Button) findViewById(R.id.submit);
submit.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
hostIPString = hostIPBox.getText().toString();
try {
if (!(hostIPString.equals(""))) {
hostIP = InetAddress.getByName(hostIPString);
if(!(started)) {
playerConnect = new PlayerConnect();
playerConnect.SetHostIP(hostIP);
denied = playerConnect.GetDenied();
started = true;
}
else {
playerConnect.SetHostIP(hostIP);
denied = playerConnect.GetDenied();
}
if (denied)
{
started = false;
}
else
{
new Thread(playerConnect).start();
}
}
}
catch (Exception e) {
Log.e("LOG", e.toString());
}
}
});
}
}
PlayerConnect:
public class PlayerConnect implements Runnable {
InetAddress hostIP;
boolean closeSockets = false;
boolean denied = false;
public void run() {
SendPlayerData(hostIP, playerName, playerInitiative);
}
private void SendPlayerData(InetAddress IP, String name, int init) {
try {
int port = 8080;
Socket socket = new Socket();
socket.connect(new InetSocketAddress(IP, port), 3000);
DataOutputStream output = new DataOutputStream(socket.getOutputStream());
if (socket.isConnected())
{
output.writeUTF(name);
output.writeInt(init);
output.close();
socket.close();
}
if (closeSockets) {
output.close();
socket.close();
closeSockets = false;
}
}
catch (Exception e) {
denied = true;
Log.i("LOG", e.printStackTrace());
}
}
public void SetHostIP(InetAddress host)
{
hostIP = host;
}
public boolean GetDenied()
{
return denied;
}
}
You should use Service to handle socket connection. You won't have problems with Activity lifecycle.
Related
I have a PlayerActivity class, and a PlayerConnect class.
Previously, I used PlayerActivity class to start a thread that would run PlayerConnect. I recently found that the thread misbehaves a little, and have been recommended to use services (for this: IntentServices).
Problem: My logs tell me that the IP, name, and init are null, null, 0, which should not be the case as they are supposed to be set before the point of starting the service. Logs also tell me that it's failing to connect to the localhost, which I think it's defaulting to because it doesn't have a set IP to try.
Am I correctly referencing the PlayerConnect class in my Intent?
PlayerActivity:
public class PlayerActivity extends AppCompatActivity {
InetAddress hostIP;
String playerName;
int playerInitiative = -1;
boolean denied = false;
boolean started = false;
PlayerConnect playerConnect;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
submit = (Button) findViewById(R.id.submit);
submit.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (!(playerName.equals("")) && playerInitiative > -1) {
try {
if (!(hostIPString.equals(""))) {
hostIP = InetAddress.getByName(hostIPString);
if(!(started)) {
playerConnect = new PlayerConnect();
playerConnect.SetHostIP(hostIP);
playerConnect.SetPlayerName(playerName);
playerConnect.SetPlayerInit(playerInitiative);
denied = playerConnect.GetDenied();
started = true;
}
else {
playerConnect.SetHostIP(hostIP);
playerConnect.SetPlayerName(playerName);
playerConnect.SetPlayerInit(playerInitiative);
denied = playerConnect.GetDenied();
}
if (denied)
{
started = false;
}
else
{
Intent playerConnectIntent =
new Intent(PlayerActivity.this, playerConnect.getClass());
startService(playerConnectIntent);
}
}
}
catch (Exception e) {
Log.i("LOG", e.toString());
}
}
}
});
}
}
PlayerConnect:
public class PlayerConnect extends IntentService {
public PlayerConnect() {
super("PlayerConnect");
}
InetAddress hostIP;
String playerName;
int playerInitiative;
boolean denied = false;
#Override
public void onHandleIntent(Intent intent) {
SendPlayerData(hostIP, playerName, playerInitiative);
}
private void SendPlayerData(InetAddress IP, String name, int init) {
try {
int port = 8080;
Socket socket = new Socket();
socket.connect(new InetSocketAddress(IP, port), 3000);
DataOutputStream output = new DataOutputStream(socket.getOutputStream());
if (socket.isConnected())
{
output.writeUTF(name);
output.writeInt(init);
output.close();
socket.close();
Log.i("LOG", "client socket connected");
}
if (socket.isClosed())
{
stopSelf();
Log.i("LOG", "client socket closed");
}
}
catch (Exception e) {
denied = true;
Log.i("LOG", e.toString());
Log.i("LOG", IP + name + init);
}
}
public void SetHostIP(InetAddress host)
{
hostIP = host;
}
public void SetPlayerName(String name)
{
playerName = name;
}
public void SetPlayerInit(int init)
{
playerInitiative = init;
}
public boolean GetDenied()
{
return denied;
}
}
greeble31 Your comment helped me a lot, so I'm making it the answer for future readers. Thank you.
No, you never want to construct a Service subclass yourself (new PlayerConnect()). That's the system's job. The one it gives you in response to a startService() call will be completely different from the one you get back from the constructor, so it won't have any of the data you added to it. You need to add your data to playerConnectIntent, and retrieve it in onHandleIntent().
Hai friends i am new in Socket.
Now I implement a Chat app using Socket. I am testing with single Server SA and Client CA and CB. The Socket connection, chat and disconnect are working good.
But my problem is when client(CA) disconnect from server socket server not getting instant notification. Once CA reconnect again with new name CAA server will notify CA removed, and CAA connected.
my point is server must get instant notification when client CA disconnect socket.
This is my server Activity:
public class ServerMain extends ActionBarActivity {
static final int SocketServerPORT = 8080;
TextView infoIp, infoPort, chatMsg;
String msgLog = "";
List<ChatClient> userList;
ServerSocket serverSocket;
private Timer mTimer1;
private TimerTask mTt1;
private Handler mTimerHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
infoIp = (TextView) findViewById(R.id.infoip);
infoPort = (TextView) findViewById(R.id.infoport);
chatMsg = (TextView) findViewById(R.id.chatmsg);
infoIp.setText(getIpAddress());
userList = new ArrayList<ChatClient>();
ChatServerThread chatServerThread = new ChatServerThread();
chatServerThread.start();
}
#Override
protected void onDestroy() {
super.onDestroy();
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ChatServerThread extends Thread {
#Override
public void run() {
Socket socket = null;
try {
serverSocket = new ServerSocket(SocketServerPORT);
ServerMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
infoPort.setText("I'm waiting here: "
+ serverSocket.getLocalPort());
}
});
while (true) {
socket = serverSocket.accept();
ChatClient client = new ChatClient();
userList.add(client);
ConnectThread connectThread = new ConnectThread(client, socket);
connectThread.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
private class ConnectThread extends Thread {
Socket socket;
ChatClient connectClient;
String msgToSend = "";
ConnectThread(ChatClient client, Socket socket) {
connectClient = client;
this.socket = socket;
client.socket = socket;
client.chatThread = this;
}
#Override
public void run() {
DataInputStream dataInputStream = null;
DataOutputStream dataOutputStream = null;
try {
dataInputStream = new DataInputStream(socket.getInputStream());
dataOutputStream = new DataOutputStream(socket.getOutputStream());
String n = dataInputStream.readUTF();
connectClient.name = n;
msgLog += connectClient.name + " connected#" +
connectClient.socket.getInetAddress() +
":" + connectClient.socket.getPort() + "\n";
ServerMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
chatMsg.setText(msgLog);
}
});
dataOutputStream.writeUTF("Welcome " + n + "\n");
dataOutputStream.flush();
broadcastMsg(n + " join our chat.\n");
while (true) {
if (dataInputStream.available() > 0) {
String newMsg = dataInputStream.readUTF();
msgLog += n + ": " + newMsg;
ServerMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
chatMsg.setText(msgLog);
}
});
broadcastMsg(n + ": " + newMsg);
}
if (!msgToSend.equals("")) {
dataOutputStream.writeUTF(msgToSend);
dataOutputStream.flush();
msgToSend = "";
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dataInputStream != null) {
try {
dataInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (dataOutputStream != null) {
try {
dataOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
userList.remove(connectClient);
ServerMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
if (connectClient.socket.isClosed()) {
Toast.makeText(ServerMain.this,
connectClient.name + " removed.", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(ServerMain.this,
"not removed", Toast.LENGTH_LONG).show();
}
Toast.makeText(ServerMain.this,
connectClient.name + " removed.", Toast.LENGTH_LONG).show();
msgLog += "-- " + connectClient.name + " leaved\n";
ServerMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
chatMsg.setText(msgLog);
}
});
broadcastMsg("-- " + connectClient.name + " leaved\n");
}
});
}
}
private void sendMsg(String msg) {
msgToSend = msg;
}
}
private void broadcastMsg(String msg) {
for (int i = 0; i < userList.size(); i++) {
userList.get(i).chatThread.sendMsg(msg);
msgLog += "- send to " + userList.get(i).name + "\n";
}
ServerMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
chatMsg.setText(msgLog);
}
});
}
private String getIpAddress() {
String ip = "";
try {
Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
.getNetworkInterfaces();
while (enumNetworkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = enumNetworkInterfaces
.nextElement();
Enumeration<InetAddress> enumInetAddress = networkInterface
.getInetAddresses();
while (enumInetAddress.hasMoreElements()) {
InetAddress inetAddress = enumInetAddress.nextElement();
if (inetAddress.isSiteLocalAddress()) {
ip += "SiteLocalAddress: "
+ inetAddress.getHostAddress() + "\n";
}
}
}
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
ip += "Something Wrong! " + e.toString() + "\n";
}
return ip;
}
class ChatClient {
String name;
Socket socket;
ConnectThread chatThread;
}
}
This is my Client Activity:
public class ClientMain extends ActionBarActivity {
static final int SocketServerPORT = 8080;
LinearLayout loginPanel, chatPanel;
EditText editTextUserName, editTextAddress;
Button buttonConnect;
TextView chatMsg, textPort;
EditText editTextSay;
Button buttonSend;
Button buttonDisconnect;
String msgLog = "";
Socket socket = null;
ChatClientThread chatClientThread = null;
DataOutputStream dataOutputStream = null;
DataInputStream dataInputStream = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loginPanel = (LinearLayout) findViewById(R.id.loginpanel);
chatPanel = (LinearLayout) findViewById(R.id.chatpanel);
editTextUserName = (EditText) findViewById(R.id.username);
editTextAddress = (EditText) findViewById(R.id.address);
textPort = (TextView) findViewById(R.id.port);
textPort.setText("port: " + SocketServerPORT);
buttonConnect = (Button) findViewById(R.id.connect);
buttonDisconnect = (Button) findViewById(R.id.disconnect);
chatMsg = (TextView) findViewById(R.id.chatmsg);
buttonConnect.setOnClickListener(buttonConnectOnClickListener);
buttonDisconnect.setOnClickListener(buttonDisconnectOnClickListener);
editTextSay = (EditText) findViewById(R.id.say);
buttonSend = (Button) findViewById(R.id.send);
buttonSend.setOnClickListener(buttonSendOnClickListener);
}
OnClickListener buttonDisconnectOnClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
if (chatClientThread == null) {
Log.e("chatClientThread", "null");
return;
} else {
chatClientThread.disconnect();
}
}
};
OnClickListener buttonSendOnClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
if (editTextSay.getText().toString().equals("")) {
return;
}
if (chatClientThread == null) {
return;
}
chatClientThread.sendMsg(editTextSay.getText().toString() + "\n");
}
};
OnClickListener buttonConnectOnClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
String textUserName = editTextUserName.getText().toString();
if (textUserName.equals("")) {
Toast.makeText(ClientMain.this, "Enter User Name",
Toast.LENGTH_LONG).show();
return;
}
String textAddress = editTextAddress.getText().toString();
if (textAddress.equals("")) {
Toast.makeText(ClientMain.this, "Enter Addresse",
Toast.LENGTH_LONG).show();
return;
}
msgLog = "";
chatMsg.setText(msgLog);
loginPanel.setVisibility(View.GONE);
chatPanel.setVisibility(View.VISIBLE);
chatClientThread = new ChatClientThread(
textUserName, textAddress, SocketServerPORT);
chatClientThread.start();
}
};
private class ChatClientThread extends Thread {
String name;
String dstAddress;
int dstPort;
String msgToSend = "";
boolean goOut = false;
ChatClientThread(String name, String address, int port) {
this.name = name;
dstAddress = address;
dstPort = port;
}
#Override
public void run() {
try {
socket = new Socket(dstAddress, dstPort);
dataOutputStream = new DataOutputStream(
socket.getOutputStream());
dataInputStream = new DataInputStream(socket.getInputStream());
dataOutputStream.writeUTF(name);
dataOutputStream.flush();
while (!goOut) {
if (dataInputStream.available() > 0) {
msgLog += dataInputStream.readUTF();
ClientMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
chatMsg.setText(msgLog);
}
});
}
if (!msgToSend.equals("")) {
dataOutputStream.writeUTF(msgToSend);
dataOutputStream.flush();
msgToSend = "";
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
final String eString = e.toString();
ClientMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(ClientMain.this, eString, Toast.LENGTH_LONG).show();
}
});
} catch (IOException e) {
e.printStackTrace();
final String eString = e.toString();
ClientMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(ClientMain.this, eString, Toast.LENGTH_LONG).show();
}
});
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (dataOutputStream != null) {
try {
dataOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (dataInputStream != null) {
try {
dataInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
ClientMain.this.runOnUiThread(new Runnable() {
#Override
public void run() {
loginPanel.setVisibility(View.VISIBLE);
chatPanel.setVisibility(View.GONE);
}
});
}
}
private void sendMsg(String msg) {
msgToSend = msg;
}
private void disconnect() {
goOut = true;
}
}
}
I have simple Java server:
public class Main {
public static void main(String[] args) throws IOException {
System.out.println("Welcome to Server side");
BufferedReader in;
PrintWriter out;
ServerSocket servers = null;
Socket fromClient = null;
// create server socket
try {
servers = new ServerSocket(4444);
} catch (IOException e) {
System.out.println("Couldn't listen to port 4444");
System.exit(-1);
}
try {
System.out.print("Waiting for a client...");
fromClient = servers.accept();
System.out.println("Client connected");
} catch (IOException e) {
System.out.println("Can't accept");
System.exit(-1);
}
in = new BufferedReader(new InputStreamReader(fromClient.getInputStream()));
out = new PrintWriter(fromClient.getOutputStream(), true);
String input;
System.out.println("Wait for messages");
while ((input = in.readLine()) != null) {
if (input.equalsIgnoreCase("exit")) break;
out.println("S ::: " + input);
System.out.println(input);
}
out.close();
in.close();
fromClient.close();
servers.close();
}
}
and simple Android client:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText) findViewById(R.id.editText);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
new MyAsync().execute();
}
---------------------
public class MyAsync extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
try {
fromServer = new Socket("192.168.0.103",4444);
in = new BufferedReader(new InputStreamReader(fromServer.getInputStream()));
out = new PrintWriter(fromServer.getOutputStream(), true);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
-------------------
#Override
public void onClick(View v) {
if (v.getId() == R.id.button){
out.println(editText.getText().toString());
}
}
All work good. I send message from Android to server and sever print this message in console. But I want send Object, for example User:
public class User {
private int age;
private String fio;
public User() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getFio() {
return fio;
}
public void setFio(String fio) {
this.fio = fio;
}
In Android i can write:
User user = new User();
out.print(user);
But i am not understanding that how can i read this on server side?
You can't do it with print(). Use an ObjectOutputStream.writeObject() at the sender, and ObjectInputStream.readObject() at the receiver. You will need to adjust your User class to implement Serializable and provide a private static long serialVersionUID value.
NB Don't write code like this. Code that depends on the success of code in a try block should be inside the same try block.
Using this great tutorial by the Java Code Geeks, I am easily able to create a client activity that sends data via TCP to a server's port 4000 using the following code:
public class Client extends Activity {
private Socket socket;
private static final int SERVERPORT = 5000;
private static final String SERVER_IP = "10.0.2.2";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Thread(new ClientThread()).start();
}
public void onClick(View view) {
try {
EditText et = (EditText) findViewById(R.id.EditText01);
String str = et.getText().toString();
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),
true);
out.println(str);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
class ClientThread implements Runnable {
#Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
Then using their other snippet for the server activity I can catch messages using TCP on that port:
public class Server extends Activity {
private ServerSocket serverSocket;
Handler updateConversationHandler;
Thread serverThread = null;
private TextView text;
public static final int SERVERPORT = 6000;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
text = (TextView) findViewById(R.id.text2);
updateConversationHandler = new Handler();
this.serverThread = new Thread(new ServerThread());
this.serverThread.start();
}
#Override
protected void onStop() {
super.onStop();
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
class ServerThread implements Runnable {
public void run() {
Socket socket = null;
try {
serverSocket = new ServerSocket(SERVERPORT);
} catch (IOException e) {
e.printStackTrace();
}
while (!Thread.currentThread().isInterrupted()) {
try {
socket = serverSocket.accept();
CommunicationThread commThread = new CommunicationThread(socket);
new Thread(commThread).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class CommunicationThread implements Runnable {
private Socket clientSocket;
private BufferedReader input;
public CommunicationThread(Socket clientSocket) {
this.clientSocket = clientSocket;
try {
this.input = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
String read = input.readLine();
updateConversationHandler.post(new updateUIThread(read));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class updateUIThread implements Runnable {
private String msg;
public updateUIThread(String str) {
this.msg = str;
}
#Override
public void run() {
text.setText(text.getText().toString()+"Client Says: "+ msg + "\n");
}
}
}
My question is how can I make it so these 2 can communicate back and forth?
Android -> Server(4000) -> Android(4001)?
In other words how can I make my app help the device act as both the client (sending out data to another device on port 4000) and the server (listening for data on port 4001) at the same time?
On the Server side change the port to 5000 (same as Client) instead of 6000:
And you need to update the following class because you don't want to create a new socket, you should use the one already created (in the Client part).
NB: the socket given as an argument to CommunicationThread is the socket that you supposedly already created (Client part).
class ServerThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
CommunicationThread commThread = new CommunicationThread(socket);
new Thread(commThread).start();
}
}
}
I made a simple prototype of a client-server application on Android
I managed to connect two clients to the server and the server can receive their messages. The problem now is that I can't seem to broadcast/receive the messages to other clients.
I try to broadcast the received message through a for loop in the Server class:
private void broadcastMessage(String message) {
for (int i = 0, j = clients.size(); i <= j; i++) {
PrintWriter out = null;
Socket socket = clients.get(i);
try {
out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// WHERE YOU ISSUE THE COMMANDS
out.println(message);
Log.d("SERVER Loop", "Broadcasting messages...");
out.close();
}
Log.d("SERVER", "Message Brodcasted");
}
This I then try to receive through a listener in the Client class :
public class ClientThreadListener implements Runnable {
protected Socket serverSocket = null;
protected String mMsgFromServer;
public ClientThreadListener(Socket serverSocket) {
this.serverSocket = serverSocket;
}
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
serverSocket.getInputStream()));
while ((mMsgFromServer = in.readLine()) != null) {
Log.d("MESSAGE FROM SERVER: ", mMsgFromServer);
handler.post(new Runnable() {
#Override
public void run() {
msgFromOtherClients.append('\n'
+ "Message From Server: " + mMsgFromServer);
}
});
}
} catch (Exception e) {
Log.e("ClientListener", "C: Error", e);
connected = false;
}
}
}
I don't get any errors or force closes though. Forgive me I know it is very messy but please bear with me and please focus on the issue at hand instead :D
Here is the full code for the Server class
public class Server extends Activity {
private TextView serverStatus;
// DEFAULT IP
public static String SERVERIP = "10.0.2.15";
// DESIGNATE A PORT
public static final int SERVERPORT = 8080;
private Handler handler = new Handler();
private ServerSocket serverSocket;
private String mMsgFromClient;
private MultiThreadedServer server;
private ArrayList<Socket> clients = new ArrayList<Socket>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.server);
serverStatus = (TextView) findViewById(R.id.server_status);
// SERVERIP = getLocalIpAddress();
server = new MultiThreadedServer(8080);
new Thread(server).start();
}
public class MultiThreadedServer implements Runnable {
protected int serverPort = 8080;
protected ServerSocket serverSocket = null;
protected boolean isStopped = false;
protected Thread runningThread = null;
public MultiThreadedServer(int port) {
this.serverPort = port;
}
public void run() {
synchronized (this) {
this.runningThread = Thread.currentThread();
}
openServerSocket();
while (!isStopped()) {
Socket clientSocket = null;
try {
clientSocket = this.serverSocket.accept();
clients.add(clientSocket);
} catch (IOException e) {
if (isStopped()) {
Log.d("SERVER TEXT", "Server Stopped.");
return;
}
throw new RuntimeException(
"Error accepting client connection", e);
}
new Thread(new WorkerRunnable(clientSocket, this)).start();
}
Log.d("SERVER TEXT", "Server Stopped.");
}
private synchronized boolean isStopped() {
return this.isStopped;
}
public synchronized void stop() {
this.isStopped = true;
try {
this.serverSocket.close();
} catch (IOException e) {
throw new RuntimeException("Error closing server", e);
}
}
private void openServerSocket() {
try {
this.serverSocket = new ServerSocket(this.serverPort);
} catch (IOException e) {
throw new RuntimeException("Cannot open port 8080", e);
}
}
private void broadcastMessage(String message) {
for (int i = 0, j = clients.size(); i <= j; i++) {
PrintWriter out = null;
Socket socket = clients.get(i);
try {
out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// WHERE YOU ISSUE THE COMMANDS
out.println(message);
Log.d("SERVER Loop", "Broadcasting messages...");
out.close();
}
Log.d("SERVER", "Message Brodcasted");
}
}
public class WorkerRunnable implements Runnable {
protected Socket clientSocket = null;
protected String mMsgFromClient = null;
private UUID id;
public WorkerRunnable(Socket clientSocket, MultiThreadedServer server) {
this.clientSocket = clientSocket;
id = UUID.randomUUID();
}
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
while ((mMsgFromClient = in.readLine()) != null) {
handler.post(new Runnable() {
#Override
public void run() {
serverStatus.append('\n'
+ "Message From Client ID " + getID()
+ ": " + mMsgFromClient);
}
});
}
Log.d("SERVERTEXT", "Proceed to broadcast");
server.broadcastMessage(mMsgFromClient);
} catch (IOException e) {
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
serverStatus
.append('\n'
+ "Message From Client ID "
+ getID()
+ ": "
+ "Oops. Connection interrupted. Please reconnect your phones.");
}
});
e.printStackTrace();
}
}
private String getID() {
return id.toString();
}
}
}
Here is the full code for the Client class
public class Client extends Activity {
private EditText serverIp;
private EditText chatMsg;
private Button connectPhones;
private Button sendMsg;
private TextView msgFromOtherClients;
private String serverIpAddress = "";
private boolean connected = false;
private boolean willSendMsg = false;
private Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.client);
serverIp = (EditText) findViewById(R.id.server_ip);
connectPhones = (Button) findViewById(R.id.connect_phones);
connectPhones.setOnClickListener(connectListener);
chatMsg = (EditText) findViewById(R.id.chat_msg);
sendMsg = (Button) findViewById(R.id.send_msg);
sendMsg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
willSendMsg = true;
}
});
msgFromOtherClients = (TextView) findViewById(R.id.msg_from_other_clients);
}
private OnClickListener connectListener = new OnClickListener() {
#Override
public void onClick(View v) {
if (!connected) {
serverIpAddress = serverIp.getText().toString();
if (!serverIpAddress.equals("")) {
Thread cThread = new Thread(new ClientThread());
cThread.start();
}
}
}
};
public class ClientThread implements Runnable {
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(serverIpAddress);
Log.d("ClientActivity", "C: Connecting...");
Socket socket = new Socket(serverAddr, Server.SERVERPORT);
connected = true;
Thread listener = new Thread(new ClientThreadListener(new Socket(serverAddr, Server.SERVERPORT)));
listener.start();
while (connected) {
if (willSendMsg) {
willSendMsg = false;
try {
Log.d("ClientActivity", "C: Sending command.");
PrintWriter out = new PrintWriter(
new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())), true);
// WHERE YOU ISSUE THE COMMANDS
out.println(chatMsg.getText().toString());
Log.d("ClientActivity", "C: Sent.");
} catch (Exception e) {
Log.e("ClientActivity", "S: Error", e);
}
}
}
socket.close();
Log.d("ClientActivity", "C: Closed.");
} catch (Exception e) {
Log.e("ClientActivity", "C: Error", e);
connected = false;
}
}
}
public class ClientThreadListener implements Runnable {
protected Socket serverSocket = null;
protected String mMsgFromServer;
public ClientThreadListener(Socket serverSocket) {
this.serverSocket = serverSocket;
}
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
serverSocket.getInputStream()));
while ((mMsgFromServer = in.readLine()) != null) {
Log.d("MESSAGE FROM SERVER: ", mMsgFromServer);
handler.post(new Runnable() {
#Override
public void run() {
msgFromOtherClients.append('\n'
+ "Message From Server: " + mMsgFromServer);
}
});
}
} catch (Exception e) {
Log.e("ClientListener", "C: Error", e);
connected = false;
}
}
}
}
Your code has some issue that prevents it from working.
As already said in other answers, in your code you are closing the socket output stream right after sending the message to the client. call close() only out of your for message loop. Of course closing the socket in the client will have the same effect as closing it on the server. You must close the sockets only when client and server have finished talking. Closing it while transmitting data it's like hanging up the phone in the middle of a conversation.
Second, you create a new socket on the client side:
Log.d("ClientActivity", "C: Connecting...");
Socket socket = new Socket(serverAddr, Server.SERVERPORT);
but then you pass to the listener another, newly created, socket (I suppose this is not intended):
connected = true;
Thread listener = new Thread(new ClientThreadListener(new Socket(serverAddr, Server.SERVERPORT)));
listener.start();
Third, always call flush() on an output stream right after sending data, or the data will likely not be sent (the send methods will just enqueue your data in the sending buffer).
Last (This may not be useful to you since I don't know your ultimate goal), if you need to send and receive on sockets, 90% of the time it's better and easier to do this asinchronously, using separate threads for listening and sending.
If it still doesn't work, add here some output or log trace from logcat.
You need to move the line:
server.broadcastMessage(mMsgFromClient);
inside the while:
while ((mMsgFromClient = in.readLine()) != null) {
handler.post(new Runnable() {
#Override
public void run() {
serverStatus.append('\n'
+ "Message From Client ID " + getID()
+ ": " + mMsgFromClient);
}
});
// HERE
Log.d("SERVERTEXT", "Proceed to broadcast");
server.broadcastMessage(mMsgFromClient);
}
Otherwise, you'll only broadcast null.
EDIT: You should make sure that mMsgFromClient is not changed between posting the new Runnable and it actually executing. The best way is to initialize a field in the anonymous class with the current value, and log the value of that field instead.
EDIT2: Unless your server is supposed to close its connection to a client after sending it a broadcast message, you should use out.flush() instead of out.close() in the broadcastMessage method. It's preferrable that client connections are closed after a timeout, or just let the clients disconnect, again with a timeout.
Otherwise, your test will be very limited most of the times: a client connects and sends a message; then it receives its own message and the server closes the connection.
Please try to use AsyncTask in android which will create separate thread for communication with server.