So basically what I want to do is: Create a very simple multithreaded TCP server that can connect to several clients at once. This using threads and transferring messages through Byte[] and returning an echo of the message.
I have never touched anything related to server programming or TCP before, so I expect to have made a lot of mistakes. I am open for improvement and suggestions.
I made a simple Server class:
public class TCPEchoServer {
public static final int SERVERPORT = 4950;
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(SERVERPORT);
while (true) {
Socket clientSocket = serverSocket.accept();
Runnable connectionHandler = new TCPConnectionHandler(clientSocket);
new Thread(connectionHandler).start();
}
}
}
And the connection handler class:
public class TCPConnectionHandler implements Runnable {
private final Socket clientSocket;
private int msgLength = 0;
private byte[] data;
public TCPConnectionHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
#Override
public void run() {
try {
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(clientSocket.getOutputStream());
InputStream in = clientSocket.getInputStream();
DataInputStream dis = new DataInputStream(in);
msgLength = dis.readInt();
data = new byte[msgLength];
if (msgLength > 0) {
dis.readFully(data);
}
String message = inFromClient.readLine();
System.out.println("Message recieved: " + message);
outToClient.writeBytes(String.valueOf(data));
clientSocket.close();
}
catch (IOException e) {
System.out.printf("Could not listen on port: " + clientSocket.getLocalPort());
}
}
}
And the Client class:
public class TCPEchoClient {
public static final int MYPORT = 0;
public static int BUFFSIZE = 0;
public static Socket socket;
public static final String MSG = "An Echo Message! LOL";
public static String RETURNMSG = "";
public static final byte[] messageByteArr = MSG.getBytes(Charset.forName("UTF-8"));
private static final Pattern PATTERN = Pattern.compile(
"^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
public static void main(String[] args) throws IOException, InterruptedException {
if (args.length != 4) {
System.err.printf("ERROR: The EchoClient expects 4 parameter inputs:");
System.out.printf("IP address, Port number, message rate (msg/second).");
System.exit(1);
}
if (!isValidIP(args[0])) {
System.out.printf("ERROR: The entered IP address is not a valid IPv4 address.");
System.out.printf("Please enter a valid IPv4 address as the first argument.");
System.exit(1);
}
if (Integer.parseInt(args[1]) < 0 || Integer.parseInt(args[1]) > 65535) { //If the portnumber is negative or bigger than the highest portnumber (unsigned 16-bit integer)
System.out.printf("ERROR: The chosen portnumber is outside the available range.");
System.out.printf("Expected portnumbers: 0-65535");
System.exit(1);
}
if (Integer.parseInt(args[3]) < messageByteArr.length) {
System.out.println("Buffer size can not be smaller than the message size.");
System.out.println("Current message size: " + messageByteArr.length);
System.exit(1);
}
BUFFSIZE = Integer.parseInt(args[3]);
byte[] buf = new byte[BUFFSIZE];
DataOutputStream outToServer = null;
BufferedReader serverEcho = null;
try {
socket = new Socket(args[0], Integer.parseInt(args[1]));
outToServer = new DataOutputStream(socket.getOutputStream());
serverEcho = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (UnknownHostException e) {
System.out.println("Unknown host address: " + args[0]);
System.exit(1);
} catch (IOException e) {
System.out.println("Could not access port " + Integer.parseInt(args[1]));
System.exit(1);
}
int msgLength = 0;
int msgStart = 0;
if (msgLength < 0) {
throw new IllegalArgumentException("Negative length not allowed.");
}
if (msgStart < 0 || msgStart >= messageByteArr.length) {
throw new IndexOutOfBoundsException("Out of bounds: " + msgStart);
}
for (int i = 1; i <= Integer.parseInt(args[2]); i++) {
outToServer.writeInt(msgLength);
if (msgLength > 0) {
outToServer.write(messageByteArr, msgStart, msgLength);
System.out.println("Message sent: " + MSG);
}
RETURNMSG = serverEcho.readLine();
System.out.println("ECHO MESSAGE: " + RETURNMSG);
}
socket.close();
}
private static boolean isValidIP(final String ip) {
return PATTERN.matcher(ip).matches();
}
}
I ran into a problem just now as well, the print outs worked before on the client and sever, but now when a message is sent. Nothing happens at all.
My main question is how I can incorporate a buffer and use it when sending and receiving messages.
You have a loop in the client that will wait args[2] times the RETURNMSG, the server will send this message only once. The RETURNMSG reading code should be outside (after) the for loop.
You should flush the buffer once you've finished writing in it.
This should be done in both client and server, since your client will wait the RETURNMSG forever depending how your protocol will evolve in the future.
Related
there is a server that is considered to server multiple clients at the same time.
So when clients connects, he is added to clients array. And when server gets the message, it is sent to all the clients.
It works perfectly when one client is connected, but when I have 2 clients at the same time, the message is sent only once, it doesn't work anymore after that. What's the problem?
Server
static DataInputStream inputStream;
static DataOutputStream outputStream;
static ServerSocket serverSocket;
static final int PORT = 3003;
static Socket someClient;
static List<Socket> clients = new ArrayList<>();
public Server()
{
start();
}
public static void main(String[] args) throws IOException
{
try{
serverSocket = new ServerSocket(PORT);
print("Server started on " + serverSocket.getInetAddress().getHostAddress());
while (true)
{
someClient = serverSocket.accept();
new Server();
}
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void run()
{
try{
clients.add(someClient);
print("Connected from " + someClient.getInetAddress().getHostAddress());
InputStream sin = someClient.getInputStream();
OutputStream sout = someClient.getOutputStream();
inputStream = new DataInputStream(sin);
outputStream = new DataOutputStream(sout);
String message;
while (true)
{
message = inputStream.readUTF();
print(message);
for (int i = 0; i < clients.size(); i++)
{
Socket client = clients.get(i);
OutputStream os = client.getOutputStream();
DataOutputStream oss = new DataOutputStream(os);
oss.writeUTF(message);
}
}
} catch (Exception e){
e.printStackTrace();
}
}
Client
socket = new Socket("0.0.0.0", 3003);
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
inputStream = new DataInputStream(sin);
outputStream = new DataOutputStream(sout);
sendButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(key != null && key.length() == 16)
{
Date date = new Date();
String msg = ">> " + nickname + ": " + messageField.getText()+" | " + date.getHours()+":"+date.getMinutes()+"\n";
try {
outputStream.writeUTF(Encrypt.AESEncrypt(key, msg));
} catch (IOException e1) {
e1.printStackTrace();
}
messageField.setText("");
}
else if(key == null)
JOptionPane.showMessageDialog(J_Frame, "Your key field is empty");
else if(key.length() != 16)
JOptionPane.showMessageDialog(J_Frame, "Key's length should be 16 symbols");
}
});
while (true)
{
String message;
message = inputStream.readUTF();
append("\n" + Encrypt.AESDecrypt(key, message));
}
} catch (Exception e1) {
clear();
append(">> Unable to connect to the server.");
hideButtons();
}
Every time a client connects to your server, it replaces the previous connection:
while (true)
{
someClient = serverSocket.accept();
...
}
someClient is static:
static Socket someClient;
which means it is shared by all threads.
Also, access to it is not synchronized in any way, which means changes to its value are not guaranteed to be visible to other threads.
As Peter Lawrey pointed out in the comments, the streams also need to be non-static:
static DataInputStream inputStream;
static DataOutputStream outputStream;
actually, the fact that you are always reading from the "latest" inputStream may be the main cause of the behavior you are describing.
outputStream seems to be unused, so it might be best to remove it.
In addition to that, OutputStreams may need to be flushed in order to actually send data.
I have this class where i try to create a number of threads and then i need to send a message to the server over all the threads or using a specific thread. I can't seem to find a way to do that in this way I can only send using the last thread that was created.
Here is the class:
public class test{
public test(){
}
public static void main(String[] args){
MultiThreadChatClient mc = new MultiThreadChatClient();
test st = new test();
for(int i =0; i<=4; i++){
mc.createThreads();
}
while (true) {
System.out.print("type your message: ");
Scanner s = new Scanner(System.in);
String ss = s.nextLine();
ss = ss.trim().replaceAll(" +", " ");
mc.sendMessage(ss);
try
{
Thread.sleep(400);
}
catch(InterruptedException e)
{
// this part is executed when an exception (in this example InterruptedException) occurs
System.out.println("Exeption: " + e);
}
}
}
}
Here's the Client thread class:
public class MultiThreadChatClient implements Runnable {
// The client socket
private static Socket clientSocket = null;
// The output stream
private static PrintStream os = null;
// The input stream
private static BufferedReader br;
private static BufferedReader inputLine = null;
private static boolean closed = false;
public static void main(String[] args) {
// The default port.
int portNumber = 2222;
// The default host.
String host = "localhost";
if (args.length < 2) {
System.out.println("Usage: java MultiThreadChatClient <host> <portNumber>\n"
+ "Now using host=" + host + ", portNumber=" + portNumber);
} else {
host = args[0];
portNumber = Integer.valueOf(args[1]).intValue();
}
}
/*
* Open a socket on a given host and port. Open input and output streams.
*/
public void createThreads(){
try {
clientSocket = new Socket("localhost", 2222);
inputLine = new BufferedReader(new InputStreamReader(System.in));
os = new PrintStream(clientSocket.getOutputStream());
br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about host " + 2222);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to the host " + 2222);
}
//}
/*
* If everything has been initialized then we want to write some data to the
* socket we have opened a connection to on the port portNumber.
*/
if (clientSocket != null && os != null && br != null) {
new Thread(new MultiThreadChatClient()).start();
}
}
public void sendMessage(String mes){
os.println(mes);
}
/*
* Create a thread to read from the server. (non-Javadoc)
*
* #see java.lang.Runnable#run()
*/
public void run() {
/*
* Keep on reading from the socket till we receive "Bye" from the
* server. Once we received that then we want to break.
*/
String responseLine;
try {
while ((responseLine = br.readLine()) != null){
System.out.println(responseLine);
if (responseLine.indexOf("*** Bye") != -1)
break;
}
closed = true;
} catch (IOException e) {
System.err.println("IOException1234: " + e);
}
}
}
Here's the server code:
public class MultiThreadChatServerSync {
// The server socket.
private static ServerSocket serverSocket = null;
// The client socket.
private static Socket clientSocket = null;
// This chat server can accept up to maxClientsCount clients' connections.
private static final int maxClientsCount = 50;
private static final clientThread[] threads = new clientThread[maxClientsCount];
public static void main(String args[]) {
// The default port number.
int portNumber = 2222;
if (args.length < 1) {
System.out.println("Usage: java MultiThreadChatServerSync <portNumber>\n"
+ "Now using port number=" + portNumber);
} else {
portNumber = Integer.valueOf(args[0]).intValue();
}
/*
* Open a server socket on the portNumber (default 2222). Note that we can
* not choose a port less than 1023 if we are not privileged users (root).
*/
try {
serverSocket = new ServerSocket(portNumber);
//System.out.println(serverSocket.getPort());
} catch (IOException e) {
System.out.println(e);
}
/*
* Create a client socket for each connection and pass it to a new client
* thread.
*/
while (true) {
try {
clientSocket = serverSocket.accept();
int i = 0;
for (i = 0; i < maxClientsCount; i++) {
if (threads[i] == null) {
(threads[i] = new clientThread(clientSocket, threads)).start();
//System.out.println("A new client is created");
break;
}
}
if (i == maxClientsCount) {
PrintStream os = new PrintStream(clientSocket.getOutputStream());
os.println("Server too busy. Try later.");
os.close();
clientSocket.close();
}
} catch (IOException e) {
System.out.println(e);
}
}
}
}
class clientThread extends Thread {
MultiThreadChatServerSync ms = new MultiThreadChatServerSync();
private String clientName = null;
//private DataInputStream is = null;
private BufferedReader br = null;
private PrintStream os = null;
private Socket clientSocket = null;
private final clientThread[] threads;
private int maxClientsCount;
public clientThread(Socket clientSocket, clientThread[] threads) {
this.clientSocket = clientSocket;
this.threads = threads;
maxClientsCount = threads.length;
//System.out.println("Inside the Client thread");
}
public void run() {
MultiThreadChatServerSync mss = new MultiThreadChatServerSync();
int maxClientsCount = this.maxClientsCount;
clientThread[] threads = this.threads;
//System.out.println("Inside the run");
try {
/*
* Create input and output streams for this client.
*/
br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
os = new PrintStream(clientSocket.getOutputStream());
while (true) {
String line = br.readLine();
System.out.println("message received via Client port: " + clientSocket.getPort());
System.out.println("Received: " + line);
}
} catch (IOException e) {
}
}
}
So, is there a way to create 5 threads and then send the message over all of them or choose one specific thread out of them?
To be able to have a client which starts several sessions, and to be able to send data to one or several of them, the client must change in a few ways.
The static references in the client must be removed.
The Socket instances must be created and stored in the main thread (the writers will be used to send messages on behalf of one or more client sessions)
Each client session is now only responsible for reading (the reader is passed via the constructor).
The code is based on your original code, and meant to be a template for further enhancements: It will manage 5 client sessions, and be able to write to 1 or many.
The new main Thread (As an example, the Scanner will first ask the message for client #1, then 2, 3, 4 and next will broadcast to all, in a loop):
public class MultiThreadChatClientRunner {
final int NO_CLIENTS = 5;
//final String HOST_IP = "192.168.2.7";
final String HOST_IP = "localhost";
public static void main(String[] args) {
new MultiThreadChatClientRunner().start();
}
private void start() {
Socket[] sockets = new Socket[NO_CLIENTS];
PrintStream[] writers = new PrintStream[NO_CLIENTS];
BufferedReader[] readers = new BufferedReader[NO_CLIENTS];
for (int i = 0; i < NO_CLIENTS; i++) {
System.out.println("Creating client number "+i);
Socket clientSocket;
try {
clientSocket = new Socket(HOST_IP, 2222);
writers[i] = new PrintStream(clientSocket.getOutputStream());
readers[i] = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
new Thread(new MultiThreadChatClient(i, readers[i])).start();
} catch (IOException e) {
e.printStackTrace();
}
}
int clientId = 0;
Scanner s = new Scanner(System.in);
while (true) {
System.out.print("type your message for client #"+clientId);
String ss = s.nextLine();
ss = ss.trim().replaceAll(" +", " ");
writers[clientId].println(ss);
clientId = (clientId+1)%NO_CLIENTS;
// Test to broadcast to all clients
if (clientId == 4) {
for (int i = 0; i<NO_CLIENTS; i++)
writers[i].println("Broadcast message: "+ss);
}
try {
Thread.sleep(400);
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
break;
}
}
s.close();
}
}
The new client, which is much simpler than before:
public class MultiThreadChatClient implements Runnable {
// The client socket
private Socket clientSocket = null;
// The output stream
private PrintStream os = null;
// The input stream
private final BufferedReader br;
private final int clientId;
public MultiThreadChatClient(int clientId, BufferedReader br) {
super();
this.clientId = clientId;
this.br = br;
}
/*
* Create a thread to read from the server. (non-Javadoc)
*
* #see java.lang.Runnable#run()
*/
public void run() {
/*
* Keep on reading from the socket till we receive "Bye" from the
* server. Once we received that then we want to break.
*/
String responseLine;
try {
while ((responseLine = br.readLine()) != null) {
System.out.printf("Client #%d received message=%s\n", clientId, responseLine);
if (responseLine.indexOf("*** Bye") != -1)
break;
}
} catch (IOException e) {
System.err.println("IOException1234: " + e);
}
}
}
And the server, which is nearly identical, but manages closes better, and has some more logging to show which client is communicating.
public class MultiThreadChatServerSync {
// The server socket.
private static ServerSocket serverSocket = null;
// The client socket.
private static Socket clientSocket = null;
// This chat server can accept up to maxClientsCount clients' connections.
private static final int maxClientsCount = 50;
private static final ClientThread[] threads = new ClientThread[maxClientsCount];
public static void main(String args[]) {
// The default port number.
int portNumber = 2222;
if (args.length < 1) {
System.out.println(
"Usage: java MultiThreadChatServerSync <portNumber>\n" + "Now using port number=" + portNumber);
} else {
portNumber = Integer.valueOf(args[0]).intValue();
}
/*
* Open a server socket on the portNumber (default 2222). Note that we
* can not choose a port less than 1023 if we are not privileged users
* (root).
*/
try {
serverSocket = new ServerSocket(portNumber);
// System.out.println(serverSocket.getPort());
} catch (IOException e) {
System.out.println(e);
}
/*
* Create a client socket for each connection and pass it to a new
* client thread.
*/
while (true) {
try {
System.out.println("Awaiting a new connection on "+serverSocket.getLocalPort());
clientSocket = serverSocket.accept();
int i = 0;
for (i = 0; i < maxClientsCount; i++) {
if (threads[i] == null) {
(threads[i] = new ClientThread(i, clientSocket)).start();
// System.out.println("A new client is created");
break;
}
}
if (i == maxClientsCount) {
PrintStream os = new PrintStream(clientSocket.getOutputStream());
os.println("Server too busy. Try later.");
os.close();
clientSocket.close();
}
} catch (IOException e) {
System.out.println(e);
}
}
}
}
class ClientThread extends Thread {
private BufferedReader br = null;
private PrintStream os = null;
private final Socket clientSocket;
private final int clientId;
public ClientThread(int clientId, Socket clientSocket) {
this.clientSocket = clientSocket;
this.clientId = clientId;
}
public void run() {
try {
/*
* Create input and output streams for this client.
*/
br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
os = new PrintStream(clientSocket.getOutputStream());
String line = "";
while ((line = br.readLine()) != null) {
System.out.printf("Client <%d> received message=<%s> via Client port: <%d>\n", clientId, line, clientSocket.getPort());
// Echo it back (as a test)
os.println(line);
}
br.close();
os.close();
clientSocket.close();
} catch (IOException e) {
}
System.out.println("Client has closed the session");
}
}
Let me know if you have any further questions at this time.
I send information to the server, the server hangs for all eternity, any guidance is much appreciated. I think the problem is with one of the conidtional statements or the dataoutputstream but idk.
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public Client(Socket newSocket, int ID) {
// Set properties
id = ID;
socket = newSocket;
connected = true;
// Get input
inport = new Inport();
inport.start();
}
public static int conversion(int serverValue) {
if (serverValue >= 10) {
return 10;
} else
return serverValue;
}
/**
* Handles all incoming data from this user.
*/
private class Inport extends Thread {
private ObjectInputStream in;
//private DataInputStream in;
public void run() {
// Open the InputStream
try {
in = new ObjectInputStream(socket.getInputStream());
} catch (IOException e) {
System.out.println("Could not get input stream from " + toString());
return;
}
// Announce
System.out.println(socket + " has connected input.");
// Enter process loop
while (true) {
// Sleep
try {
} catch (Exception e) {
System.out.println(toString() + " has input interrupted.");
}
}
}
}
static String standOrHit = "";
private Socket socket;
private boolean connected;
private Inport inport;
static int serverValue;
static String serverName = "127.0.0.1"; //Our Server Name
static String selection = "";
static int port = 9999; //Our Server
static Scanner keyboard = new Scanner(System.in);
static boolean isTurn = true;
static boolean isGameOver = false;
static int score = 0;
static String sh = "It's your turn. Enter 'H' for Hit or 'S' for stand) \n Score:" + score;
static int id = 500;
public static void main(String[] args) throws IOException {
System.out.println("Fetching server connection...");
Socket client = new Socket(serverName, port); //Create a Client Socket and attempt to connect to the server # port
System.out.println("Connection Established.");
System.out.println("Fetching Player ID...");
System.out.println("Game Found you're Player " + 3 + ".");
System.out.println("Waiting for server...");
OutputStream outputStream = client.getOutputStream(); //Create a stream for sending data
DataOutputStream out = new DataOutputStream(outputStream); //Wrap that stream around a DataOutputStream
//InputStream is used for reading
InputStream inputStream = client.getInputStream(); //Read the incoming stream as bytes
DataInputStream dataInputStream = new DataInputStream(inputStream); //Read the inputStream and convert to primative times
// while (isGameOver == false) {
//Ask the User if what they want to do
System.out.println(sh);
selection = keyboard.nextLine();
//If they choose H
//if (selection.equals("H")) {
//Send the H to the Server(Works)
out.writeUTF(selection);
out.flush();
//Add server score to our score
// System.out.println(dataInputStream.readUTF());
while (dataInputStream.available() > 0) {
serverValue = dataInputStream.readInt();
}
score += conversion(serverValue);
System.out.println("Drew a " + serverValue + ".");
System.out.println("Waiting for player " + id);
//Loop back until
// }
//If they choose to stand just send S
if (selection.equals("S"))
{
//Send the H to the Server(Works)
out.writeUTF(selection);
out.flush();
}
// }
}
}
Server
import java.net.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Random;
/**
*
*/
public class Server extends Thread implements Runnable{
private ServerSocket serverSocket;
private Socket socket;
private ArrayList<Client> clients = new ArrayList<Client>();
private boolean isGameOver = false;
private Random rand;
private int scoreToAdd;
public Server(int port) throws IOException
{
serverSocket = new ServerSocket(port,2);
rand = new Random();
}
//Thread to execute once t.Start is called
public void run()
{
//Just as we are awaiting,print to the user.
System.out.println("Server started. Finding Clients...");
//System.out.println("Waiting for client on port : " + serverSocket.getLocalPort() + " ... ");
Socket server = null;
DataOutputStream dataOutputStream ;
//InputStream is used for reading
InputStream inputStream; //Read the incoming stream as bytes
DataInputStream dataInputStream; //Read the inputStream and convert to primative times
try {
//If the server is accepted, connect the user
//System.out.println("Connection Established.");
// Get a client trying to connect
server = serverSocket.accept();
//dataOutputStream = new DataOutputStream(server.getOutputStream());
//inputStream = server.getInputStream();
//dataInputStream = new DataInputStream(inputStream);
// Client has connected
System.out.println("Found Client "+ (clients.size()+1));
// Add user to list
clients.add(new Client(server,1));
}
catch (IOException e)
{
e.printStackTrace();
}
while (true)
{
try {
socket = serverSocket.accept();
dataOutputStream = new DataOutputStream(server.getOutputStream());
inputStream = server.getInputStream();
dataInputStream = new DataInputStream(inputStream);
// Client has connected
System.out.println("Found Client "+ (clients.size()+1));
System.out.println("Initiating Game...");
// Add user to list
clients.add(new Client(socket,1));
clients.get(0).isTurn = true;
while(isGameOver == false || ((clients.get(0).standOrHit.equals("S") && clients.get(1).standOrHit.equals("S")) == false))
{
if(clients.get(0).isTurn) {
System.out.println("Player 1's Turn");
if (dataInputStream.readUTF().equals("H")) {
//Generate a random number (Works)
System.out.println("Got H from Client");
//Send the random number in a outputbuffer (Works)
dataOutputStream.write(rand.nextInt(13) + 1);
dataOutputStream.flush();
clients.get(0).isTurn = false;
clients.get(1).isTurn = true;
}
}
else if (clients.get(1).isTurn)
{
System.out.println("Player 2's Turn");
if(dataInputStream.readUTF().equals("H"))
{
//Generate a random number (Works)
//System.out.println(rand.nextInt(13)+1);
//Send the random number in a outputbuffer (Works)
dataOutputStream.write(rand.nextInt(13)+1);
dataOutputStream.flush();
clients.get(1).isTurn = false;
clients.get(0).isTurn = true;
}
}
}
//Read input from the client
//DataInputStream dataInputStream = new DataInputStream(server.getInputStream());
//If we decide to hit
//if(dataInputStream.readUTF().equals("1"))
//{
//We want to send a random score 1-14
//rand = new Random();
// temp = rand.nextInt(14);
//}
//DataOutputStream dataOutputStream = new DataOutputStream(server.getOutputStream());
//dataOutputStream.writeUTF(Integer.toString(temp));
//dataOutputStream.flush();
// server.close();
}
catch (IOException e)
{
e.printStackTrace();
break;
}
}
}
public static void main(String [] args)
{
int port = 9999;
try
{
Thread t = new Server(port);
t.start();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
I have been working on a message system where users type in a server IP/Port and that server then takes in messages and relays them to all other users on the server. The whole program was based of a echo server i rewrote from scratch, for every server.accept() socket it creates two Threads, one to receive messages and one to send them back. The two Threads are connected by a DatagramPacket system so if the server receives a message from one socket it sends it back to all other users because their Threads are listening for the same thing, this is where i am encountering problems; everything work fine except the fact that the user who receives the message alternates in order to time of log on.
Example of problem when two clients are connected:
Client #1 sends 10 messages:
0
1
2
3
4
5
6
7
8
9
The Server receives all of them.
Client #1 receives:
1
3
5
7
9
Client #2 receives:
0
2
4
6
8
Here is the code for the Client:
import java.io.*;
import java.util.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MessageClient {
public static void main(String[] args) {
System.out.println("Starting Message System...");
Scanner in = new Scanner(System.in);
MessageClient mc = new MessageClient();
String input;
System.out.println(":System Started, type help for help.");
System.out.print(":");
while (true) {
input = in.nextLine();
if (input.equalsIgnoreCase("HELP")) {
mc.printHelp();
System.out.print(":");
} else if (input.equalsIgnoreCase("QUIT")) {
System.exit(0);
} else if (input.equalsIgnoreCase("CONNECT")) {
mc.connect(in);
in.nextLine();
System.out.print(":");
} else {
System.out.print("No command found.\n:");
}
}
}
public static void printHelp() {
System.out.println("help\tShow this prompt\nconnect\tStarts a new connection\nquit\tQuit the program\nexit\tExit a connection");
}
public void connect(Scanner in) {
Socket soc = null;
InetAddress addr = null;
System.out.print("IP_ADDRESS/HOST:");
String ip = in.nextLine();
System.out.print("PORT:");
int port = in.nextInt();
try {
System.out.println("Attempting to connect to HOST:\'" + ip + "\' on PORT:\'" + port + "\'");
addr = InetAddress.getByName(ip);
soc = new Socket(addr, port);
} catch(Exception e) {
System.out.println("Error connecting to server: " + e.getLocalizedMessage());
return;
}
SwingUtilities.invokeLater(new MessageGUI(ip + ":" + port, soc));
}
}
class MessageGUI implements Runnable {
public MessageGUI(String windowName, Socket server) {
JFrame window = new JFrame(windowName);
window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
window.setSize(500, 300);
window.setLayout(new BorderLayout());
window.setVisible(true);
MessageReceive mr = new MessageReceive(server);
mr.setEditable(false);
mr.setBackground(new Color(0, 0, 0));
mr.setForeground(new Color(0, 255, 0));
mr.setVisible(true);
new Thread(mr).start();
window.add(mr, BorderLayout.CENTER);
DataOutputStream dos = null;
try {
dos = new DataOutputStream(server.getOutputStream());
} catch(Exception e) {
System.out.println("Error creating output stream to server: " + e.getLocalizedMessage());
}
JTextField input = new JTextField();
input.addActionListener(new MessageSend(server, input, dos));
input.setBackground(new Color(0, 0, 0));
input.setForeground(new Color(0, 255, 0));
window.add(input, BorderLayout.PAGE_END);
System.out.println("Displaying connection.");
}
public void run() {}
}
class MessageReceive extends JTextArea implements Runnable {
protected Socket server;
public MessageReceive(Socket server) {
this.server = server;
}
public void run() {
DataInputStream dis = null;
int bytes;
try {
dis = new DataInputStream(server.getInputStream());
} catch(Exception e) {
System.out.println("Error connecting server: " + e.getLocalizedMessage());
}
this.append("Connected.\n");
while (true) {
try {
while ((bytes = dis.read()) != -1) this.append(String.valueOf((char) bytes));
} catch(Exception e) {
System.out.println("Error reading from server: " + e.getLocalizedMessage());
return;
}
}
}
}
class MessageSend implements ActionListener {
protected Socket server;
protected JTextField input;
protected DataOutputStream dos = null;
public MessageSend(Socket server, JTextField input, DataOutputStream dos) {
this.server = server;
this.input = input;
this.dos = dos;
}
public void actionPerformed(ActionEvent ae) {
try {
dos.writeBytes(input.getText() + "\n");
input.setText("");
} catch(Exception e) {
System.out.println("Error writing to server output stream: " + e.getLocalizedMessage());
}
}
}
Here is the code for the Server:
import java.io.*;
import java.net.*;
import java.util.*;
public class MessageServer {
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
MessageServer ms = new MessageServer();
System.out.println("Starting server on port " + port + "...");
ServerSocket ss = null;
try {
ss = new ServerSocket(port);
} catch(Exception e) {
System.out.println("Error creating server: " + e.getLocalizedMessage());
System.exit(0);
}
System.out.println("Created server port, now waiting for users...");
Socket client = null;
DatagramSocket ds = null;
try {
ds = new DatagramSocket(4);
} catch(Exception e) {
System.out.println("IN:Error creating Datagram Server: " + e.getLocalizedMessage());
e.printStackTrace();
System.exit(0);
}
while (true) {
try {
client = ss.accept();
System.out.println("Connecting user: " + client.getInetAddress().toString());
} catch(Exception e) {
System.out.println("Error on server: " + e.getLocalizedMessage());
}
new MessageConnectionIn(client, ds).start();
new MessageConnectionOut(client, ds).start();
}
}
}
class MessageConnectionOut extends Thread {
protected Socket client;
public DatagramSocket ds;
public MessageConnectionOut(Socket client, DatagramSocket ds) {
this.client = client;
this.ds = ds;
}
public void run() {
this.setName(client.getInetAddress().getHostAddress() + ":OUT");
try {
System.out.println("OUT:User connected.");
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
while (true) {
byte[] outgoing = new byte[4096];
DatagramPacket dp = new DatagramPacket(outgoing, outgoing.length);
ds.receive(dp);
dos.writeChars(new String(outgoing) + "\n");
}
} catch(Exception e) {
System.out.println("OUT:Error connecting " + this.getName() + ": " + e.getLocalizedMessage());
return;
}
}
}
class MessageConnectionIn extends Thread {
protected Socket client;
public DatagramSocket ds;
public MessageConnectionIn(Socket client, DatagramSocket ds) {
this.client = client;
this.ds = ds;
}
public void run() {
this.setName(client.getInetAddress().getHostAddress() + ":IN");
try {
System.out.println("IN:User connected.");
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
while (true) {
String lineIn = br.readLine();
byte[] input = lineIn.getBytes();
System.out.println(lineIn);
byte[] output = new byte[4096];
for (int c = 0; c < output.length; c++) output[c] = 0x0;
for (int i = 0; i < input.length && i < output.length; i++) output[i] = input[i];
DatagramPacket dp = new DatagramPacket(output, output.length, InetAddress.getLocalHost(), 4);
ds.send(dp);
}
} catch(Exception e) {
System.out.println("IN:Error connecting to " + this.getName() + ": " + e.getLocalizedMessage());
return;
}
}
}
UPDATE:
I tried replacing all the DatagramSockets with MulticastSockets and adding it to a group when I declared it, MessageServer.main(). The same problem occurred.
Multicast code:
public class MessageServer {
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
MessageServer msgsrv = new MessageServer();
System.out.println("Starting server on port " + port + "...");
ServerSocket ss = null;
try {
ss = new ServerSocket(port);
} catch(Exception e) {
System.out.println("Error creating server: " + e.getLocalizedMessage());
System.exit(0);
}
System.out.println("Created server port, now waiting for users...");
Socket client = null;
MulticastSocket ms = null;
try {
ms = new MulticastSocket(4);
ms.joinGroup(InetAddress.getByName("225.65.65.65"));
} catch(Exception e) {
System.out.println("IN:Error creating Datagram Server: " + e.getLocalizedMessage());
e.printStackTrace();
System.exit(0);
}
while (true) {
try {
client = ss.accept();
System.out.println("Connecting user: " + client.getInetAddress().toString());
} catch(Exception e) {
System.out.println("Error on server: " + e.getLocalizedMessage());
}
new MessageConnectionIn(client, ms).start();
new MessageConnectionOut(client, ms).start();
}
}
}
class MessageConnectionOut extends Thread {
protected Socket client;
public MulticastSocket ms;
public MessageConnectionOut(Socket client, MulticastSocket ms) {
this.client = client;
this.ms = ms;
}
public void run() {
this.setName(client.getInetAddress().getHostAddress() + ":OUT");
try {
System.out.println("OUT:User connected.");
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
while (true) {
byte[] outgoing = new byte[4096];
DatagramPacket dp = new DatagramPacket(outgoing, outgoing.length);
ms.receive(dp);
dos.writeChars(new String(outgoing) + "\n");
System.out.println("SENT_TO:" + this.getName());
}
} catch(Exception e) {
System.out.println("OUT:Error connecting " + this.getName() + ": " + e.getLocalizedMessage());
return;
}
}
}
class MessageConnectionIn extends Thread {
protected Socket client;
public MulticastSocket ms;
public MessageConnectionIn(Socket client, MulticastSocket ms) {
this.client = client;
this.ms = ms;
}
public void run() {
this.setName(client.getInetAddress().getHostAddress() + ":IN");
try {
System.out.println("IN:User connected.");
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
while (true) {
String lineIn = br.readLine();
byte[] input = lineIn.getBytes();
System.out.println(lineIn);
byte[] output = new byte[4096];
for (int c = 0; c < output.length; c++) output[c] = 0x0;
for (int i = 0; i < input.length && i < output.length; i++) output[i] = input[i];
DatagramPacket dp = new DatagramPacket(output, output.length, InetAddress.getLocalHost(), 4);
ms.send(dp);
}
} catch(Exception e) {
System.out.println("IN:Error connecting to " + this.getName() + ": " + e.getLocalizedMessage());
return;
}
}
}
This sample may be help you out.
There is 2 threads for Server.
One for reading UDP messages. I used 2 different ports since I just want to avoid messages read by same process. I don't have 2 machines to test it. Tested on my local host.
Another thread will broadcast UDP messages received by reader thread.
There is a thread safe list which acts between threads as data sync. Received data added to the list. Broadcaster thread polling the list for the data, if there is any broadcast and else sleep for 500 microseconds. Threads are created using executor.
private final static String INET_ADDR = "224.0.0.3";
private final static int PORT1 = 8888;
private final static int PORT2 = 8889;
private static List<String> threadSafeList = null;
public static void main(String[] args) throws UnknownHostException, InterruptedException {
threadSafeList = new CopyOnWriteArrayList<String>();
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new Sender(InetAddress.getByName(INET_ADDR), PORT1));
executorService.submit(new Receiver(InetAddress.getByName(INET_ADDR), PORT2));
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
private static class Receiver implements Runnable {
private InetAddress addr;
private int port;
public Receiver (InetAddress inetAddress, int port) throws UnknownHostException {
this.addr = InetAddress.getByName(INET_ADDR);
this.port = port;
}
public void run() {
System.out.println(" # Receiver ");
System.out.println(" # Receiver " + this.port);
byte[] buf = new byte[256];
try {
MulticastSocket clientSocket = new MulticastSocket(this.port);
//Joint the Multicast group.
clientSocket.joinGroup(this.addr);
while (true) {
// Receive the information and print it.
DatagramPacket msgPacket = new DatagramPacket(buf, buf.length);
clientSocket.receive(msgPacket);
String msg = new String(buf, 0, buf.length);
System.out.println("Socket 1 received msg: " + msg);
threadSafeList.add(msg);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private static class Sender implements Runnable {
private InetAddress addr;
private int port;
public Sender (InetAddress inetAddress, int port) throws UnknownHostException {
this.addr = InetAddress.getByName(INET_ADDR);
this.port = port;
}
public void run() {
System.out.println(" # Sender Address " + new String(this.addr.getAddress()));
System.out.println(" # Sender port " + this.port);
// Open a new DatagramSocket, which will be used to send the data.
while (true) {
try (DatagramSocket serverSocket = new DatagramSocket()) {
for (Iterator<String> it = threadSafeList.iterator(); !threadSafeList.isEmpty() && it.hasNext(); ) {
String i = it.next();
String msg = "Sent message no " + i;
// Create a packet that will contain the data
// (in the form of bytes) and send it.
DatagramPacket msgPacket = new DatagramPacket(msg.getBytes(), msg.getBytes().length, this.addr, this.port);
serverSocket.send(msgPacket);
threadSafeList.remove(i);
System.out.println("Server sent packet with msg: " + msg);
}
} catch (IOException ex) {
ex.printStackTrace();
}
try {
System.out.println("going for sleep");
Thread.currentThread().sleep(500);
System.out.println("going for sleeping");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Design can be modified by changing the creation of sender thread. Whenever receiver thread gets a message, create a sender thread and do the broadcast and shutdown that thread. You can use reusable thread pool instead of fixed Thread pool what used in this example. And you can pass message as argument while you create sender thread (so list may not be needed at all) and do the submit. I do have code.
public static void main(String[] args) throws UnknownHostException,
InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(new Receiver(InetAddress.getByName(INET_ADDR),
PORT2, executorService));
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
And inner classes,
private static class Receiver implements Runnable {
private InetAddress addr;
private int port;
private ExecutorService executorService;
public Receiver(InetAddress inetAddress, int port,
ExecutorService executorService) throws UnknownHostException {
this.addr = InetAddress.getByName(INET_ADDR);
this.port = port;
this.executorService = executorService;
}
public void run() {
System.out.println(" # Receiver ");
System.out.println(" # Receiver " + this.port);
byte[] buf = new byte[256];
try {
MulticastSocket clientSocket = new MulticastSocket(this.port);
// Joint the Multicast group.
clientSocket.joinGroup(this.addr);
while (true) {
// Receive the information and print it.
DatagramPacket msgPacket = new DatagramPacket(buf,
buf.length);
clientSocket.receive(msgPacket);
String msg = new String(buf, 0, buf.length);
System.out.println("Socket 1 received msg: " + msg);
executorService.submit(new Sender(InetAddress
.getByName(INET_ADDR), PORT1, msg));
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
And
private static class Sender implements Runnable {
private InetAddress addr;
private int port;
private String message;
public Sender(InetAddress inetAddress, int port, String message)
throws UnknownHostException {
this.addr = InetAddress.getByName(INET_ADDR);
this.port = port;
this.message = message;
}
public void run() {
System.out.println(" # Sender Address "
+ new String(this.addr.getAddress()));
System.out.println(" # Sender port " + this.port);
try {
DatagramSocket serverSocket = new DatagramSocket();
String msg = "Sent message no " + message;
// Create a packet that will contain the data
// (in the form of bytes) and send it.
DatagramPacket msgPacket = new DatagramPacket(msg.getBytes(),
msg.getBytes().length, this.addr, this.port);
serverSocket.send(msgPacket);
System.out.println("Server sent packet with msg: " + msg);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Client has got 2 threads,
One for reading broadcaster messages.
Another for sending 5 messages in loop. Once it is finished, thread will shut-down.
No data exchange here, so no thread safe list.
private static class Receiver implements Runnable {
private InetAddress addr;
private int port;
public Receiver(InetAddress inetAddress, int port)
throws UnknownHostException {
this.addr = InetAddress.getByName(INET_ADDR);
this.port = port;
}
public void run() {
System.out.println(" # Receiver ");
System.out.println(" # Receiver port " + this.port);
byte[] buf = new byte[256];
try (MulticastSocket clientSocket = new MulticastSocket(this.port)) {
// Joint the Multicast group.
clientSocket.joinGroup(this.addr);
while (true) {
// Receive the information and print it.
DatagramPacket msgPacket = new DatagramPacket(buf,
buf.length);
clientSocket.receive(msgPacket);
String msg = new String(buf, 0, buf.length);
System.out.println("Socket 1 received msg: " + msg);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
And
private static class Sender implements Runnable {
private InetAddress addr;
private int port;
public Sender(InetAddress inetAddress, int port)
throws UnknownHostException {
this.addr = InetAddress.getByName(INET_ADDR);
this.port = port;
}
public void run() {
System.out.println(" # Sender Address "
+ new String(this.addr.getAddress()));
System.out.println(" # Sender port " + this.port);
// Open a new DatagramSocket, which will be used to send the data.
try {
DatagramSocket serverSocket = new DatagramSocket();
for (int i = 0; i < 5; i++) {
System.out.println("inside loop");
String msg = "Sent message no 2" + i;
// Create a packet that will contain the data
// (in the form of bytes) and send it.
DatagramPacket msgPacket = new DatagramPacket(
msg.getBytes(), msg.getBytes().length, this.addr,
this.port);
System.out.println("Before sending to socket");
serverSocket.send(msgPacket);
System.out.println("Server sent packet with msg: " + msg);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
This article sample code is extended further.
Code to be fine tuned.
I, doing some computer network homework and I have to develop some sort of distributed DBMS which are connected to each other with peer to peer network, so I have a TCP client and a TCP server in one .java file which running next to each other by threads. the TCP Server of the class always listen to the other TCP client from others and give them service, the problem is when I System.out the String which I have to send back to the client on the Server side it's in the way which it's supposed to be but after sending , the client gets nothing and prints null. I wrote my code based on tutorials I found on the net and I when I test them they worked well but it's not working in my own code. could you see where my problem is? thanks
class CommandComp
{
int PORT = 1210;
int PORT2 = 1211;
String IPLocal = "";
String IPdest = "";
InetAddress IPAD;
InetAddress IPAD2;
int numOfNodes;
int numOfNodesnonchanged;
String[] Nodes;
Random rand = new Random();
int max = 2000;
int min = 1000;
String command;
Socket clientSocket;
CommandComp(String[] IPdest, String IPLocal, int numOfNodes, String command)
{
try
{
this.numOfNodes = numOfNodes;
numOfNodesnonchanged = numOfNodes;
this.IPLocal = IPLocal;
this.Nodes = IPdest;
this.command = command;
// this.IPAD = InetAddress.getByName(this.IPdest);
this.IPAD2 = InetAddress.getByName(this.IPLocal);
// clientSocket = new Socket(this.IPAD , PORT ,this.IPAD2 , PORT2 );
}
catch (Exception e)
{
// //e.printStackTrace();
}
}
public String call()
{
int i = 0;
while (numOfNodes > 0)
{
String response = "";
try
{
Thread.sleep(rand.nextInt(max - min + 1) + min);
i = numOfNodes - 1;
int max2 = 50;
int min2 = 10;
this.IPAD = InetAddress.getByName(Nodes[i]);
clientSocket = new Socket(this.IPAD, PORT, this.IPAD2, PORT2 + rand.nextInt(max2 - min2 + 1) + min2);
PrintWriter outToServer = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
outToServer.println(command);
System.out.println(inFromServer.readLine());
response = inFromServer.readLine();
outToServer.close();
inFromServer.close();
clientSocket.close();
numOfNodes--;
System.out.println(Nodes[i] + " Remote DBMS");
System.out.println(response);
}
catch (Exception e)
{
e.printStackTrace();
try
{
clientSocket.close();
}
catch (Exception e2)
{
// TODO: handle exception
}
}
}
return command;
}
}
class TCPListnerService
implements Callable<Object>
{
String from;
String to;
ServerSocket Server;
String IP = "";
int numOfNodes;
int numofNodesUnchanged;
static clientThread t[];
TCPListnerService(String IP, int numOfNodes)
{
try
{
this.IP = IP;
this.numOfNodes = numOfNodes;
numofNodesUnchanged = numOfNodes * 2;
this.t = new clientThread[numofNodesUnchanged];
InetAddress IPAD = InetAddress.getByName(IP);
Server = new ServerSocket(1210, 20, IPAD);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public String call()
throws Exception
{
String gotten = "";
while (numOfNodes > 0)
{
Socket connected = Server.accept();
for (int i = 0; i < numofNodesUnchanged; i++)
{
if (t[i] == null)
{
(t[i] = new clientThread(connected)).start();
break;
}
}
}
return gotten;
}
}
class clientThread
extends Thread
{
Socket clientSocket = null;
sqlite DB = new sqlite();
String response = "";
String fromclient;
String delims = "[ =)(',\n\t\r]+";
String[] tokens;
public clientThread(Socket clientSocket)
{
this.clientSocket = clientSocket;
}
public void run()
{
try
{
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter outToClient = new PrintWriter(clientSocket.getOutputStream(), true);
fromclient = inFromClient.readLine();
tokens = fromclient.split(delims);
if (tokens[0].equalsIgnoreCase("create")
|| tokens[0].equalsIgnoreCase("drop")
|| tokens[0].equalsIgnoreCase("delete")
|| tokens[0].equalsIgnoreCase("insert")
|| tokens[0].equalsIgnoreCase("update")
|| tokens[0].equalsIgnoreCase("select"))
{
response = DB.RunQuery(fromclient);
System.out.println(response);
outToClient.print(response);
clientS.close();
}
else if (tokens[0].equalsIgnoreCase("shut"))
{
System.exit(0);
}
inFromClient.close();
outToClient.close();
clientSocket.close();
}
catch (Exception e)
{
}
;
}
}
The problem is here:
inFromClient.close();
outToClient.close();
clientSocket.close();
You are closing (1) the input stream, which closes the socket, (2) the PrintWriter, which flushes it and closes the socket, and (3) the socket. Obviously (2) cannot succeed if the socket is already closed. The data you sent to the client is still buffered, never got flushed, so it never got sent.
Just close the PrintWriter, and close the socket in a finally block in case (2) fails somehow. No need to close the input at all.