So, I'm making a tic tac toe project and I'm having a problem with the network part, it's all finished, only missing the part that connects players with each other, this is the class with the problem:
public class Enemy implements Runnable{
private static Socket enemy;
public Enemy(Socket sock){
enemy = sock;
}
public static void passaJogada(int xPos, int yPos){
try {
PrintWriter saida = new PrintWriter(enemy.getOutputStream());
String x = "" + xPos;
saida.println(x);
String y = "" + yPos;
System.out.print(x + y);
saida.println(y);
} catch (IOException e) {
System.out.println("Ocorreu um erro!");
}
}
public void run() {
try {
BufferedReader entrada = new BufferedReader(new InputStreamReader(enemy.getInputStream()));
while(!EndGameWindow.getEnd()) {
int x = Integer.parseInt(entrada.readLine());
int y = Integer.parseInt(entrada.readLine());
GameWindow.gameButton[x][y].fazerJogada(x,y);
}
entrada.close();
} catch (IOException e) {
System.out.println("Um errro ocorreu!");
}
}
}
and I don't have a clue of what is going on, all I know is that the PrintWriter is writing but the BufferedReader is not reading.
Just ignore the portuguese name of the variables and methods.
See the API for PrintWriter, in particular the single parameter OutputStream constructor you are using:
Creates a new PrintWriter, without automatic line flushing, from an existing OutputStream.
In other words, the PrintWriter is buffered and needs to be flushed. To enable automatic line flushing, use the appropriate constructor
PrintWriter saida = new PrintWriter(enemy.getOutputStream(), true);
...or explicitly flush the PrintWriter:
....
saida.println(y);
saida.flush();
I am assuming that you want players to play with each other even if none of them are sitting on the computer where the server "game" is. In that case what you should do is to separate the game from the networking. I am going to also assume that you have all the game part working like it should. So here is what you can do.
1) First make a class where where only the connecting to the server part is taking place , you can name it server(look below in example code).
2) Make another class to make object of these clients (in your case players).(again look at the code below)
3) Then you can have them interact with the game.
If you are having trouble fixing it, let me know maybe i can help you with more detail.
You simple server part .
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* Created by baljit on 03.05.2015.
* #author Baljit Sarai
*/
public class Server {
ServerSocket serverSocket;
public static List<ClientConnection> clientConnectionList = new ArrayList<ClientConnection>();
public Server() {
try {
serverSocket = new ServerSocket(12345);
System.out.println("Server started");
} catch (IOException ioe) {
System.out.println("Something went wrong");
}
}
public void serve(){
Socket clientSocket;
try {
while((clientSocket = serverSocket.accept()) != null){
System.out.println("got client from"+clientSocket.getInetAddress());
ClientConnection clientConnection = new ClientConnection(clientSocket);
clientConnectionList.add(clientConnection);
ClientHandler clientHandler = new ClientHandler(clientConnection,serverSocket);
clientHandler.start();
}
}catch (IOException ioe){
System.out.println("Something went wrong");
}
}
}
The clientConnection in this case can be the pllayer object you should make just to make things easier for your self.
The clientHandler is the class that can handle each player at the same time.
The "clientConnectionList" here is just to store the connections so you know where to find them.
So here is the ClientConnection class.
import java.io.*;
import java.net.Socket;
/**
* Created by baljit on 03.05.2015.
* #author Baljit Sarai
*/
public class ClientConnection {
private Socket connection;
private DataInputStream streamIn;
private DataOutputStream streamOut;
private BufferedInputStream bufferedInputStream;
public ClientConnection(Socket socket){
connection = socket;
try{
bufferedInputStream = new BufferedInputStream(connection.getInputStream());
streamIn = new DataInputStream(bufferedInputStream);
streamOut = new DataOutputStream(connection.getOutputStream());
}catch (IOException ioe){
System.out.println("Something went wrong when trying create the Connection Object");
}
}
public boolean isBound(){
return connection.isBound();
}
public DataInputStream getStreamIn() {
return streamIn;
}
public DataOutputStream getStreamOut() {
return streamOut;
}
public void close(){
try{
connection.close();
}catch (IOException ioe){
System.out.print("Something went wrong trying to close the connection");
}
}
public String getAddress(){
return connection.getInetAddress().toString();
}
}
And here is the ClientHandler Class
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by baljit on 03.05.2015.
* #author Baljit Sarai
*/
public class ClientHandler extends Thread{
private ServerSocket serverSocket;
String data;
ClientConnection connection;
public ClientHandler(ClientConnection clientConnection, ServerSocket serverSocket) {
connection = clientConnection;
}
public void makeMove(String data){
//This is where you put the logic for how the server can
//interpetate the readed data to a game move
}
#Override
public void run(){
System.out.println("ClientHandler running");
try{
while(connection.isBound()){
data= connection.getStreamIn().readUTF();
this.makeMove(data);
}
connection.close();
}catch (IOException ioe){
System.out.println("Connection dead "+connection.getAddress());
Server.clientConnectionList.remove(connection);
}
}
}
Please let me know how it is working out for you. Maybe i can help you better :) good luck :)
Related
I am trying to learn some network programming, so I thought a good place to start would be with sockets and how to use them. Although it seems that I have hit a brick wall, but the issue does not have as much to do with sockets as it does with checking a socket for two things at (seemingly)the same time.
package com.redab.server;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class server implements Runnable {
private final int portNumber = 4444;
private ServerSocket serverSocket;
private Socket clientSocket;
private Thread thread;
private PrintWriter out;
private BufferedReader in;
private BufferedReader stdIn;
private String incomingText;
private String outgoingText;
private Boolean isRunning = false;
public server() {
thread = new Thread(this, "serverThread");
try {
serverSocket = new ServerSocket(portNumber);
clientSocket = serverSocket.accept(); // Unless socket connection is made, probram will not proceed beyond this line.
System.out.println("connected");
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
stdIn = new BufferedReader(new InputStreamReader(System.in));
} catch (IOException e) {
System.out.println("Exception caught when trying to listen on port " + portNumber + " or listening for a connection");
System.out.println(e.getMessage());
}
}
private synchronized void start() {
thread.start();
isRunning = true;
}
private synchronized void stop() {
try {
thread.join();
isRunning = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
System.out.println("running...");
while (isRunning) {
incoming();
outgoing();
}
}
private synchronized void incoming() {
System.out.println("Incoming");
try {
if ((incomingText = in.readLine()) != null) {
System.out.println(incomingText);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private synchronized void outgoing() {
System.out.println("outgoing");
try {
if ((outgoingText = stdIn.readLine()) != null) {
out.println("Server: " + outgoingText);
System.out.println("Server: " + outgoingText);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
server server = new server();
server.start();
}
}
My problem is the following:
How do I make it so both the method incoming() and outgoing() is ran constantly when I execute the code?
I did google a bit and threads seems to be the solution, so I tried creating a thread which is supposed to run both methods for me. But I run into the same problem again, the code gets stuck in the incoming() method when I want it to simply check this statement ((incomingText = in.readLine()) != null) and then proceed to the method outgoing(). I suspect I might need two threads, one of which checks for incoming messages through the socket and the other checks for outgoing messages that are typed into the console(System.in).
I suspect I might need two threads, one of which checks for incoming messages through the socket and the other checks for outgoing messages that are typed into the console(System.in)
You are right, you need two threads, one per each task.
You might want to check if there is data available to read by using the in.ready() method first. If available, you can read the data using in.readLine(), else do nothing. Currently, in.readLine() blocks because there is no input available on the socket.
So I don't know who to go about creating a multithreaded server. I have client and server working fine together but can't introduce multiple clients properly. Here is my Server code:
package dod;
import java.net.*;
import java.io.*;
import dod.game.GameLogic;
public class Server{
Server(GameLogic game, int port) throws IOException{
ServerSocket ss = null;
Socket sock = null;
try{
ss = new ServerSocket(4444);//port no.
while(true){
try{
sock = ss.accept();
ClientThread thread = new ClientThread(game, sock);
System.out.println("Adding new player...");
thread.run();
}catch(final Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
}catch(Exception d){
System.out.println(d);
}finally{
if(ss!=null){
ss.close();
}
}
}
}
Here is my thread class:
package dod;
import java.io.*;
import java.net.Socket;
import dod.game.GameLogic;
import dod.game.PlayerListener;
public class ClientThread extends CommandLineUser implements PlayerListener, Runnable{
DataInputStream in;
PrintStream out;
// The game which the command line user will operate on.
// This is private to enforce the use of "processCommand".
ClientThread(GameLogic game, Socket sock) {
super(game);
try{
in = new DataInputStream(sock.getInputStream());
out = new PrintStream(sock.getOutputStream());
}catch(IOException ioe){
System.out.println(ioe);
}
game.addPlayer(this);
}
/**
* Constantly asks the user for new commands
*/
public void run() {
System.out.println("Added new human player.");
// Keep listening forever
while(true){
try{
// Try to grab a command from the command line
final String command = in.readLine();;
// Test for EOF (ctrl-D)
if(command == null){
System.exit(0);
}
processCommand(command);
}catch(final RuntimeException e){
System.err.println(e.toString());
System.exit(1);
} catch (final IOException e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
/**
* Outputs a message to the player
*
* #param message
* the message to send to the player.
*/
public void outputMessage(String message) {
out.print(message);
}
}
Not asking for new code as such, just need pointers as to what I need to do have multiple client connection at the same time! Thanks to anyone who helps out!
To start, add new Thread(clientThread) in the server and call start() on it - as is everything's happening on the same thread.
public class Server{
Server(GameLogic game, int port) throws IOException{
ServerSocket ss = null;
Socket sock = null;
try{
ss = new ServerSocket(4444);//port no.
while(true){
try{
sock = ss.accept();
ClientThread thread = new ClientThread(game, sock);
System.out.println("Adding new player...");
thread.start(); //you have to use start instead of run method to create multi thread application.
}catch(final Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
}catch(Exception d){
System.out.println(d);
}finally{
if(ss!=null){
ss.close();
}
}
}
}
You have to use start instead of run method to create multi thread application.
If you want to send messages about new connections you have to hold sock in a list and when a new connection accepted send message to all socket object in list. (server broadcasts to all connected sockets)
I hope it helps.
Hi i am trying to get some data about a socket client connecting to a mutltythreaded server process in the same machine. The server thread is triggered correctly and the client ip is retreived ok, but i cant seem to be able to send a string through the connection.
THE CLIENT
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package solverapplet;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.*;
/**
*
* #author me
*/
public class Solverapplet {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Socket s;
try {
s = new Socket("IP", 4445);
System.out.println(s.getPort());
//DataInputStream in = new DataInputStream (s.getInputStream());
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
out.write("gamma");
out.newLine();
out.flush();
} catch (UnknownHostException ex) {
Logger.getLogger(Solverapplet.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Solverapplet.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
THE SERVER THREAD
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package serversideserver;
import java.io.*;
import java.net.*;
import java.security.*;
import java.util.List;
import java.sql.*;
import com.google.gson.Gson;
class doComms implements Runnable {
private Socket server;
private String line,input,ip;
doComms(Socket server, String ip) {
this.server=server;
this.ip=ip;
}
public void run () {
input="";
try {
// Get input from the client
BufferedReader in = new BufferedReader(
new InputStreamReader(server.getInputStream()));
PrintStream out = new PrintStream(server.getOutputStream());
Connection conn = null;
try
{
String userName = "root";
String password = "";
String url = "jdbc:mysql://localhost/test";
Class.forName ("com.mysql.jdbc.Driver").newInstance ();
conn = DriverManager.getConnection (url, userName, password);
System.out.println ("Database connection established");
// create the java statement
Statement st = conn.createStatement();
// ResultSet rs;
while((line = in.readLine()) != null && !line.equals(".")) {
// Now do the magic.
//Data data = new Gson().fromJson(line, Data.class);
System.out.println("LINE: " + line);
input=line;
st.executeUpdate("UPDATE `solvers` SET `online`='1',`ip`='"+ server.getInetAddress().toString().substring(1) +"' WHERE `user`='"+ line +"'");
// input= data.getcmd();
out.println("{\"captcha\":1,\"text\":\"abc\",\"is_correct\":\"true\"}");
}
}
catch (Exception e)
{
System.out.println (e.toString());
}
// Now write to the client
System.out.println("UPDATE `solvers` SET `online`='1',`ip`='"+ server.getInetAddress() +"' WHERE `user`='"+ input +"'");
//out.println("Overall message is:" + input);
server.close();
} catch (IOException ioe) {
System.out.println("IOException on socket listen: " + ioe);
ioe.printStackTrace();
}
}
}
The line sent through is empty.
CONNECTION ESTABLISHED
package serversideserver;
import java.io.*;
import java.net.*;
import java.security.*;
import java.sql.*;
/**
* Title: Sample Server
* Description: This utility will accept input from a socket, posting back to the socket before closing the link.
* It is intended as a template for coders to base servers on. Please report bugs to brad at kieser.net
* Copyright: Copyright (c) 2002
* Company: Kieser.net
* #author B. Kieser
* #version 1.0
*/
public class Serversideserver {
private static int port=4445,portsolver=4445, maxConnections=0;
// Listen for incoming connections and handle them
public static void main(String[] args) {
int i=0;
try{
ServerSocket listener = new ServerSocket(port);
Socket server;
long counter=0;
int counter1=0;
int id=0;
String ip="uninit";
while((i++ < maxConnections) || (maxConnections == 0)){
server = listener.accept();
counter++;
doComms conn_c= new doComms(server,ip);
Thread t = new Thread(conn_c);
t.start();
//System.out.println("counter "+ (counter % id) );
}
} catch (IOException ioe) {
System.out.println("IOException on socket listen: " + ioe);
ioe.printStackTrace();
}
}
}
I looks like you've got a bit of a timing issue. The following is your code with correct timing. Note that I removed code unnecessary to the issue at hand.
Client: It looks like in the client, you were writing to the socket and immediately terminating the application (causing the connection to close). The doComms class was writing back to the client so I've added code to read the response. If, however, you were not expecting a response, you would still want to read in a byte. This will allow you to make sure you got the EOF rather than some data, and it blocks the current thread and keeps the connection alive.
package solverapplet;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.*;
/**
*
* #author you
*/
public class Solverapplet {
/**
* #param args the command line arguments
* #throws IOException
*/
public static void main(String[] args) throws IOException {
Socket s = null;
try {
// make connection
s = new Socket("localhost", 4445);
// define streams
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
// write data
out.write("gamma");
out.newLine();
out.flush();
// read response
String returnData = in.readLine();
System.out.println(returnData);
} catch (UnknownHostException ex) {
Logger.getLogger(Solverapplet.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Solverapplet.class.getName()).log(Level.SEVERE, null, ex);
} finally {
// close connection
s.close();
}
}
}
Server: I've changed the server to allow a maximum number of connections at any given time rather than shutting down after the maximum number of connections has been established. Also, note that I the threads are not daemons. If you want to serve X number clients then shutdown, you need a mechanism to allow the threads to continue executing before shutting down ServerSocket
package serversideserver;
import java.io.*;
import java.net.*;
/**
* Title: Sample Server
* Description: This utility will accept input from a socket, posting back to the socket before closing the link.
* It is intended as a template for coders to base servers on. Please report bugs to brad at kieser.net
* Copyright: Copyright (c) 2002
* Company: Kieser.net
* #author B. Kieser
* #version 1.0
*/
public class Serversideserver {
private static int port =4445;
private static int maxConnections =10;
private static int connections = 0;
synchronized static void connectionClosed() {
connections--;
Serversideserver.class.notify();
}
/**
* The blocking mechanism to only allow <code>maxConnections<code>
* #throws InterruptedException
* thrown if blocking thread is interupted
*/
private synchronized static void nextConnection() throws InterruptedException {
while(connections>=maxConnections) {
Serversideserver.class.wait();
}
}
public static void main(String[] args) throws InterruptedException {
try{
// server socket
ServerSocket listener = new ServerSocket(port);
// socket
Socket socket;
// Keep the server alive
while(true){
// Blocks if we have reached the max
nextConnection();
// Accept connection to client
socket = listener.accept();
// define request service
doComms conn_c= new doComms(socket,socket.getInetAddress().getCanonicalHostName());
Thread t = new Thread(conn_c);
t.setDaemon(false);
// run request service
t.start();
}
} catch (IOException ioe) {
System.out.println("IOException on socket listen: " + ioe);
ioe.printStackTrace();
}
}
}
doComms: Not much has changed with this class... I just cleaned it up a bit and removed unnecessary lines of code.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package serversideserver;
import java.io.*;
import java.net.*;
class doComms implements Runnable {
private Socket socket;
private String ip;
doComms(Socket socket, String ip) {
this.socket = socket;
this.ip = ip;
}
public void run () {
try {
// Define input/output
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintStream out = new PrintStream(socket.getOutputStream());
// Process requests until the EOF is found
String line;
while((line = in.readLine()) != null && !line.equals(".")) {
// print input
System.out.println("LINE: " + line);
// print process line
System.out.println("UPDATE `solvers` SET `online`='1',`ip`='"+ ip +"' WHERE `user`='"+ line +"'");
// write response
out.println("{\"captcha\":1,\"text\":\"abc\",\"is_correct\":\"true\"}");
}
socket.close();
} catch (IOException ioe) {
System.out.println("IOException on socket listen: " + ioe);
ioe.printStackTrace();
} finally {
Serversideserver.connectionClosed();
}
}
}
Hope this helps :)
Your question is seriously vague, but if you're wondering why the server can't reply to your client, it's because the client never reads from the socket.
I've got codes of a server and clients written on Java. But the question is how to run multiple clients on DIFFERENT console-windows with Eclipse when server is running? Thx for helping!
(solved!!)
UPDATE**
Another question: I'll create a new question
Server:
import java.net.*;
import java.io.*;
public class ATMServer {
private static int connectionPort = 8989;
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
boolean listening = true;
try {
serverSocket = new ServerSocket(connectionPort);
} catch (IOException e) {
System.err.println("Could not listen on port: " + connectionPort);
System.exit(1);
}
System.out.println("Bank started listening on port: " + connectionPort);
while (listening)
new ATMServerThread(serverSocket.accept()).start();
serverSocket.close();
}
}
ServerThread:
import java.io.*;
import java.net.*;
public class ATMServerThread extends Thread {
private Socket socket = null;
private BufferedReader in;
PrintWriter out;
public ATMServerThread(Socket socket) {
super("ATMServerThread");
this.socket = socket;
}
public void run(){
}
}
}
Client: (**UPDATE)
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class ATMClient {
private static int connectionPort = 8989;
public static void main(String[] args) throws IOException {
Socket ATMSocket = null;
PrintWriter out = null;
BufferedReader in = null;
String adress = "";
try {
adress = "127.0.0.1";
} catch (ArrayIndexOutOfBoundsException e) {
System.err.println("Missing argument ip-adress");
System.exit(1);
}
try {
ATMSocket = new Socket(adress, connectionPort);
out = new PrintWriter(ATMSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader
(ATMSocket.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Unknown host: " +adress);
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't open connection to " + adress);
System.exit(1);
}
out.close();
in.close();
ATMSocket.close();
}
You can run as many socket clients from Eclipse provided that you pass user-defined ip/port info as command arguments from main() under Program Arguments tab in Run Configuration dialog for that program inside Eclipse rather than using some hardwired values for ip/port.
To create multiple console views (via separate Console View tabs rather than clicking on each instance), you need to create a new console view for each target instance in Eclipse Debug View mode; to achieve this, you need to select "New Console View" (from the icon button with the plus symbol to the far right of the Console View) and assign which program instance to view from each new console.
Another question: if I have to change something on ServerThread, for example, add a title, is that possible to execute that without restart the server?
Which title? I don't see any GUI code for the ServerThread code snippet. Do you mean the title name of the Console view tab?
I'm trying to handle multiple connections on the same port of my server. I'm doing this by instantiating an Object and passing it into the constructor for another class, which implements Runnable. Then I set up a socket in the Runnable class and call notify() on the passed Object after a Client connects on the port. This should then allow the server to restart its loop, creating another instance of the Runnable class after being notified. However, currently the wait() isnt being reached until after the client is closed. Here are the 3 relevant classes I have:
Server class:
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.HashMap;
public class Server {
public static void main(String[] args){
HashMap<String, PortDummy> portDummies = new HashMap<String, PortDummy>();
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8000);
} catch (IOException e1) {
e1.printStackTrace();
}
for(;;){
Object block = new Object();
PortDummy dummy = new PortDummy(serverSocket, block, portDummies);
System.out.println("Running dummy port...");
dummy.start();
try {
synchronized(block){
System.out.println("Waiting...");
block.wait();
System.out.println("Block notified.");
}
} catch (InterruptedException e) {
System.out.println("Can't be interrupted!");
e.printStackTrace();
}
}
}
}
PortDummy (Runnable) class:
package server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
public class PortDummy extends Thread {
private Object block;
private HashMap<String, PortDummy> portDummies;
private String clientName = null;
ServerSocket serverSocket;
BufferedReader socketIn;
PrintWriter socketOut;
public PortDummy(ServerSocket serverSocket, Object block, HashMap<String, PortDummy> portDummies){
this.block = block;
this.portDummies = portDummies;
this.serverSocket = serverSocket;
}
#Override
public void run() {
try {
System.out.println("Starting dummy port...");
Socket clientSocket = serverSocket.accept();
System.out.println("Connection made.");
synchronized(block){
System.out.print("Notifying...");
block.notify();
System.out.println("...done.");
}
socketIn = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
socketOut = new PrintWriter(clientSocket.getOutputStream(), true);
String inContent;
boolean loggedIn = false;
while((inContent = socketIn.readLine()) != null){
socketOut.println("Server Echo: " + inContent);
if(inContent.startsWith("/exit")){
if(loggedIn){
portDummies.remove(clientName);
System.out.println(clientName + " signed out. Removed from portDummies.");
}
else{
System.out.println("Closing...");
}
}
else if(inContent.startsWith("/register")){
System.out.println("/register accepted");
if(!loggedIn){
if(registerUser(inContent)){
System.out.println("Successfully registered.");
socketOut.println(clientName + " successfully registered.");
loggedIn = true;
}else{
socketOut.print("That user already exists.");
}
}
else{
socketOut.print("Already logged in.");
}
}
else if(inContent.startsWith("/tell")){
if(!loggedIn){
socketOut.println("You need to log in.");
}
else{
String[] parts = inContent.split("\\w");
String[] withoutCommand = new String[parts.length-1];
for(int i = 1; i<parts.length-1; i++){
withoutCommand[i] = parts[i];
}
String[] messageParts = new String[withoutCommand.length-1];
String message = "";
for(int j = 1; j<withoutCommand.length-1; j++){
message += withoutCommand[j] + " ";
}
String recipient = withoutCommand[0];
sendMessage(recipient, message);
}
}
else if(inContent.startsWith("/help")){
socketOut.print("/help ~~~~~~~ List all commands. \n " +
"/register <username> ~~~~~~~ Register a new user with 'username'. \n " +
"/tell <username> <message> ~~~~~~~ Send 'username' text 'message'. \n " +
"/exit ~~~~~~~ Log out.");
}
}
System.out.println("Shutting down client connections...");
socketOut.close();
socketIn.close();
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
System.out.println("IOException!");
e.printStackTrace();
}
}
private boolean registerUser(String text){
System.out.println("Registering user...");
String user = text.substring(10);
if((user != null) && !(portDummies.containsKey(user))){
portDummies.put(user, this);
clientName = user;
System.out.println(user + " registered.");
return true;
}
return false;
}
private void sendMessage(String username, String message){
if(portDummies.containsKey(username)){
PortDummy recip = portDummies.get(username);
recip.getSocketOutput().println(clientName + ": " + message);
}
else{
socketOut.write("User " + username + " doesn't exist.");
}
}
public PrintWriter getSocketOutput(){
return socketOut;
}
}
Client class:
package client;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
import java.io.IOException;
public class Client {
protected String username;
public static void main(String[] args){
try{
Socket serverSocket = new Socket("localhost", 8000);
BufferedReader socketIn = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
PrintWriter socketOut = new PrintWriter(serverSocket.getOutputStream(), true);
Scanner keyboardInputScanner = new Scanner(System.in);
String keyboardInput, serverInput;
System.out.println("Welcome to Chris Volkernick's Server IM Client! \n" +
"Type '/register <username>' to register, '/list' to list connected users," +
"\n or '/tell <username> <message>' to send a user a message. '/help' lists these commands. (Type '/exit' to sign out.)");
while((keyboardInput = keyboardInputScanner.nextLine()) != null){
System.out.println("Input '" + keyboardInput + "' read on client side.");
if(keyboardInput.equals("/exit")){
socketOut.println("/exit");
socketOut.close();
socketIn.close();
serverSocket.close();
}else{
socketOut.println(keyboardInput);
while((serverInput = socketIn.readLine()) != null){
System.out.println(serverInput);
}
}
}
keyboardInputScanner.close();
}catch(IOException e){
System.out.println("IOException!");
e.printStackTrace();
}
}
}
Am I doing something wrong with the wait() and/or notify()?
EDIT: I also tried changing the implements Runnable to extends Thread then changing the .run() in the server to .start(), but that gives me this error:
java.net.BindException: Address already in use: JVM_Bind
at java.net.PlainSocketImpl.socketBind(Native Method)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:365)
at java.net.ServerSocket.bind(ServerSocket.java:319)
at java.net.ServerSocket.<init>(ServerSocket.java:185)
at java.net.ServerSocket.<init>(ServerSocket.java:97)
at server.PortDummy.run(PortDummy.java:28)
EDIT 2: It seems to be working the way it should now in terms of starting new threads. However, I'm now running into another problem: After I enter a command on the client side of any given client, I can't enter additional commands. The first command will work fine (minus /exit; haven't quite figured out how that should work yet), just can't do anything after that. For example, I can register (sign in) but after that nothing else. I can go into another instance of Client and list all the current users (works), but again, after that I cannot enter additional commands. Any idea what may be happening to cause this?
The problem is that your child threads are trying to listen on port 8000, but the parent thread is already doing that. What you need to do is pass accept a connection from the original socket and then give it to the child thread. I'm not exactly sure how to do this in Java, but I suspect it's just..
Put this in your main thread:
ServerSocket serverSocket = new ServerSocket(8000);
Socket clientSocket = serverSocket.accept();
And then once you get that, pass clientSocket to your Thread.
That way there's only one socket listening on port 8000, but you can make child threads handle each connection.
When using wait and notify, realize that notifies aren't queued, so if the notify happens before the wait occurs, you will never exit the wait. Therefore you should never perform naked waits, that is there should always be some condition you test to see if you should wait.
sychronized(block) {
while (!available) {
block.wait();
}
}
and
synchronized(block) {
available = true;
block.notifyAll();
}
etc
package so_7775790;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Logger;
/**
* barebones server -- ctl-C to kill it ;)
*/
public class Server implements Runnable {
final static Logger log = Logger.getLogger(Server.class.getSimpleName());
public static void main(String[] args) {
final int port = 8000;
final String tname = "my-server-thread";
final Server server = new Server(port);
try {
Thread tserver = new Thread(server, tname);
tserver.start();
tserver.join();
} catch (Exception e) {
log.severe(e.getMessage());
}
}
// -------------------------------------------------
// server
// -------------------------------------------------
final int port;
public Server(int port) {
this.port = port;
}
public void run() {
try{
final ServerSocket srvsocket = new ServerSocket(port);
log.info(String.format("Server started # %s\n", srvsocket));
while(!Thread.currentThread().isInterrupted()){
Socket newclient = srvsocket.accept();
// spawn thread and hand off new client to handler
new Thread(new ClientHandler(newclient)).start();
}
}
catch (Exception e) {
log.severe(e.getMessage());
}
log.info("server stopped");
}
// -------------------------------------------------
// client handler
// -------------------------------------------------
static class ClientHandler implements Runnable {
final Socket socket;
public ClientHandler(final Socket socket) {
assert socket != null : "serverthread is null";
this.socket = socket;
}
#SuppressWarnings("unused")
#Override final
public void run() {
log.info(String.format("new client # %s\n", socket.getRemoteSocketAddress()));
try {
final InputStream in = socket.getInputStream();
final OutputStream out = socket.getOutputStream();
// NOTE: this is just a stub busy loop!
for(;;) {
/* your protocol impl here .. */
}
} catch (Exception e) {
log.severe(e.getMessage());
}
finally {
try {
socket.close();
}
catch (Exception e) {
log.severe(e.getMessage());
}
}
}
}
}