I'm working on an application that communicates with a ServerSocket.
I'm using the spring integration's TCP client to connect to the server for sending and receiving messages.
Each part is as following snap code:
Server:
public void startSocketServer(){
try (final ServerSocket serverSocket = new ServerSocket(9992)) {
gl.info("Server is listening on: " + serverSocket.getLocalSocketAddress());
while (true) {
final Socket socket = serverSocket.accept();
gl.info("A new client connected");
new SocketThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private class SocketThread extends Thread {
private final Socket socket;
private final PrintWriter writer;
private final BufferedReader reader;
public SocketThread(Socket socket) throws IOException {
this.socket = socket;
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
reader = new BufferedReader(new InputStreamReader(input));
writer = new PrintWriter(output, true);
}
public void run() {
try {
while (true) {
String inputMessage = reader.readLine();
if (inputMessage != null) {
MessageType messageType = getTypeInstance(inputMessage);
if (messageType instanceof LoginMessage loginMessage) {
if (isAuthenticated(loginMessage.getUsername(), loginMessage.getPassword())) {
gl.info("#### SERVER => User authorized");
final String messageBody = createConnectionAckMessage();
print(writer, messageBody);
} else {
print(writer, createRefusalMessage());
}
} else if (messageType instanceof StartTransferingData startData) {
getMessages().forEach(message-> print(writer, message));
} else if (messageType instanceof DisconnectionAck disAck) {
print(writer, "By then")
break;
}
}
}
socket.close();
} catch (IOException ex) {
gl.info("Server exception: " + ex.getMessage());
}
}
private void print(PrintWriter writer, String msg) {
writer.print(msg);
writer.print("\r\n");
}
}
And Client:
public class CapConfig {
#MessagingGateway(defaultRequestChannel = "toTcp", errorChannel = "errorChannel")
public interface TcpGateway {
#Gateway
void send(String in);
}
#Bean
public MessageChannel toTcp() {
return new DirectChannel();
}
#Bean
public AbstractClientConnectionFactory clientCF() {
return Tcp.netClient("localhost", 9992)
.serializer(TcpCodecs.crlf())
.deserializer(TcpCodecs.crlf())
.get();
}
#Bean
public IntegrationFlow tcpOutFlow(AbstractClientConnectionFactory connectionFactory) {
return IntegrationFlows.from(toTcp())
.handle(Tcp.outboundAdapter(connectionFactory))
.get();
}
#Bean
public IntegrationFlow tcpInFlow(AbstractClientConnectionFactory connectionFactory) {
return IntegrationFlows.from(Tcp.inboundAdapter(connectionFactory))
.transform(stringTransformer)
.log()
//---- Do some other stuffs
.get();
}
}
And the scenario is as following:
The client sends username&password with the gateway and then the server receives the message and authenticates it, if the client authenticated, the server sends connectionAck message to the client to show the connection accepted.
Then the client sends startData message to the server to start data transmission.
The problem is:
When the client sends username&pass to the server, and the server sends connectionAck to the client, the client does not receive the message!!!!.
Any help?
Thanks in advance.
I just used writer.println(msg) instead of writer.print(msg) and the problem solved.
I don't know why but it worked.
Related
I have a small primitive server for studying, and client side.
Here I have piece of my server code:
public class Connector implements Runnable, SocketListener {
private Socket socket;
private ServerSocket serverSocket;
private List<ServerSideClient> clients = new LinkedList<>();
private boolean triger;
public Connector(ServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
#Override
public void run() {
while (true) {
try {
System.out.println("Waiting for clients..");
triger = true;
socket = serverSocket.accept();
System.out.println("Client connected");
ServerSideClient client = createClient();
client.setConnection(true);
client.startListeningClient();
clients.add(client);
new Thread(() -> {
socketIsClosed(client);
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private ServerSideClient createClient() {
return new ServerSideClient(socket);
}
#Override
public synchronized void socketIsClosed(ServerSideClient client) {
while (triger == true) {
if (client.isConnected() == false) {
triger = false;
clients.remove(client);
System.out.println("Client was removed " + clients.size());
}
}
}
}
Here we wait for new Client, then create client instance and add it to LinkedList. In instance on server side we waiting information from client and sending answer on separated thread. But when client closes connection with server, socketIsClosed() method should to delete current client reference from collection. But when client is disconnected I haven't even Logout System.out.println("Client was removed " + clients.size()); from socketIsClosed(ServerSideClient client) method.
Client Code:
public class Client {
private final String HOST = "localhost";
private final int PORT = 1022;
private InputStream inputStream;
private OutputStream outputStream;
private BufferedReader bufferedReader;
private Socket socket;
private boolean connection;
public Client() throws IOException {
socket = new Socket();
socket.connect(new InetSocketAddress(HOST, PORT));
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
bufferedReader = new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
Client client = null;
try {
client = new Client();
client.work();
} catch (IOException e) {
e.printStackTrace();
}
}
private void work() {
connection = true;
listenForConsoleInput();
receiveAnswerFromServer();
}
private void listenForConsoleInput() {
new Thread(() -> {
while (connection == true) {
String requset = null;
try {
requset = bufferedReader.readLine();
if (requset.equals(".")) {
closeConnection();
return;
} else {
sendRequest(requset);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private void sendRequest(String request) {
try {
outputStream.write(request.getBytes());
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private void receiveAnswerFromServer() {
new Thread(() -> {
while (connection == true) {
byte[] data = new byte[32 * 1024];
try {
int numberOfBytes = inputStream.read(data);
System.out.println("Server>> " + new String(data, 0, numberOfBytes));
} catch (IOException e) {
closeConnection();
}
}
}).start();
}
private void closeConnection() {
try {
connection = false;
socket.close();
inputStream.close();
outputStream.close();
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
socketIsClosed(ServerSideClient client) method works in separated thread.
public class ServerSideClient {
private Socket socket;
private InputStream in;
private OutputStream out;
private boolean connection;
private int numOfBytes;
public boolean isConnected() {
return connection;
}
public void setConnection(boolean connection) {
this.connection = connection;
}
public ServerSideClient(Socket socket) {
this.socket = socket;
try {
in = socket.getInputStream();
out = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
public void startListeningClient() {
new Thread(() -> {
listenUsers();
}).start();
}
private void listenUsers() {
while (connection == true) {
byte[] data = new byte[32 * 1024];
readInputFromClient(data);
if (numOfBytes == -1) {
try {
connection = false;
socket.close();
in.close();
out.close();
isConnected();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Client disconected..");
return;
}
String requestFromClient = new String(data, 0, numOfBytes);
System.out.println("Client sended>> " + requestFromClient);
sendResponce(requestFromClient);
}
}
private void readInputFromClient(byte[] data) {
try {
numOfBytes = in.read(data);
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendResponce(String resp) {
try {
out.write(resp.getBytes());
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I trying to resolve this problem since 2 week, Helllllllp.....
I was able to replicate your problem, and as a easy solution to fix is creating a class SocketClosedListener:
class SocketClosedListener implements Runnable {
private final ServerSideClient client;
private List<ServerSideClient> clients;
public SocketClosedListener(ServerSideClient client, List<ServerSideClient> clients) {
this.client = client;
this.clients = clients;
}
#Override
public void run() {
while (true) {
if (!client.isConnected()) {
clients.remove(client);
System.out.println("Client was removed " + clients.size());
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
And inside your run() method in the Connector class, we have this call:
#Override
public void run() {
while (true) {
try {
System.out.println("Waiting for clients..");
triger = true;
socket = serverSocket.accept();
System.out.println("Client connected");
ServerSideClient client = createClient();
client.setConnection(true);
client.startListeningClient();
clients.add(client);
new Thread(new SocketClosedListener(client, clients)).start();//added
} catch (IOException e) {
e.printStackTrace();
}
}
}
The line added:
new Thread(new SocketClosedListener(client, clients)).start();
It is responsible to be looking for client when disconnected in a separated thread. Also a delay of 100ms to avoid checking each ms that can cause issues when multiple threads running.
With this code I was able to have this in the console:
Waiting for clients..
Client sended>> hi
Client disconected..
Client was removed 1
Client disconected..
Client was removed 0
Okay, I decided this problem with javaRX library. I just used event that send to observer server state. In Observable class I've created:
private PublishSubject<Boolean> subject = PublishSubject.create();
public Observable<Boolean> observable = subject.asObservable();
public void setConnection(boolean connection) {
this.connection = connection;
subject.onNext(this.connection);
}
Method setConnection() set true if client has been connected and false if client initialized disconnect.
In Observer class I initialized an instance of Observable class, and initialized subscription:
client = createClient();
client.observable.subscribe(state -> removeClient(state));
public void removeClient(Boolean state) {
System.out.println("Server state " + state);
if (state == false) {
clients.remove(client);
System.out.println("Client remowed. List size: " + clients.size());
}
}
Now, I always know about server state, and make client removing if last has initialized disconnection.
I need to talk to a C++ application running as a server on a given port. It exposes a binary API(Protocol Buffer) for better performance. My RESTful service is developed in Spring MVC and Jersey and would like to use this new feature. I have been able to consume and produce Protocol Buffer messages successfully.
In my spring web application, I initially created a Apache Commons Pool to create a pool of socket connections. This is how I was reading/writing to the socket
Update 1: Adding PooledObjectFactory implementation
public class PooledSocketConnectionFactory extends BasePooledObjectFactory<Socket> {
private static final Logger LOGGER = LoggerFactory.getLogger(PooledSocketConnectionFactory.class);
final private String hostname;
final private int port;
private PooledSocketConnectionFactory(final String hostname, final int port) {
this.hostname = hostname;
this.port = port;
}
#Override
public Socket create() throws Exception {
return new Socket(hostname, port);
}
#Override
public PooledObject wrap(Socket socket) {
return new DefaultPooledObject<>(socket);
}
#Override
public void destroyObject(final PooledObject<Socket> p) throws Exception {
final Socket socket = p.getObject();
socket.close();
}
#Override
public boolean validateObject(final PooledObject<Socket> p) {
final Socket socket = p.getObject();
return socket != null && socket.isConnected();
}
#Override
public void activateObject(final PooledObject<SocketConnection> p) throws Exception {
}
#Override
public void passivateObject(final PooledObject<SocketConnection> p) throws Exception {
}
}
#Service
#Scope("prototype")
public class Gateway {
#Autowired
private GenericObjectPool pool;
public Response sendAndReceive(Request request) throws CommunicationException {
Response response = null;
final Socket socket = pool.borrowObject();
try {
request.writeDelimitedTo(socket.getOutputStream());
response = Response.parseDelimitedFrom(socket.getInputStream());
} catch (Exception ex) {
LOGGER.error("Gateway error", ex);
throw new CommunicationException("Gateway error", ex);
} finally {
pool.returnObject(socket);
}
return response;
}
}
This works for the first request and when the pool returns any previously used socket it is found that the socket is already closed. This could be because different requests are getting connected to the same input and output streams. If I close the socket after reading the response then it beats the purpose of pooling. If I use a singleton socket and inject it, it is able to process first request and then times out.
If I create the socket on every instance then it works and the performance is around 2500 microseconds for every request. My target is to get the performance within 500 microseconds.
What should be the best approach given the requirements?
Update 2: Adding a server and client
package com.es.socket;
import com.es.protos.RequestProtos.Request;
import com.es.protos.ResponseProtos.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer1 {
final static Logger LOGGER = LoggerFactory.getLogger(TcpServer1.class.getName());
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(Integer.parseInt(args[0]));
Socket socket = null;
while (true) {
try {
socket = serverSocket.accept();
} catch (IOException e) {
LOGGER.warn("Could not listen on port");
System.exit(-1);
}
Thread thread = new Thread(new ServerConnection1(socket));
thread.start();
}
}
}
class ServerConnection1 implements Runnable {
static final Logger LOGGER = LoggerFactory.getLogger(ServerConnection.class.getName());
private Socket socket = null;
ServerConnection1(Socket socket) {
this.socket = socket;
}
public void run() {
try {
serveRequest(socket.getInputStream(), socket.getOutputStream());
//socket.close();
} catch (IOException ex) {
LOGGER.warn("Error", ex);
}
}
public void serveRequest(InputStream inputStream, OutputStream outputStream) {
try {
read(inputStream);
write(outputStream);
} catch (IOException ex) {
LOGGER.warn("ERROR", ex);
}
}
private void write(OutputStream outputStream) throws IOException {
Response.Builder builder = Response.newBuilder();
Response response = builder.setStatus("SUCCESS").setPing("PING").build();
response.writeDelimitedTo(outputStream);
LOGGER.info("Server sent {}", response.toString());
}
private void read(InputStream inputStream) throws IOException {
Request request = Request.parseDelimitedFrom(inputStream);
LOGGER.info("Server received {}", request.toString());
}
}
package com.es.socket;
import com.es.protos.RequestProtos.Request;
import com.es.protos.ResponseProtos.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.Socket;
public class TcpClient1 {
final static Logger LOGGER = LoggerFactory.getLogger(TcpClient1.class.getName());
private Socket openConnection(final String hostName, final int port) {
Socket clientSocket = null;
try {
clientSocket = new Socket(hostName, port);
} catch (IOException e) {
LOGGER.warn("Exception occurred while connecting to server", e);
}
return clientSocket;
}
private void closeConnection(Socket clientSocket) {
try {
LOGGER.info("Closing the connection");
clientSocket.close();
} catch (IOException e) {
LOGGER.warn("Exception occurred while closing the connection", e);
}
}
private void write(OutputStream outputStream) throws IOException {
Request.Builder builder = Request.newBuilder();
Request request = builder.setPing("PING").build();
request.writeDelimitedTo(outputStream);
LOGGER.info("Client sent {}", request.toString());
}
private void read(InputStream inputStream) throws IOException {
Response response = Response.parseDelimitedFrom(inputStream);
LOGGER.info("Client received {}", response.toString());
}
public static void main(String args[]) throws Exception {
TcpClient1 client = new TcpClient1();
try {
Socket clientSocket = null;
LOGGER.info("Scenario 1 --> One socket for each call");
for (int i = 0; i < 2; i++) {
clientSocket = client.openConnection("localhost", Integer.parseInt(args[0]));
OutputStream outputStream = clientSocket.getOutputStream();
InputStream inputStream = clientSocket.getInputStream();
LOGGER.info("REQUEST {}", i);
client.write(outputStream);
client.read(inputStream);
client.closeConnection(clientSocket);
}
LOGGER.info("Scenario 2 --> One socket for all calls");
clientSocket = client.openConnection("localhost", Integer.parseInt(args[0]));
OutputStream outputStream = clientSocket.getOutputStream();
InputStream inputStream = clientSocket.getInputStream();
for (int i = 0; i < 2; i++) {
LOGGER.info("REQUEST {}", i);
client.write(outputStream);
client.read(inputStream);
}
client.closeConnection(clientSocket);
} catch (Exception e) {
LOGGER.warn("Exception occurred", e);
System.exit(1);
}
}
}
Here Request and Response are Protocol Buffer classes. In Scenario 1, it is able to able to process both calls whereas in scenario 2 it never returns from the second read. Seems Protocol Buffer API is handling the streams differently. Sample output below
17:03:10.508 [main] INFO c.d.e.socket.TcpClient1 - Scenario 1 --> One socket for each call
17:03:10.537 [main] INFO c.d.e.socket.TcpClient1 - REQUEST 0
17:03:10.698 [main] INFO c.d.e.socket.TcpClient1 - Client sent ping: "PING"
17:03:10.730 [main] INFO c.d.e.socket.TcpClient1 - Client received status: "SUCCESS"
ping: "PING"
17:03:10.730 [main] INFO c.d.e.socket.TcpClient1 - Closing the connection
17:03:10.731 [main] INFO c.d.e.socket.TcpClient1 - REQUEST 1
17:03:10.732 [main] INFO c.d.e.socket.TcpClient1 - Client sent ping: "PING"
17:03:10.733 [main] INFO c.d.e.socket.TcpClient1 - Client received status: "SUCCESS"
ping: "PING"
17:03:10.733 [main] INFO c.d.e.socket.TcpClient1 - Closing the connection
17:03:10.733 [main] INFO c.d.e.socket.TcpClient1 - Scenario 2 --> One socket for all calls
17:03:10.733 [main] INFO c.d.e.socket.TcpClient1 - REQUEST 0
17:03:10.734 [main] INFO c.d.e.socket.TcpClient1 - Client sent ping: "PING"
17:03:10.734 [main] INFO c.d.e.socket.TcpClient1 - Client received status: "SUCCESS"
ping: "PING"
17:03:10.734 [main] INFO c.d.e.socket.TcpClient1 - REQUEST 1
17:03:10.735 [main] INFO c.d.e.socket.TcpClient1 - Client sent ping: "PING"
After great pain I was able to resolve the issue. The class which was handling the read/write to the socket was defined as prototype. So once a reference to the socket was retrieved it was not cleared up(managed by Tomcat). As such subsequent calls to the socket gets queued up, which then times out and the object is destroyed by Apache Commons Pool.
To fix this, I created class SocketConnection with a ThreadLocal of Socket. On the processing side, I created a Callback to handle read/write to the socket. Sample code snippet below:
class SocketConnection {
final private String identity;
private boolean alive;
final private ThreadLocal<Socket> threadLocal;
public SocketConnection(final String hostname, final int port) throws IOException {
this.identity = UUID.randomUUID().toString();
this.alive = true;
threadLocal = ThreadLocal.withInitial(rethrowSupplier(() -> new Socket(hostname, port)));
}
}
public class PooledSocketConnectionFactory extends BasePooledObjectFactory<SocketConnection> {
private static final Logger LOGGER = LoggerFactory.getLogger(PooledSocketConnectionFactory.class);
final private String hostname;
final private int port;
private SocketConnection connection = null;
private PooledSocketConnectionFactory(final String hostname, final int port) {
this.hostname = hostname;
this.port = port;
}
#Override
public SocketConnection create() throws Exception {
LOGGER.info("Creating Socket");
return new SocketConnection(hostname, port);
}
#Override
public PooledObject wrap(SocketConnection socketConnection) {
return new DefaultPooledObject<>(socketConnection);
}
#Override
public void destroyObject(final PooledObject<SocketConnection> p) throws Exception {
final SocketConnection socketConnection = p.getObject();
socketConnection.setAlive(false);
socketConnection.close();
}
#Override
public boolean validateObject(final PooledObject<SocketConnection> p) {
final SocketConnection connection = p.getObject();
final Socket socket = connection.get();
return connection != null && connection.isAlive() && socket.isConnected();
}
#Override
public void activateObject(final PooledObject<SocketConnection> p) throws Exception {
final SocketConnection socketConnection = p.getObject();
socketConnection.setAlive(true);
}
#Override
public void passivateObject(final PooledObject<SocketConnection> p) throws Exception {
final SocketConnection socketConnection = p.getObject();
socketConnection.setAlive(false);
}
}
class SocketCallback implements Callable<Response> {
private SocketConnection socketConnection;
private Request request;
public SocketCallback() {
}
public SocketCallback(SocketConnection socketConnection, Request request) {
this.socketConnection = socketConnection;
this.request = request;
}
public Response call() throws Exception {
final Socket socket = socketConnection.get();
request.writeDelimitedTo(socket.getOutputStream());
Response response = Response.parseDelimitedFrom(socket.getInputStream());
return response;
}
}
#Service
#Scope("prototype")
public class SocketGateway {
private static final Logger LOGGER = LoggerFactory.getLogger(SocketGateway.class);
#Autowired
private GenericObjectPool<SocketConnection> socketPool;
#Autowired
private ExecutorService executorService;
public Response eligibility(Request request) throws DataException {
EligibilityResponse response = null;
SocketConnection connection = null;
if (request != null) {
try {
connection = socketPool.borrowObject();
Future<Response> future = executorService.submit(new SocketCallback(connection, request));
response = future.get();
} catch (Exception ex) {
LOGGER.error("Gateway error {}");
throw new DataException("Gateway error", ex);
} finally {
socketPool.returnObject(connection);
}
}
return response;
}
}
Try to do some concurrent messaging between the server and the client. When they first connect to eachother and the Server sends the test string, the client gets it perfectly fine the first time. And the client can SEND messages just fine to the Server. But my Client class cant constantly check for messages like my Server can and idk what's wrong. Any suggestions?
Server class code:
import java.lang.*;
import java.io.*;
import java.net.*;
import java.util.Random;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Server {
String testMessage = "You are now connected and can begin chatting!";
boolean connected = false;
int port;
public Server(int port) {
this.port = port;
}
public void Open() {
//creates Threadpool for multiple instances of chatting
final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(10);
Runnable serverTask = new Runnable() {
#Override
public void run() {
try {
System.out.println("Opening...");
ServerSocket srvr = new ServerSocket(port);
while (true) {
Socket skt = srvr.accept();
clientProcessingPool.submit(new ClientTask(skt));
}
} catch (Exception e) {
try {
System.out.println(e);
System.out.print("You're opening too many servers in the same location, fool!\n");
ServerSocket srvr = new ServerSocket(port);
while (true) {
Socket skt = srvr.accept();
clientProcessingPool.submit(new ClientTask(skt));
}
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
Thread serverThread = new Thread(serverTask);
serverThread.start();
}
private class ClientTask implements Runnable {
private final Socket skt;
private ClientTask(Socket skt) {
this.skt = skt;
}
#Override
public void run() {
//for sending messages
if (connected == false) {
System.out.println("======================");
System.out.println("Server has connected!");
processMessage(testMessage);
}
//for receiving messages
while (true) {
try {
// Read one line and output it
BufferedReader br = new BufferedReader(new InputStreamReader(skt.getInputStream()));
String incomingMessage = br.readLine();
if (incomingMessage != null) {
System.out.println("Server: Received message: " + incomingMessage);
processMessage(incomingMessage);
}
//br.close();
//skt.close(); //maybe delete
} catch (Exception e) {
System.out.println("Server had error receiving message.");
System.out.println("Error: " + e);
}
}
}
//for processing a message once it is received
public void processMessage(String message) {
PrintWriter out = null;
try {
out = new PrintWriter(skt.getOutputStream(), true);
} catch (IOException ex) {
System.out.println(ex);
System.out.println("Server had error sending message.");
}
System.out.print("Server: Sending message: " + message + "\n");
out.print(message);
out.flush();
connected = true;
try {
skt.shutdownOutput();
//out.close();
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Client class code:
import java.lang.*;
import java.io.*;
import java.net.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
class Client {
public String message;
Socket skt;
public int port;
public Client(int port) {
this.port = port;
}
//for receiving messages from Server
public void receiveMessage() {
final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(10);
Runnable serverTask = new Runnable() {
#Override
public void run() {
try {
skt = new Socket(InetAddress.getLocalHost().getHostName(), port);
while (true) {
clientProcessingPool.submit(new Client.ClientTask(skt));
}
} catch (IOException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
Thread serverThread = new Thread(serverTask);
serverThread.start();
}
//for sending messages to Server
public void sendMessage(String outgoingMessage) throws IOException {
try {
skt = new Socket(InetAddress.getLocalHost().getHostName(), port);
PrintWriter pw = new PrintWriter(skt.getOutputStream());
System.out.println("Client: Sending message: " + outgoingMessage);
pw.print(outgoingMessage);
pw.flush();
skt.shutdownOutput();
//skt.close(); //maybe delete
} catch (Exception e) {
System.out.println(e);
System.out.print("Client had error sending message.\n");
JOptionPane.showMessageDialog(null, "That User is not currently online.", "ERROR!!", JOptionPane.INFORMATION_MESSAGE);
}
}
private class ClientTask implements Runnable {
private final Socket skt;
private ClientTask(Socket skt) {
this.skt = skt;
}
#Override
public void run() {
while (true) {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(skt.getInputStream()));
//while (!in.ready()) {}
String incomingMessage = in.readLine();
if (incomingMessage != null) {
System.out.println("Client: Received message: " + incomingMessage); // Read one line and output it
message = incomingMessage;
}
//skt.shutdownInput();
//in.close();
//skt.close(); //maybe delete
} catch (Exception e) {
System.out.print("Client had error receiving message.\n");
}
}
}
}
}
Streams cannot be re-wrapped. Once assigned to a wrapper, they must use that wrapper for the entire life-cycle of the stream. You also shouldn't close a stream until you are done using it, which in this case isn't until your client and server are done communicating.
In your current code, there are a few times where you re-initialize streams:
while (true) {
try {
//Each loop, this reader will attempt to re-wrap the input stream
BufferedReader br = new BufferedReader(new InputStreamReader(skt.getInputStream()));
String incomingMessage = br.readLine();
if (incomingMessage != null) {
System.out.println("Server: Received message: " + incomingMessage);
processMessage(incomingMessage);
}
//don't close your stream and socket so early!
br.close();
skt.close();
} catch (Exception e) {
//...
}
You get the idea; you can use this knowledge to find the stream problems in your client code as well.
With that said, servers are the middle-man between multiple clients. If you want to be able to type in the server's console to send a message to clients, it shouldn't go to only 1 client (unless you had a system that allowed you to specify a name). You need to store every connection in some kind of collection so when you type in the server's console, it goes to every client that's connected. This also helps when a client wants to send a message to every other client (global message). The server's main thread is primarily for accepting clients; I created another thread to allow you to type in the console.
As for your streams, you should create them whenever you start the ClientTask, both server side and client side:
public class Server {
private ExecutorService executor = Executors.newFixedThreadPool(10);
private Set<User> users = new HashSet<>();
private boolean running;
private int port;
public Server(int port) {
this.port = port;
}
public void start() {
running = true;
Runnable acceptor = () -> {
try(ServerSocket ss = new ServerSocket(port)) {
while(running) {
User client = new User(ss.accept());
users.add(client);
executor.execute(client);
}
} catch(IOException e) {
//if a server is already running on this port;
//if the port is not open;
e.printStackTrace();
}
};
Runnable userInputReader = () -> {
try(Scanner scanner = new Scanner(System.in)) {
while(running) {
String input = scanner.nextLine();
for(User user : users) {
user.send(input);
}
}
} catch(IOException e) {
//problem sending data;
e.printStackTrace();
}
};
Thread acceptorThread = new Thread(acceptor);
Thread userThread = new Thread(userInputReader);
acceptorThread.start();
userThread.start();
}
public void stop() {
running = false;
}
public static void main(String[] args) {
new Server(15180).start();
System.out.println("Server started!");
}
}
In the run() method is where the streams should be wrapped.
class User implements Runnable {
private Socket socket;
private boolean connected;
private DataOutputStream out; //so we can access from the #send(String) method
public User(Socket socket) {
this.socket = socket;
}
public void run() {
connected = true;
try(DataInputStream in = new DataInputStream(socket.getInputStream())) {
out = new DataOutputStream(socket.getOutputStream());
while(connected) {
String data = in.readUTF();
System.out.println("From client: "+data);
//send to all clients
}
} catch(IOException e) {
//if there's a problem initializing streams;
//if socket closes while attempting to read from it;
e.printStackTrace();
}
}
public void send(String message) throws IOException {
if(connected) {
out.writeUTF(message);
out.flush();
}
}
}
It's pretty much the same idea with the client:
1. Connect to Server
2. Create "communication" thread
3. Create "user input" thread (to receive input from console)
4. Start threads
public class Client {
private final String host;
private final int port;
private boolean connected;
private Socket socket;
public Client(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws IOException {
connected = true;
socket = new Socket(host, port);
Runnable serverInputReader = () -> {
try (DataInputStream in = new DataInputStream(socket.getInputStream())) {
while (connected) {
String data = in.readUTF();
System.out.println(data);
}
} catch (IOException e) {
// problem connecting to server; problem wrapping stream; problem receiving data from server;
e.printStackTrace();
}
};
Runnable userInputReader = () -> {
try (DataOutputStream out = new DataOutputStream(socket.getOutputStream());
Scanner scanner = new Scanner(System.in)) {
while (connected) {
String input = scanner.nextLine();
out.writeUTF(input);
}
} catch (IOException e) {
//problem wrapping stream; problem sending data;
e.printStackTrace();
}
};
Thread communicateThread = new Thread(serverInputReader);
Thread userThread = new Thread(userInputReader);
communicateThread.start();
userThread.start();
}
public static void main(String[] args) throws IOException {
new Client("localhost", 15180).start();
}
}
There are a few things I used in the code above that you may not be familiar with. They help simplify the syntax for your code:
Lambda Expressions - Prevents the need to create an anonymous class (or subclass) to declare a method
Try-With-Resources - Closes the resources specified automatically once the try block as ended
EDIT
When a user connects, you should store their connection by name or id. That way, you can send data to specific users. Even if your client is running on the same machine as the server, it's still the same idea: client connects to server, server sends message to client based on name or id:
while(running) {
User client = new User(ss.accept());
users.add(client); //add to set
executor.execute(client);
}
Right now, you are simply adding users to a Set. There is currently no way to grab a specific value from this set. What you need to do is give it some kind of "key". To give you an idea, here's an old algorithm I used to use. I have an array full of empty slots. When someone connects, I look for the first empty slot. Once an empty slot is found, I pass the user the index of the array it's being stored at (that will be the user's id), then store the user in the array at the specified index. When you need to send a message to someone, you can use the id to access that specific array index, grab the user you want and send a message:
class Server {
private int maxConnections = 10;
private ExecutorService executor = Executors.newFixedThreadPool(maxConnections);
private User[] users = new User[maxConnections];
//...
while(running) {
Socket socket = ss.accept();
for(int i = 0; i < users.length; i++) {
if(users[i] == null) {
users[i] = new User(socket, i);
executor.execute(users[i]);
break;
}
}
}
//...
public static void sendGlobalMessage(String message) throws IOException {
for(User user : users)
if(user != null)
user.send(message);
}
public static void sendPrivateMessage(String message, int id) {
User user = users[id];
if(user != null) {
user.send(message);
}
}
}
class User {
private Socket socket;
private int id;
private DataOutputStream out;
public User(Socket socket, int id) {
this.socket = socket;
this.id = id;
}
public void send(String message) throws IOException {
out.writeUTF(message);
out.flush();
}
public void run() {
DataInputStream in;
//wrap in and out streams
while(connected) {
String data = in.readUTF();
//Server.sendGlobalMessage(data);
//Server.sendPrivateMessage(data, ...);
sendMessage(data); //sends message back to client
}
}
}
Multiple Clients say(A, B, C, D etc) make connection to one server through same socket. They all send messages to server as and when required. Client messages are sent only to server(and not to other clients). But whenever server sends a message it should be delivered to all the clients. Please help me out jam only able to get server message on only 1 client
//MultithreadedServer.java
package server;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Vector;
public class MultithreadedServer
{
Vector<ClientHandler> clients = new Vector<ClientHandler>();
Vector<String> users = new Vector<String>();
private static ServerSocket servSocket;
private static final int PORT = 1247;
public MultithreadedServer() throws IOException{
servSocket = new ServerSocket(PORT);
while(true) {
Socket client = servSocket.accept();
System.out.println("\nNew client accepted.\n");
ClientHandler handler;
handler = new ClientHandler(client);
clients.add(handler);
}
}
public static void main(String[] args) throws IOException
{
MultithreadedServer ms = new MultithreadedServer();
}
class ClientHandler extends Thread
{
private Socket client;
private BufferedReader in;
private PrintWriter out;
String name,message,response;
public ClientHandler(Socket socket)
{
//Set up reference to associated socket...
client = socket;
try
{
in = new BufferedReader(
new InputStreamReader(
client.getInputStream()));
out = new PrintWriter(
client.getOutputStream(),true);
}
catch(IOException e)
{
e.printStackTrace();
}
start();
}
public void sendMessage(String msg) {
System.out.println("is it even coming here?");
out.println("Server:" + msg);
}
public void boradcast(String message) {
// send message to all connected users
for (ClientHandler c : clients) {
c.out.println("Server: hello");
}
}
public String getUserName() {
return name;
}
public void run()
{
try
{
String received;
do
{ System.out.println("Enter Your Message: ");
String msg = in.readLine();
out.println(msg);
boradcast("testing");
received = in.readLine();
out.println("ECHO: " + received);
//Repeat above until 'QUIT' sent by client...
}while (!received.equals("QUIT"));
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if (client!=null)
{
System.out.println(
"Closing down connection...");
client.close();
}
}catch(IOException e)
{
e.printStackTrace();
}
}
}
}}
//ClientProgram
import java.io.*;
import java.net.*;
public class Client
{
private static InetAddress host;
private static final int PORT = 1247;
private static Socket link;
private static BufferedReader in;
private static PrintWriter out;
private static BufferedReader keyboard;
public static void main(String[] args) throws IOException
{
try
{
host = InetAddress.getLocalHost();
link = new Socket(host, PORT);
in = new BufferedReader(
new InputStreamReader(
link.getInputStream()));
out = new PrintWriter(
link.getOutputStream(),true);
keyboard = new BufferedReader(
new InputStreamReader(System.in));
String message, response;
do
{
System.out.print(
"Enter message ('QUIT' to exit): ");
message = keyboard.readLine();
//Send message to server on
//the socket's output stream...
out.println(message);
//Accept response frm server on
//the socket's input stream...
response = in.readLine();
//Display server's response to user...
System.out.println(response);
}while (!message.equals("QUIT"));
}
catch(UnknownHostException uhEx)
{
System.out.println(
"\nHost ID not found!\n");
}
catch(IOException ioEx)
{
ioEx.printStackTrace();
}
finally
{
try
{
if (link!=null)
{
System.out.println(
"Closing down connection...");
link.close();
}
}
catch(IOException ioEx)
{
ioEx.printStackTrace();
}
}
}
}
An obvious way to do this would be to cycle through all ClientHandlers in clients and send the message to each of them:
for (ClientHandler ch : clients){
ch.sendMessage(message); //Or something
}
I have written a simple Java dispatcher with a daemon thread to handle the incoming traffic, and using another thread to send out commands.
The problem comes when the server receives the first message, then the client/server system gets stuck on where the server trying to send out a response to the client. The sockets on both end just simply freeze when the server sends out data.
I have simplified my original problem into a echo server and client; I guess I must have a very very stupid mistake on the code. The code and result on my machine are reproduced below. Can anyone explain what is going wrong?
Thanks!
Here is the results, we can see the server and client stuck once the first message received.
Echo Server listening port...
Echo Server: Waiting from client connection.
Connecting to the server.
Connected to the server.
Dispatcher send: 10
Dispatcher send: 11
Dispatcher send: 12
Dispatcher send: 13
Dispatcher read...
Dispatcher read...
Dispatcher readed 10
Code:
EchoTest:
import java.io.*;
import java.net.*;
public class EchoTest {
public static void main(String[] args) {
EchoServer.listen();
EchoClient client = new EchoClient();
EchoServer server = EchoServer.accept();
}
}
Client and Server:
class EchoClient implements Runnable {
private static final int PORT = 13244;
Socket socket;
Disp disp;
Thread client;
public EchoClient() {
client = new Thread(this);
client.start();
}
public void run() {
try {
System.out.println("Connecting to the server.");
Socket socket = new Socket("localhost", PORT);
System.out.println("Connected to the server.");
disp = new Disp(socket);
disp.send(10);
disp.send(11);
disp.send(12);
disp.send(13);
} catch(IOException e) {
System.out.println("Would not connect to local host: " + PORT);
System.exit(-1);
}
}
public void send(int m) {
disp.send(m);
System.out.println("Sent message " + m);
int echo = disp.getMsg();
if(m == echo) {
System.out.println("Message " + m + "sent and received.");
} else {
System.out.println("Message " + m + "cannot be echoed correctly.");
}
}
}
class EchoServer implements Runnable{
private static final int PORT = 13244;
private static ServerSocket serverSocket;
Disp disp;
public EchoServer(Socket s) {
disp = new Disp(s);
}
public static void listen() {
System.out.println("Echo Server listening port...");
try {
serverSocket = new ServerSocket(PORT);
} catch (IOException e) {
System.out.println("Could not listen on port: " + PORT);
System.exit(-1);
}
}
public static EchoServer accept(){
try {
System.out.println("Echo Server: Waiting from client connection.");
return new EchoServer(serverSocket.accept());
} catch(IOException e) {
System.out.println("Couldn't accept connection from client.");
System.exit(-1);
}
return null;
}
public void run() {
while(true) {
int m = disp.getMsg();
disp.send(m);
}
}
}
Disp:
class Disp implements Runnable{
int msg = -1;
Socket socket;
BufferedInputStream input;
DataInputStream dis;
BufferedOutputStream output;
DataOutputStream dos;
Thread daemon;
public Disp(Socket s) {
this.socket = s;
try{
input = new BufferedInputStream(socket.getInputStream());
dis = new DataInputStream(input);
output = new BufferedOutputStream(socket.getOutputStream());
dos = new DataOutputStream(output);
}catch(IOException e) {
}
daemon = new Thread(this);
daemon.start();
}
public void run() {
while(true) {
int m = get();
setMsg(m);
}
}
public void send(int m) {
synchronized(dos) {
try{
System.out.println("Dispatcher send: " + m);
dos.writeInt(m);
dos.flush();
} catch(IOException e) {
}
}
}
public int get() {
System.out.println("Dispatcher read...");
synchronized(dis) {
try{
int m = dis.readInt();
System.out.println("Dispatcher readed " + m);
return m;
} catch(IOException e) {
}
}
return -1;
}
synchronized public void setMsg(int m) {
while(true) {
if(msg == -1) {
try {
wait();
} catch(InterruptedException e) {
}
} else {
msg = m;
notifyAll();
}
}
}
synchronized public int getMsg() {
while(true) {
if(msg != -1) {
try {
wait();
} catch(InterruptedException e) {
}
} else {
notifyAll();
return msg;
}
}
}
}
That's an awful lot of code. First of all I suggest cutting it down.
Secondly, you never seem to call EchoServer.run, but it's difficult to see.
You should also set the TCP_NODELAY flag, as you send only a few bytes. The Operating system usually waits for more data before it sends a package (and your flush() has no influence over that behaviour, as flush only deals with java's buffers).