user1870797 user1870797 - 3 months ago 10
Android Question

Socket's input stream still gets data after I closed the other side socket

Socket
's methods do not appear to function in the way their names or documentation suggest. For example. I create a client socket to connect a remote serversocket. When the connection establishes, the serversocket.accept() method returns a corresponding socket which to getinputstream from the client socket. But the problem is, if I close the client socket, the socket on the server still returns false for the
isClosed()
method; and, more absurdly, the
Socket
's
InputStream
on the server starts to continuously return value and no longer blocks when the client socket has closed and sending no output to the server. Below is my code:

Client code:

public class MainActivity extends AppCompatActivity {
private Button startButton, stopButton;
public byte[] buffer;
public Socket socket;
public Socket tempSocket;
private int port = 50005;
InetSocketAddress address;
private int r_port = 50006;
AudioRecord recorder;
AudioTrack audioTrack;
private int sampleRate = 16000; // 44100 for music
private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
public static boolean s_status = true;
public static boolean r_status = true;
Thread r_Thread;
Thread s_Thread;
private boolean isPlay = true;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button) findViewById(R.id.start_button);
startButton.setOnTouchListener(talkListener);
if (socket == null) {
socket = new Socket();
address = new InetSocketAddress("192.168.0.2", port);
try {
socket.setReuseAddress(true);
System.out.println("connecting-");
} catch (IOException e) {
e.printStackTrace();
}
}
}

private final View.OnTouchListener talkListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:
startButton.setBackgroundColor(Color.parseColor("#0670c0"));
try {

startStreamings();
} catch (Exception e) {
e.printStackTrace();
}
return true; // if you want to handle the touch event

case MotionEvent.ACTION_UP:
startButton.setBackgroundColor(Color.parseColor("#353535"));

try {
s_status = false;

} catch (Exception e) {
e.printStackTrace();

return true; // if you want to handle the touch event
}
}
return false;
}
};
public void startStreamings(){
s_status=true;
buffer = new byte[minBufSize];
s_Thread = new Thread(new Runnable() {

@Override
public void run() {

if (socket == null) {
try {
socket = new Socket();
socket.setReuseAddress(true);

} catch (IOException e) {
e.printStackTrace();
}
}
if (!socket.isConnected()) {
try {
socket.connect(address);
System.out.println("create new connection in startStreaming");
} catch (IOException e) {
e.printStackTrace();
}
}
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, minBufSize * 10);
try {
if (s_status == true) {
recorder.startRecording();
}
} catch (IllegalStateException e) {
e.printStackTrace();
return;
}

OutputStream os = null;
while (s_status == true) {

//reading data from MIC into buffer
recorder.read(buffer, 0, buffer.length);
try {
os = socket.getOutputStream();
os.write(buffer, 0, minBufSize);
} catch (Exception e) {
e.printStackTrace();
s_status = false;
}
//System.out.println("streaming out: " + buffer.length + "fff" + c++);
}

if (recorder != null) {
recorder.stop();
recorder.release();
recorder = null;
}
}
});
s_Thread.start();
}


}

Server code:

public TcpServerSocket(){

}

public static void main(String args[]) throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
sockets = new ArrayList<Socket>();
while(isListenning){

Socket socket = serverSocket.accept();
isMatched = false;
for(int i =0;i<sockets.size();i++){
Socket preSocket = sockets.get(i);
if(preSocket.getInetAddress().equals(socket.getInetAddress())){
sockets.remove(preSocket);
sockets.add(socket);
isMatched = true;
}
}

if(!isMatched){
sockets.add(socket);
socket.setKeepAlive(false);
new Thread(new TcpServerSocket(socket)).start();
System.out.println("new Connection");
}


}
serverSocket.close();



}

@Override
public void run() {


byte[] receiveData = new byte[1280];
byte[] emptyData = new byte[1280];
InputStream baiss = null;

OutputStream os;
while (isRunning){
try {
baiss = csocket.getInputStream();

if(csocket.isClosed()||!csocket.isConnected()){
isRunning = false;
sockets.remove(csocket);
System.out.println("socket closed!");
}


int numOfBytes = baiss.read(receiveData);
if(numOfBytes==-1){
isRunning=false;
sockets.remove(csocket);
csocket.close();
System.out.println("socket closed!");
}

} catch (IOException e) {
sockets.remove(csocket);
System.out.println("socket closed!");
e.printStackTrace();
}
int socketsLen = sockets.size();
for(int i = 0;i<socketsLen;i++){
Socket client = sockets.get(i);

if(!client.getInetAddress().equals(csocket.getInetAddress())){
try {
os = client.getOutputStream();
os.write(receiveData,0,1280);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}else if(!client.equals(csocket)){
csocket = client;
System.out.println("switched!");
}
}

System.out.println(csocket.getInetAddress().toString()+"fff"+socketsLen);
}
try {
baiss.close();
csocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}


Can you guys give me any suggestions to close the client socket perfectly so I won't keep getting input after I close the client?

EJP EJP
Answer

socket.getInputStream() still get data after I closed the other side socket

That could be because there was still data in transit that hadn't been read yet. Closing a connection doesn't abort pending data transfers. But as it turns out, it doesn't get data at all. There is simply a bug in your code.

I am really upset with socket in recent days.

I suggest you get over being upset and adopt a rational attitude towards your chosen profession.

I find socket's methods just do not function in the way their names or documentations suggest.

Let's see.

For example. I create a client socket to connect a remote serversocket. When the connection establishes,the serversocket.accept() method returns a corresponding socket which to getinputstream from the client socket. But the problem is, if I close the client socket, the socket on the server still returns true for the isClosed() method

No it doesn't. It returns false. The server's socket is still open. The client's socket is closed, and so is the connection, but isClosed() tells you about the state of the socket it is called on, not anything else, and specifically not the connection.

and more absurd, the socket.getInputStream() on the server starts to continuously return value and no longer blocks when the client socket has closed and sending no outputstream to the server.

Only if there was data in flight before the peer closed. Otherwise it is due to a bug in your code, and here it is:

//reading data from MIC into buffer
recorder.read(buffer, 0, buffer.length);

read() returns -1 at end of stream, and you are ignoring it. That's why you get a continous loop. The correct way to write this code is as follows:

int count = recorder.read(buffer, 0, buffer.length);
if (count == -1) {
    recorder.close();
    socket.close();
    break;
}
try {
    os = socket.getOutputStream();
    os.write(buffer, 0, count);

You have a similar problem with your client code. You just don't seem to care about end of stream:

baiss = csocket.getInputStream();
if(csocket.isClosed()||!csocket.isConnected()){
    isRunning = false;
    sockets.remove(csocket);
    System.out.println("socket closed!");
}
baiss.read(receiveData);
// ...
os = client.getOutputStream();
os.write(receiveData,0,1280);

The correct way to write this is as follows:

baiss = csocket.getInputStream();
int count = baiss.read(receiveData);
if(count == -1){
    isRunning = false;
    sockets.remove(csocket);
    System.out.println("socket closed!");
}
// ...
os = client.getOutputStream();
os.write(receiveData, 0, count);

Can you guys give me any suggestions to close the client socket perfectly so I won't keep getting input after I close the client?

You are closing it perfectly. The problem is that you aren't detecting it correctly at the other end.