I am having trouble getting the concurrency running on a simple UDP chat client I am writing, it all works perfectly until I try to add a new contact to the outgoing message list stored in Peer, it throws a CurrentModificationException, can someone help me understand where I have gone wrong?
Here are my classes
import java.net.*;
import java.util.*;
import java.io.*;
public class Chatter {
public static class ReceiveMess extends Thread{
DatagramSocket ds;
public ReceiveMess(DatagramSocket s){
ds = s;
}
byte[] Rbuf = new byte[1000];
DatagramPacket Rdgp = new DatagramPacket(Rbuf, Rbuf.length);
public synchronized void run() {
try{
while (true){
for(Peer p : Peer.PeerList){
ds.receive(Rdgp);
String rcvd = new String(Rdgp.getData(), 0, Rdgp.getLength()) + ", from address: "
+ Rdgp.getAddress() + ", port: " + Rdgp.getPort();
System.out.println(rcvd);
}
}
}
catch(IOException e) {
System.out.println(e);
}
}
}
public static class SendMess extends Thread{
DatagramSocket ds;
public SendMess(DatagramSocket s){
ds = s;
}
int SPORT = 40080;
byte[] Sbuf = new byte[1000];
DatagramPacket Sdgp = new DatagramPacket(Sbuf, Sbuf.length);
public synchronized void run() {
try{
while (true) {
BufferedReader consR = new BufferedReader(new InputStreamReader(System.in));
String MessOut = consR.readLine();
if(MessOut.startsWith("/NEW")){
try{
String[] splitArray = MessOut.split(" ");
String newIP = (splitArray[1]);
Peer p = new Peer(newIP);
System.out.println(newIP + " added to the contacts list");
continue;
}
catch(Exception e){
System.out.println("Please format NEW IP address's as NEW XXX.XXX.XXX.XXX");
continue;
}
}
else{
Sbuf = ("Server Said: " + MessOut).getBytes();
for(Peer p : Peer.PeerList){
DatagramPacket out = new DatagramPacket(Sbuf, Sbuf.length, p.IP, SPORT);
ds.send(out);}
}
}
}
catch(IOException e) {
System.out.println(e);
}
}
}
public static void main(String [] args){
try{
for(String s : args){
String address = s;
Peer peer = new Peer(address);
}
int PORT = 40080;
DatagramSocket ds = new DatagramSocket(PORT);
Peer.PrintList();
SendMess sendmess = new SendMess(ds);
sendmess.start();
ReceiveMess receivemess = new ReceiveMess(ds);
receivemess.start();
}
catch(Exception e){
System.out.println(e);
}
}
}
And my peer class,
import java.net.*;
import java.util.*;
public class Peer{
InetAddress IP;
static List<Peer> PeerList = new LinkedList<Peer>();
Peer(String clientAddress){
try{
IP = IP.getByName(clientAddress);
AddToList(this);
}
catch(UnknownHostException e){
System.out.println(e.toString());
}
}
public synchronized void AddToList(Peer peer){
PeerList.add(this);
}
public List<Peer> GetList(){
return PeerList;
}
public static void PrintList(){
for(Peer p : PeerList){
System.out.println(p.IP.toString());
}
}
}
So your issue is this...
You have a loop in ReceiveMess that iterates the list in Peer. At the same time in SendMess you are creating new instances of Peer. When a new instance of Peer is created this adds a new element to the PeerList. This then causes the CME in ReceiveMess.
What I would suggest is that you remove all the synchronized keywords as they are not doing anything for you and return a COPY of the list from Peer.getList. This means that as one thread is iterating the list, even if the other thread modifies the list of Peers the iterating thread will not be effected. The iterating thread will then see the update on the next iteration of the while loop.
One mechanism to do this is to use a CopyOnWriteArrayList or Guava's ImmutableList.Builder.
CopyOnWriteArrayList
ImmutableList.Builder
The keyword synchronized for a method is equivalent to:
public void run() {
synchronized(this) {
...
}
}
In your synchronized methods, this is a different object (ReceiveMess,SendMess and Peer), so there's nothing stopping those methods from running at the same time. Use a common monitor instead, (Chatter.class is the most expedient).
In other words, synchronized for methods is not global, it's just a shortcut to synchronizing on the monitor of this. I encourage you to read at least this entire track before you continue.
Related
I am currently programming an online chess game and wanted to host multiple server on 1 PC. It also works when I play with two players, but if I start a third player, it can't connect to server.
The code for the player:
public Socket socket;
public int PlayerID;
public ReadFromServer rfs;
public WriteToServer wts;
public void connectToServer(){
for (int i = 1;i <=3 ;i++ ) {
try {
socket = new Socket("localhost",(1000+i));
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
PlayerID = in.readInt();
System.out.println("You are Player Number:"+PlayerID);
if(PlayerID == 1){
System.out.println("Waiting for Oponent...");
}
rfs = new ReadFromServer(in);
wts = new WriteToServer(out);
Thread rT = new Thread(rfs);
Thread wT = new Thread(wts);
rT.start();
wT.start();
i = 4;
} catch(IOException e) {
System.out.println("ERROR at: connect to Server");
}
}
}
public static void main(String[] args) {
PlayerFrame pf = new PlayerFrame();
pf.connectToServer();
pf.GUI();
}
The Server code:
import java.io.*;
import java.net.*;
import java.net.InetAddress;
public class GameServer {
public int GameNumber = 3;
public ServerSocket ss;
public int numPlayers;
public int maxPlayers;
public Socket socket1;
public Socket socket2;
public ReadFromClient p1read;
public ReadFromClient p2read;
public WriteToClient p1write;
public WriteToClient p2write;
public GameServer[] gs;
public int p1x1,p1x2,p1y1,p1y2,p2x1,p2x2,p2y1,p2y2;
public GameServer(){
gs = new GameServer[GameNumber];
Thread[] server = new Thread[GameNumber];
ServerThread[] thread = new ServerThread[GameNumber];
for (int i = 0;i < gs.length ;i++ ) {
gs[i] = new GameServer((1000+i+1));
thread[i] = new ServerThread(i);
server[i] = new Thread(thread[i]);
server[i].start();
} // end of for
}
public GameServer(int i){
System.out.println("=====Game Server=====");
numPlayers = 0;
maxPlayers = 2;
try {
ss = new ServerSocket(i);
} catch(IOException e) {
System.out.println("ERROR at: Server Construction");
}
}
public void acceptConnections(){
try {
System.out.println("Waiting for connections...");
while (numPlayers < maxPlayers) {
Socket s = ss.accept();
DataInputStream in = new DataInputStream(s.getInputStream());
DataOutputStream out = new DataOutputStream(s.getOutputStream());
numPlayers++;
out.writeInt(numPlayers);
System.out.println("Player Number "+numPlayers+" has connected");
ReadFromClient rfc = new ReadFromClient(numPlayers,in);
WriteToClient wtc = new WriteToClient(numPlayers,out);
if(numPlayers == 1){
socket1 = s;
p1read = rfc;
p1write = wtc;
Thread read1 = new Thread(p1read);
Thread write1 = new Thread(p1write);
read1.start();
write1.start();
}
else{
socket2 = s;
p2read = rfc;
p2write = wtc;
Thread read2 = new Thread(p2read);
Thread write2 = new Thread(p2write);
read2.start();
write2.start();
}
} // end of while
System.out.println("No longer accepting connections");
} catch(IOException e) {
System.out.println("ERROR at: acceptConnections");
}
}
public static void main(String[] args) {
new GameServer();
}
public class ReadFromClient implements Runnable{
public int playerID;
public DataInputStream dataIn;
public ReadFromClient(int pid, DataInputStream in){
playerID = pid;
dataIn = in;
System.out.println("RFC "+playerID+" Runnable created");
}
#Override
public void run(){
try {
while (true) {
if(playerID == 1){
p1x1 = dataIn.readInt();
p1y1 = dataIn.readInt();
p1x2 = dataIn.readInt();
p1y2 = dataIn.readInt();
}
else{
p2x1 = dataIn.readInt();
p2y1 = dataIn.readInt();
p2x2 = dataIn.readInt();
p2y2 = dataIn.readInt();
}
try {
Thread.sleep(25);
} catch(InterruptedException ex) {
System.out.println("ERROR at RFS Run");
}
} // end of while
} catch(IOException e) {
System.out.println("ERROR at: RFS");
}
}
}
public class WriteToClient implements Runnable{
public int playerID;
public DataOutputStream dataout;
public WriteToClient(int pid, DataOutputStream out){
playerID = pid;
dataout = out;
System.out.println("WTC "+playerID+" Runnable created");
}
#Override
public void run(){
try {
while (true) {
if(playerID == 1){
dataout.writeInt(p2x1);
dataout.writeInt(p2y1);
dataout.writeInt(p2x2);
dataout.writeInt(p2y2);
dataout.flush();
}
else{
dataout.writeInt(p1x1);
dataout.writeInt(p1y1);
dataout.writeInt(p1x2);
dataout.writeInt(p1y2);
dataout.flush();
}
try {
Thread.sleep(25);
} catch(InterruptedException ex) {
System.out.println("ERROR at WTC Run");
}
} // end of while
} catch(IOException e) {
System.out.println("ERROR at: WTC run");
}
}
}
public class ServerThread implements Runnable{
public int num;
public ServerThread(int i){
num = i;
}
#Override
public void run(){
gs[num].acceptConnections();
}
}
} // end of class GameServer
I don't get any errors, even though a window doesn't pop up when I run the third player, making.
Have just one ServerSocket and a single loop, and on accepting a client socket start a game running thread using that socket.
ServerSocket serverSocket = ...
ExecutorService executorService = Executors.newFixedThreadPool(4);
while (!executorService .isTerminated()) {
Socket socket = serverSocket.accept();
Runnable gameRun = () -> { ... socket ... };
// like new WorkerThread("...");
executorService .execute(gameRun);
}
executorService .shutdown();
You can do it more neat than here, the thread and passing the socket and such.
The above runs every game conversation in its own thread.
It has one main loop on the server socket, which is crucial; you should not use the same ServerSocket in two threads.
You can easily being overrung by a DoS attack (Denial of Service, by hundreds of requests). Just keep an eye on the count of the number of threads.
On request
I cannot code here an entire client-server dialog. For that you
should look at examples, that probably are way better than what
I can write here. Also my code is not compiled.
Some code snippets.
First I would not exchange binary data, but text. That makes developing and especially debugging easier. Also with chess notation everything is almost done.
Runnable gameRun = new GameRun(socket);
executorService .execute(gameRun);
class GameRun implements Runnable, Autoclosable {
final BufferedReader in;
final PrintWriter out
GameRun(Socket socket) {
in = new BufferedReader(
new InputStreamReader(
socket.getInputStream(),
StandardCharsets.UTF_8));
out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream(),
StandardCharsets.UTF_8)));
}
#Override
public void close() {
in.close();
out.close();
}
#Override
public void run() {
for (;;) {
out.println("Your move/action:");
if (!in.ready()) {
out.println("I am awaiting...");
//continue;
}
String line = in.readLine();
out.println("echo " + line);
if (line == null) {
break;
}
}
}
}
The client should do something similar.
I can't seem to figure out how to notify my Server class that a connection was lost. My server code is:
public class Server {
static int port = 4444;
static boolean listening = true;
static ArrayList<Thread>Clients = new ArrayList<Thread>();
static MatchMaker arena;
public static void main(String[] args) {
Initialize();
Thread startConnections = new Thread(run());
startConnections.start();
}
private static Runnable run(){
System.out.println("(" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + ") Started listening on port: " + port);
try(ServerSocket socket = new ServerSocket(port)){
while(listening){
if(Clients.size() <= 4){
Socket clientSocket = socket.accept();
MultiThread connection = new MultiThread(clientSocket, arena, );
Clients.add(connection);
System.out.println("Client connected from " + clientSocket.getRemoteSocketAddress() + " Assigned ID: " + connection.getId());
System.out.println("Currently connected clients(" + Clients.size() + "): ");
for(int i = 0; i < Clients.size(); i++)
System.out.println(" - " + Clients.get(i).getId());
connection.start();
}
}
}
catch(Exception e){
e.printStackTrace();
}
return null;
}
private static void Initialize(){
arena = new MatchMaker();
}
}
The problem here is that since this class keeps track of the connected clients I want it to notice when a client has lost connection. The MultiThread class already has a functional way of detecting clients that lost connection, however I don't know how to pass that information back to the Server class. I've tried passing the server class to MultiThread as a parameter, but it said I couldn't use 'this' in a static manner.
You can keep them in synchronized map like:
Map<Integer, ClientObject> connectedClients = new HashMap<Integer, ClientObject>(); //key integer will be the client id
Other suggestion:
Map<String, ClientObject> connectedClients = new HashMap<String, ClientObject>(); //key String will be the client IP&userName (you decide)
Fist of all use thread safe collection for monitoring the client connection so replace the following
ArrayList Clients = new ArrayList();
with
ConcurrentLinkedQueue Clients = new ConcurrentLinkedQueue();
But your problem is that you are trying to use limited resource in thread safe manner so best option would be using Semaphore. I have re factored your class a bit to give an idea.Hope it helps. Plz look closely on 'SERVER_INSTANCE'.
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
public class Server {
private final int MAX_AVAILABLE = 4;
public final Semaphore SERVER_INSTANCE = new Semaphore(MAX_AVAILABLE, true);
static int port = 4444;
static volatile boolean listening = true;
static MatchMaker arena;
public static void main(String[] args) {
Initialize();
Thread startConnections = new Thread(run());
startConnections.start();
}
private static void Initialize() {
//do somthing
}
private static Runnable run(){
System.out.println("(" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + ") Started listening on port: " + port);
ServerSocket socket = null;
while(listening){
try {
socket = new ServerSocket(port);
try {
SERVER_INSTANCE.acquire();
Socket clientSocket = socket.accept();
MultiThread connection = new MultiThread(clientSocket, arena, SERVER_INSTANCE);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
return null;
}
static class MultiThread implements Runnable{
Semaphore serverInstance ;
public MultiThread(Socket clientSocket, MatchMaker arena, Semaphore serverInstance) {
serverInstance = serverInstance;
}
#Override
public void run() {
try {
serverInstance.acquire();
//Do your work here
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
serverInstance.release();
}
}
}
class MatchMaker {
}
}
Hello everyone i have created a multi threaded chat server that looks like this:
public class Main {
public static ServerSocket server;
public static Socket connection;
public static int backLog = 100;
public static int numberOfConnected;
public static boolean connected = false;
public final static int potNumber = 6080;
public static PrintWriter pw;
public static Scanner input;
public static int i = 0;
public static void main(String[] args) {
startServer();
}
public static void startServer(){
try {
server = new ServerSocket(potNumber, backLog);
waitingForConnection();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void waitingForConnection() {
connected = false;
i++;
while (!connected) {
try {
if (connected) {
}
connection = server.accept();
Server s = new Server(connection, pw = new PrintWriter(connection.getOutputStream()), input = new Scanner(connection.getInputStream()));
s.start();
numberOfConnected++;
waitingForConnection();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
The idea is that this is suppose to be a chat server so when one connects to the server it starts the following thread:
threads
public void run(){
while (connection.isConnected()) {
if (input.hasNext()) {
String fullMessage = input.nextLine();
if (fullMessage.equalsIgnoreCase("Connect")) {
connectHim();
}else {
chatMessage(fullMessage);
}
}
}
}
private void chatMessage(String fullMessage) {
String name = fullMessage.substring(0, fullMessage.indexOf(" "));
String message = fullMessage.substring(fullMessage.indexOf(" "), fullMessage.length());
pw.println(name+": "+message);
pw.flush();
}
private void connectHim() {
String name = input.nextLine();
pw.println(0);
pw.flush();
pw.println(1);
pw.flush();
pw.println();
pw.flush();
pw.println(name);
pw.flush();
}
So my problem is the following:
if the user that is bound to thread 1 (this is an example) and the user bound to thread 2 sends a message to the server how will i send that message to the user bound on thread 1?
One of options is to use Hashtable or HashMap (just call Collections.synchronizedMap(myMap) in case of Map usage). When you start new Thread, give him unique name (for example user nick name ) and put it to your collection where key - Thread name, and value - Thread as Object.
if the user that is bound to thread 1 (this is an example) and the user bound to thread 2 sends a message to the server how will i send that message to the user bound on thread 1?
For example you have user1, user2, user3. Now you build 3 Threads and put them to HashMap, like:
Map<String, Thread> threadMap = new HashMap<String,Thread>();
threadMap = Collections.synchronizedMap(threadMap);
YourThread th1 = new YourThread();
threadMap.put("user1", th);
YourThread th2 = new YourThread();
threadMap.put("user2", th);
YourThread th3 = new YourThread();
threadMap.put("user3", th);
....
Set<String> userSet = threadMap.keySet();
Iterator<String> it = userSet.iterator();
Thread currThread = null;
while(it.hasNext()){
String key = it.next();
currThread = threadMap.get(key);
// do something with currThread
}
Im facing one problem in streaming data capture for reading the broadcast data during multithreading, pls help or suggest,
Actually there is one class which is reading data from one of the udp socket. Another class accepts the tcp connection from every client request, creates a thread for every client and request the same udp class for data. The thing is working with 1st thread which gets created. But when i request with another client from another pc/ip the packets get losted to the 2nd client/thread
I have made a workaround by creating a list where im storing the Threads outputstream object
and looping it to send the data to all the client. But this is just temporary as it ll delay the packets if clients/connections gets increased.
code for reading UDP Data
public class EventNotifier
{
private InterestingEvent ie;
public DatagramSocket clientSocket;
public String[] split_str;
byte[] receiveData;
HashMap<String, String> secMap = new HashMap<String, String>();
public EventNotifier(InterestingEvent event)
{
ie = event;
clientSocket = new DatagramSocket(9050);
receiveData = new byte[500];
}
public String getDataFeed(String client_id)
{
try
{
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
clientSocket.receive(receivePacket);
String s = new String(receivePacket.getData());
String split_str = s.split(",");
if(secMap.containsValue(split_str[0]))
return s;
else
return "";
} catch(Exception e3) {}
}
}// end of eventNotifier class
code for multithreading handling client requests
public class multiServer
{
static protected List<PrintWriter> writers = new ArrayList<PrintWriter>();
static String client_id = "";
public static void main(String[] args)
{
try
{
ServerSocket servsock = new ServerSocket(8858);
Socket incoming;
while(true)
{
incoming = servsock.accept();
multiServerThread connection = new multiServerThread(incoming);
Thread t1 = new Thread(connection);
t1.start();
}
}
catch(IOException e)
{
System.out.println("couldnt make socket");
}
}
}
class multiServerThread extends Thread implements InterestingEvent
{
Socket incoming;
PrintWriter out=null;
PrintWriter broad=null;
BufferedReader in = null;
String cliString=null;
private EventNotifier en;
int id;
public static String udp_data;
public void interestingEvent(String str1)
{
this.udp_data = str1;
}
public String getUdpData()
{
String _udp_data = this.udp_data;
return _udp_data;
}
multiServerThread(Socket incoming)
{
this.incoming=incoming;
en = new EventNotifier(this);
}
public void run()
{
try
{
out = new PrintWriter(incoming.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
cliString = in.readLine();
multiServer.writers.add(out);
while(true)
{
try
{
udp_data = en.getDataFeed(cliString);
if(udp_data!=null && udp_data.length()>0)
{
//workaround for serving the data to all cleints who are connected
for (int i=0; i<multiServer.writers.size();i++)
{
broad=multiServer.writers.get(i);
broad.println(udp_data.trim());
}
//else will directly write to the outputstream object for every thread which is connected
// out.println(udp_data.trim());
}
}
catch (Exception e)
{
System.out.println("exception "+e);
}
Thread.sleep(1);
}
} catch(IOException e)
{
System.out.print("IO Exception :: "+ e);
}
catch(InterruptedException e)
{
System.out.print("exception "+ e);
}
}
}
You need mutual exclusion (or a different design).
For example, what will happen if two threads call multiServer.writers.add(out); concurrently?
From the ArrayList Javadocs
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or [...])
Another problem is two calling udp_data = en.getDataFeed(cliString); concurrently. The second thread might overwrite the result of the first. You'll loose data!
What happens if one thread calls for (int i=0; i<multiServer.writers.size();i++) while another thread is busy doing multiServer.writers.add(out);? The size may have increased, before out has actually been added to the list!
public class multiServer
{
private List<PrintWriter> writers = new ArrayList<PrintWriter>();
public synchronized void addWriter(PrintWrite out) {
writers.add(out);
}
public synchronized void serveAllWriters(String data) {
for (int i=0; i<multiServer.writers.size();i++)
{
broad=multiServer.writers.get(i);
broad.println(data);
}
}
}
Now when a thread tries to add a writer, the synchronizeds will make sure no other thread is adding or printing. So multiServerThread should be fixed to use the new methods:
class multiServerThread extends Thread implements InterestingEvent
{
//...
private String udp_data;
//...
myMultiServer.addWriter(out);
//...
udp_data = en.getDataFeed(cliString);
if(udp_data!=null && udp_data.length()>0)
myMultiServer.serveAllWriters(udp_data.trim());
//...
}
There might be more problems, not sure I don't fully understand your code. The question you must ask yourself is, can another thread read and/or write the same data or object? Yes? Then you'll need proper synchronization.
I'm building an application in Java which requires a Hashtable to be accessed from instances of two classes and both extend threads. I have declared the Hashtable in one of the two classes. I always get null when i try to access the Hashtable contents from one of the classes. The other class is able to access the contents without any problem. I thought this was a problem of concurrency control. Since these are threads of different classes we cannot use synchronized methods. Is there a way to make the Hashtable accessible from threads of both the classes?
Here are the some parts of the code of my application
This is the class which stores the HashMap:
public class DataStore {
public Map ChatWindows ;
public DataStore()
{
ChatWindows = new ConcurrentHashMap();
}
public synchronized void putWindow(String with,ChatWindow t)
{
ChatWindows.put(with,t);
notifyAll();
}
public synchronized ChatWindow getWindow(String with)
{
notifyAll();
return (ChatWindow)ChatWindows.get(with);
}
public synchronized void ChatWindowOpen(chatClient cc,String with,String msg)
{
// chatWith = with;
ChatWindow t;
System.out.println(with);
t = getWindow(with);
if(t == null)
{
t = new ChatWindow(cc,with,msg);
// th = new Thread(t);
putWindow(with, t);
// th.start();
}
else
{
t.setVisible(true);
}
}
}
Two classes which access 'ChatWindows' HashMap
public class chatClient extends javax.swing.JFrame implements
Runnable,ListSelectionListener,MouseListener,WindowListener{
static String LoginName,chatWith,msgToChatWindow;
Thread listThread=null,th,chatListen;
static Socket soc;
static DataOutputStream dout,dout1;
static DataInputStream din,din1;
DefaultListModel listModel;
ChatWindow t;
public DataStore ds;
/** Creates new form chatClient */
public chatClient(Login l,DataStore ds) {
listModel = new DefaultListModel();
initComponents();
clientList.addListSelectionListener(this);
clientList.addMouseListener(this);
addWindowListener(this);
this.LoginName=l.loginName;
soc = l.soc2;
din = l.din2;
dout = l.dout2;
dout1 = l.dout1;
din1 = l.din1;
super.setTitle(LoginName);
listThread = new Thread(this);
listThread.start();
this.ds = ds;
}
.
.
.
.
public void mouseClicked(MouseEvent e)
{
chatWith = (String)clientList.getSelectedValue();
ds.ChatWindowOpen(this,chatWith,"");
}
This class has run() method too, but that doesn't use the HashMap. This class is able to access the 'ChatWindows' properly.'ChatListenThread' class is not able to access the contents of HashMap properly.
public class ChatListenThread implements Runnable{
DataOutputStream dout1;
DataInputStream din1;
public static chatClient cc;
public static ChatWindow t;
public DataStore ds;
public ChatListenThread(Login l,DataStore ds)
{
din1 = l.din1;
dout1= l.dout1;
this.ds = ds;
}
.
.
.
.
public void run(){
while(true)
{
try{
String msgFromServer=new String();
msgFromServer = din1.readUTF();
StringTokenizer st=new StringTokenizer(msgFromServer);
String msgFrom=st.nextToken();
String MsgType=st.nextToken();
String msg = "";
while(st.hasMoreTokens())
{
msg=msg+" " +st.nextToken();
}
ds.ChatWindowOpen(cc,msgFrom,msg);
}
catch(IOException e)
{
System.out.println("Read failed");
}
}
}
}
It's possible. Take a look at Sharing data safely between two threads.
Okey, I couldn't use your code because I don't understand, what I did see was that you want something like this:
Create a empty JFrame with a JTabbedPane and start a thread that connects to a Socket
When input comes on the socket, create a ChatPanel (~JTextArea) and add it to one of the tabs
Add the ChatPanel to a Map that handles the messages from "from"
Pass the message to the newly created ChatPanel
So I did that and I'm posting the code below! Hope that you can use it!
If you like to test this, first start the TestChatServer (code below) and then the ChatSupervisor.
This is the code for the client
public class ChatSupervisor extends JFrame implements Runnable {
JTabbedPane tabs = new JTabbedPane();
Map<String, ChatPanel> chats = new ConcurrentHashMap<String, ChatPanel>();
public ChatSupervisor() {
super("Test Chat");
add(tabs, BorderLayout.CENTER);
new Thread(this).start();
}
public void run() {
Socket sock = null;
try {
sock = new Socket("localhost", 32134);
Scanner s = new Scanner(sock.getInputStream());
while (true) {
String from = s.next();
String type = s.next();
String message = s.nextLine();
getChat(from).incomingMessage(type, message);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sock != null) try { sock.close(); } catch (IOException e) {}
}
}
public ChatPanel getChat(String from) {
if (!chats.containsKey(from))
chats.put(from, new ChatPanel(from));
return chats.get(from);
}
public static void main(String[] args) {
ChatSupervisor cs = new ChatSupervisor();
cs.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cs.setSize(400, 300);
cs.setVisible(true);
}
class ChatPanel extends JTextArea {
public ChatPanel(final String from) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
tabs.addTab(from, ChatPanel.this);
}
});
}
public void incomingMessage(final String type, final String message) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
append("[" + type + "]" + message);
append("\n");
}
});
}
}
}
This is the code for the test server:
public class TestChatServer {
public static void main(String[] args) throws Exception {
Socket s = new ServerSocket(32134).accept();
System.out.println("connected");
PrintWriter p = new PrintWriter(s.getOutputStream());
while (true) {
p.println("hello info Hello World!");
p.flush();
Thread.sleep(1000);
for (int i = 0; i < 10; i++) {
p.println("test" + i + " warn Testing for testing " + i);
p.flush();
Thread.sleep(100);
}
}
}
}