public byte[] DataXfer(String header, String hostIP, int port, int timeout, int xferLength) throws JSONException, IOException{
		Socket tcpSocket = new Socket(hostIP, port);
		tcpSocket.setSoTimeout(timeout);
		tcpSocket.setTcpNoDelay(true);

		// send header
		TCPMessageHandler tcpHandler = new TCPMessageHandler(tcpSocket);
		tcpHandler.sendMessage(header);
		
		// send message
		JSONObject controlMsg = new JSONObject();
		controlMsg.put("transferSize", xferLength);
		tcpHandler.sendMessage(controlMsg);

		tcpSocket.shutdownOutput();
		
		// start to read the response
		int dataLength = 0; // keep track of the total received data length
		byte[] receiveBuf = new byte[xferLength];
		ByteBuffer totalBuf = ByteBuffer.wrap(receiveBuf);

		// read the header
		String headerStr = tcpHandler.readMessageAsString();
		if ( !headerStr.equalsIgnoreCase(EchoServiceBase.RESPONSE_OKAY_STR)){
			TransferRate.abort("tcp", xferLength);
			System.out.println("Bad response header: got " + headerStr + " but expected '" + EchoServiceBase.RESPONSE_OKAY_STR + "'");
		
		} else {
			
			// read the messages if the request amount of data is >= PACKET_SIZE bytes
			for(int i = 0; i < xferLength/PACKET_SIZE; i++){
				byte[] buf = tcpHandler.readMessageAsBytes();
				if (buf.length > 0){
					totalBuf.put(buf);
					dataLength += buf.length;
				}else{
					TransferRate.abort("tcp", xferLength);
					System.out.println("Bad response: got " + dataLength + " data but expected " + xferLength + " data.");
				}
			}
			
			// read the first/last message if it's < PACKET_SIZE bytes
			if (xferLength % PACKET_SIZE != 0){
				byte[] buf = tcpHandler.readMessageAsBytes();
				dataLength += buf.length;
				if (buf.length == xferLength % PACKET_SIZE){
					totalBuf.put(buf);
				}else{
					TransferRate.abort("tcp", xferLength);
				}
			}
			
			if (dataLength != xferLength){
				// server doesn't send back the requested amount of data
				TransferRate.abort("tcp", xferLength);
			}	
		}
		tcpHandler.close();
		return totalBuf.array();
	}