Ok so basically I'm creating an online multiplayer tic tac toe game using sockets but I've run into some logic trouble in my client class. I need to have the client running but then be able to write and read in a separate method from where the socket, streams, etc are. I'm sure this is a very simple solution and I'm probably over thinking it, I've googled it and I just can't seem to wrap my brain around it.
Basically my final structure should be
public Client
{
Instantiate sockets/streams, etc here.
}
public void writeAndRead
{
Write and read here.
}
My current problem is I cannot call my writer from another method.
My code is as follows
import java.net.*;
import java.io.*;
public class Client
{
private String host;
private int port;
public Client(String host2, int port2) throws IOException
{
host = host2;
port = port2;
//Instantiate a stringbuffer to store the reply from the server.
StringBuffer instr = new StringBuffer();
//Stores the host string into a usable IP.
InetAddress address = InetAddress.getByName(host);
//Instantiate a socket with the given IP and port name.
Socket socket = new Socket(address, port);
//Instantiate a output stream to the socket.
BufferedOutputStream stream = new BufferedOutputStream(socket.getOutputStream());
//Instantiate a writer for the output stream using standard encoding.
OutputStreamWriter writer = new OutputStreamWriter(stream, "US-ASCII");
//Instantiate an input stream to read in reply from server.
BufferedInputStream inputStream = new BufferedInputStream(socket.getInputStream());
//Instantiate a reader to read and convert the stream, once again using standard encoding.
InputStreamReader reader = new InputStreamReader(inputStream, "US-ASCII");
//Create the string to send to server with a char to let the stream reader know when the stream is completed.
String process = "x" + (char) 0;
//Write the string to the output stream.
writer.write(process);
//Clear the output stream.
writer.flush();
//Int c reads the data from the input stream.
int c;
//Receive the message and print.
while ( (c = reader.read()) != 0)
instr.append( (char) c);
System.out.println("Message received from server: " + instr);
}
}
What you do is read the input stream from the socket on a separate Thread so you can read and write the same time without waiting for packets of information to be delivered first. For the writing to the socket you can put the in the Main Thread.
In a class it is usually a good idea to have some variables as private fields in you class and that your constructor is responsible the initial collection of and initialization of these variables.
So as you have
public Client(String host, int port) throws IOException
{
this.host = host;
this.port = port;
}
and then other methods will do one piece of work
e.g.
public void connect () {
//Stores the host string into a usable IP.
InetAddress address = InetAddress.getByName(host);
//Instantiate a socket with the given IP and port name.
this.socket = new Socket(address, port); // yes another field
}
and then other methods will do the reading and writing.
Also you may find that you need to use separate Threads, so try following a Chat example which you can easily find on the internet.
If I understand your question correctly, the variable writer should be a class variable instead of in the scope of the constructor for Client:
public class Client {
private OutputStreamWriter writer;
private String host;
private int port;
public Client(String host2, int port2) throws IOException
{
// ...
}
public void readAndWrite() {
// ...
writer.write();
}
}
Related
I created a simple server and a client, but the server could not read anything that was sent from the client. I also add a print statement after I sent the string, but it cannot be printed either.
public class Server {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket serverSocket = new ServerSocket(6666);
Socket clientSocket = serverSocket.accept();
System.out.println("accepting client at address " + clientSocket.getRemoteSocketAddress());
ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream());
String input = (String) in.readObject();
System.out.println(input);
out.writeObject("Received");
out.flush();
}
}
Below is the client, and I just want to send a string "?????does not send":
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Client client = new Client();
client.sentInfo();
}
private static class Client {
private ObjectInputStream objectInputStream;
private ObjectOutputStream objectOutputStream;
public Client() throws IOException {
Socket socket = new Socket("127.0.0.1", 6666);
this.objectInputStream = new ObjectInputStream(socket.getInputStream());
this.objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
}
public void sentInfo() throws IOException, ClassNotFoundException {
this.objectOutputStream.writeObject("?????does not send");
this.objectOutputStream.flush();
System.out.println("????????");
Message resp = (Message) this.objectInputStream.readObject();
System.out.println(resp.getMessage());
}
}
}
I tried something else, if I just use InputStream and use a buffer to read bytes, like this:
Server code
This is the client code: client code
The code in the two link above would work. However, it would not work if I tried to use ObjectInputStream:
This is the server: server
This is the client: client
This is the Message object I want to send: Message class
Can someone explain this for me please? Thanks!
To read Strings from a socket use something like this:
DataInputStream input = new DataInputStream(clientSocket.getInputStream());
String message = input.readUTF();
You can open multiple streams from a socket, so if you want to read something else that really needs the ObjectInputStream than it can be open as well. Don't forget to properly close the streams & sockets.
Here I am creating a thread to check for a server response every 2 seconds, the issue is that the client.monitorResponse() is a readLine() method and will not continue until a response is received.
client = new ClientObject("localhost");
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
try {
String response = null;
if(!(response = client.monitorResponse()).isEmpty()) {
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 2000, 2000);
I am sending the response via the Server like so (where client is a established Socket):
public SocketObject(Socket client, int numberOfClients) throws Exception {
socket = client; // the .accept() socket is passed through
// this is because I assign them ID's for later use (I hold an ArrayList of sockets)
this.clientId = numberOfClients;
// both these are static to the class
outputStream = new PrintWriter(client.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(client.getInputStream()));
}
public void sendResponse(String response) {
outputStream.println(response);
}
I am then picking the response up via the client Socket that has connected to the server:
public ClientObject(String hostname) throws IOException {
// socket is static to this class
socket = new Socket(hostname, 4444);
System.out.println("Connected to " + hostname + " on port 4444...");
// both static to this class
outputStream = new PrintWriter(socket.getOutputStream(), true);
inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("Successfully started a stream on " + hostname);
this.hostname = hostname;
}
public String monitorResponse() throws Exception {
System.out.println("Listening for a response...");
return inputStream.readLine();
}
The debug console only displays the Listening for a response... output once which is telling me that it doesn't get past the inputStream.readLine() method in-side the Thread. Is there anyway I can add a timeout on the BufferedReader? I have tried multiple solutions like adding a .setSoTimeout() to the socket before creating the BufferedReader but all that did was close the connection/socket after the specified time.
Any help would be appreciated.
You should use a non-blocking (NIO) request and read chunks looking for newlines in-between. Typically in Java you just have to look for the NIO version of the Stream class you are using and use it to check every N seconds for new content. In your case with minimal modifications you can use the less fancy and efficient method of blocking calls with BufferedReader.ready() to prevent blocking:
String partialLine="";
public static String monitorResponse() throws Exception {
System.out.println("Listening for a response...");
int nextByte;
String nextChar;
while (inputStream.ready()) {
nextByte = inputStream.read();
nextChar = Character.toString ((char) nextByte);
partialLine += nextChar;
if ("\n".equals(nextChar)) {
String line = partialLine;
partialLine = "";
return line.replace("\r\n", "");
}
}
return "";
}
Check out http://tutorials.jenkov.com/java-nio/nio-vs-io.html for more info.
Is there anyway I can add a timeout on the BufferedReader?
No, but you can set a timeout on the Socket, with Socket.setSoTimeout().
I have tried multiple solutions like adding a .setSoTimeout() to the socket before creating the BufferedReader but all that did was close the connection/socket after the specified time.
No it doesn't close the socket. It throws SocketTimeoutException, which you should catch and handle as pertinent. If the socket is being closed, you're closing it. Solution: don't.
I'm creating an application which will need to transmit data back and forth between multiple computers on a network. Because of the way the data is to be sent, the client computers will be running the socket server, and the coordinating computer will be running the client socket.
I've created simple classes which are simply intended to encapsulate reading from and writing to these sockets. However, instead of reading anything, the receiving socket simply outputs nothing. I have confirmed that both client and server have a connection.
In the following Server and Client classes, the Socket is made public for debugging purposes only.
public class Server {
public Socket client;
private DataInputStream inStr;
private PrintStream outStr;
public Server() throws UnknownHostException, IOException {this("localhost");}
public Server(String hostname) throws UnknownHostException, IOException {
client = new Socket(hostname, 23);
inStr = new DataInputStream(client.getInputStream());
outStr = new PrintStream(client.getOutputStream());
}
public void send(String data) {outStr.print(data); outStr.flush();}
public String recv() throws IOException {return inStr.readUTF();}
}
The following is my Client:
public class Client {
private ServerSocket serv;
public Socket servSock;
private DataInputStream inStr;
private PrintStream outStr;
public Client() throws IOException {
serv = new ServerSocket(23);
servSock = serv.accept();
inStr = new DataInputStream(servSock.getInputStream());
outStr = new PrintStream(servSock.getOutputStream());
}
public void send(String data) {outStr.print(data); outStr.flush();}
public String recv() throws IOException {return inStr.readUTF();}
}
The Client class is instantiated, and the program started. Then, in a separate program, the Server is instantiated and started:
Server s = new Server(); System.out.println(s.client.isConnected());
while(true) {System.out.println(s.recv()); Thread.sleep(200);}
Client c = new Client(); System.out.println(c.servSock.isConnected());
while(true) {c.send("Hello World!"); Thread.sleep(200);}
isConnected() returns true for both the Client and the Server.
What could be causing this? I've never had to use sockets before now.
DataInputStream.readUTF() expects the first two bytes to be the number of bytes to read, but PrintStream.print(String) will convert the string to bytes and write them as-is.
DataOutputStream.writeUTF(String) will write the length like readUTF() expects.
been trying to figure this problem out for about 5 hours but cant seem to see it, although all the steps are done to send data, I can only receive messages to the server, but not from server to client. I'm in the early stages of building/learning how to do a chat client program in command line. The following is the server code:
The CServer class:
public class CServer {
private static int port=2008, maxConnections=0;
private static String shutDownServer = "no";
public static void main(String[] args) throws IOException{
ServerSocket listen = new ServerSocket(port);
Socket server;
while(shutDownServer.equalsIgnoreCase("no")){
doComm connection;
System.out.println("\nWaiting for clients to connect...");
server = listen.accept(); // accept incomming connections from client
System.out.println("Client connected. Location: " + server.getInetAddress().getHostName());
connection = new doComm(server);
Thread thread = new Thread(connection);
thread.start();
}
}
public void shutDownServer(String command){
this.shutDownServer = command;
}
}
Now the doComm class that handles each client in thread:
public class doComm implements Runnable{
Socket server;
private String clientData;
public doComm(Socket server){
this.server = server;
}
public void run(){
try {
BufferedReader fromClient = new BufferedReader(new InputStreamReader(server.getInputStream()));
DataOutputStream toClient = new DataOutputStream(server.getOutputStream());
clientData = fromClient.readLine();
System.out.println("Client sent: "+clientData);
(( The problem -imo- may be either this statement: ))
toClient.writeBytes("Recieved your sentence '"+clientData+"' and more to come :)!");
//server.close();
} catch (IOException e) {
System.out.println("IOException on socket listen: " + e);
e.printStackTrace();
}
}
}
Now the client class CClient:
public class CClient {
static String address = "localhost";
static int port = 4444;
static Socket echoSocket;
public CClient(int port, String addr){
changePort(port);
changeAddr(addr);
}
public static void main(String[] args) throws IOException, UnknownHostException{
Scanner scan = new Scanner(System.in);
System.out.println("Please enter the port to connect to: ");
int temp_port = Integer.parseInt(scan.nextLine());
System.out.println("Please enter the address of server: ");
System.out.flush();
String temp_addr = scan.nextLine();
CClient client = new CClient(temp_port,temp_addr);
PrintWriter out = null;
BufferedReader in = null;
try{
System.out.flush();
echoSocket = new Socket(address,port);
out = new PrintWriter(echoSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
}
catch(IOException e){
System.err.println("IOException error: " + e.getStackTrace());
}
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println("thingy prints right after this...");
(( or here: ))
System.out.println("echo: " + in.readLine());
}
}
public void changePort(int port){
this.port=port;
}
public void changeAddr(String addr){
this.address=addr;
}
}
clientData = fromClient.readLine();
toClient.writeBytes("Recieved your sentence '"+clientData+"' and more to come :)!");
This is a very common problem whose root cause is the failure to document and specify the protocol being used for communication. Here you are receiving lines but sending bytes. If you had a protocol document, it would either specify that lines were exchanged or that arbitrary units of bytes were exchanged. That would show that one of these lines of code is wrong, and you could fix it. But without a protocol specification, you can't even tell which side is wrong.
Please, take my advice from years of painful lessons -- document a protocol before you implement. Then, if it doesn't work, you can follow this three step process:
Does the client follow the documented protocol? If not, it is broken.
Does the server follow the documented protocol? If not, it is broken.
The protocol specification is broken.
In this case, the protocol specification would document what constitutes a "message" for your protocol. It would then be each side's responsibility to send complete messages and find these message boundaries on receive. However, in your case, one piece of code expects a line terminator to mark a message boundary and the other side doesn't send one.
Is the sender wrong to omit a message boundary? Is the receiver wrong to insist on receiving one? Nobody knows because there's no specification to say what's the right thing to do.
It's a simple client-server where Server using a BufferedWriter object would send to the Client receiving in the object BufferedReader.
When I use OutputStream and PrintWriter for the Server and InputStream and Scanner for the Client it works well.
What happens is that the client in Buffered way reads -1 if I'm sending an int and null for String.
I hope my English makes sense. ;P
That's the code:
Server:
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8189);
Socket incoming;
incoming = server.accept();
try {
// OutputStream output = incoming.getOutputStream();
// PrintWriter outStream = new PrintWriter(output, true /*autoFlush*/);
// outStream.println("ENTER");
BufferedWriter output = new BufferedWriter(new
OutputStreamWriter(incoming.getOutputStream()));
output.write(3);
System.out.println("\nSent");
} finally {
incoming.close();
}
}
}
Client:
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//Client theClient= new Client();
Socket RTSPSocket;
int ServerPort = 8189;
//server name address
String ServerHost = "localhost";
//get Server IP Address
InetAddress ServerIPAddress = InetAddress.getByName(ServerHost);
RTSPSocket = new Socket(ServerIPAddress, ServerPort);
try {
/*
InputStream input = theClient.RTSPSocket.getInputStream();
Scanner in = new Scanner(input);
String line = in.nextLine();
System.out.println(line);
*/
BufferedReader input = new BufferedReader(new
InputStreamReader(RTSPSocket.getInputStream()));
//String line = input.readLine();
//System.out.println("\nRicevuto: " + line);
System.out.println(input.read());
} catch (Exception e) {
System.err.println("Error: " + e);
}
}
}
try putting the following code after output.write(3);
output.flush();
The BufferedOutputStream is built to only send the data when the buffer is full (I believe the default 1024 bytes). So, to force the data to be sent you need to flush the stream.
You have to flush data to receive them in the client part.
output.write(3);
output.flush();
System.out.println("\nSent");
When you have an OutputStream (or a Writer) you have to flush your data, this way you're 100% sure that what you wanted to be send has been sent.
Most (if not all) the OutputStream subclasses use a "mini buffer" which is flushed only when it's full or you manually call flush(). In your case, it's even more flagrant because you're using a BufferedWriter.
Another thing, when you use streams/writers, you really should close them after you're finished, one of the main thing close() do (most of the time) is flushing the last data remaining in the "mini buffer".
Resources :
Javadoc - OutputStream.flush()