Id of the connection in Ratchet web socket requests - symfony

I am testing Ratchet with Symfony. Tests are fine but I cannot understand where a property $conn->resourceID is coming from... Here is the code from the official tutorial
<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
// Store the new connection to send messages to later
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
, $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
foreach ($this->clients as $client) {
if ($from !== $client) {
// The sender is not the receiver, send to each client connected
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn) {
// The connection is closed, remove it, as we can no longer send it messages
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
I cannot find it ($conn->resourceID), IDE cannot find it, but it works, it is populated somehow....
Does anyone has a clue to help me to understand?
Thanks

Related

How to differentiate the data of one topic or another coming from Mercure hub if the client is suscribed to two topics

I have a a dashboard on a Symony app with two ChartJS graphs, one for temperature data, and the other for pressure data. I need update both on realtime; for that purpose I try to use MercureBundle with two topics: ['realtime-notif/temperature/{sensorId}', 'realtime-notif/pressure/{sensorId}']. Although the topics sound similar, the logic to conform datas are differents because the two ChartJS are differents, and for that I have two Messenger messages handlers with AMQP queue, one publish a mercure update in topic 'realtime-notif/temperature/{sensorId}' and the other message handler class publish in 'realtime-notif/pressure/{sensorId}'. I will try to summarize the code to be concise.
#mercure.yaml:
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: ['realtime-notif/temperature/{sensorId}', 'realtime-notif/pressure/{sensorId}']
subscribe: ['realtime-notif/temperature/{sensorId}', 'realtime-notif/pressure/{sensorId}']
#The TemperatureMessageHandler class:
class TemperatureMessageHandler implements MessageHandlerInterface
{
private $mercureHub;
private $managerRegistry;
public function __construct(HubInterface $mercureHub, ManagerRegistry $managerRegistry)
{
$this->mercureHub = $mercureHub;
$this->managerRegistry = managerRegistry;
}
public function __invoke(TemperatureMessage $message)
{
try {
$graphId=$message->getGraphId();
$lastElapsedTime=$message->getLastElapsedTime();
$em=$this->managerRegistry->getManager();
$storedData = $em->getRepository(Temperature::class)->findLastRecordsForGraph($graphId, $lastElapsedTime);
/**
Set the data source for the temperature graph to a specific format from $toredData
**/
$formatedChartData = [];
**....**
$update = new Update(
sprintf('realtime-notif/temperature/%s', $graphId),
\json_encode($$formatedChartData),
true
);
$this->mercureHub->publish($update);
} catch (\Exception $exc) {
}
}
}
And,
#The PressureMessageHandler class:
class PressureMessageHandler implements MessageHandlerInterface
{
private $mercureHub;
private $managerRegistry;
public function __construct(HubInterface $mercureHub, ManagerRegistry $managerRegistry)
{
$this->mercureHub = $mercureHub;
$this->managerRegistry = managerRegistry;
}
public function __invoke(PressureMessage $message)
{
try {
$graphId = $message->getGraphId();
$lastElapsedTime = $message->getLastElapsedTime();
$em = $this->managerRegistry->getManager();
$storedData = $em->getRepository(Pressure::class)->findLastRecordsForGraph($graphId, $lastElapsedTime);
/**
Set the data source for the pressure graph to a specific format from $toredData
**/
$formatedChartData = [];
**....**
$update = new Update(
sprintf('realtime-notif/pressure/%s', $graphId),
\json_encode($$formatedChartData),
true
);
$this->mercureHub->publish($update);
} catch (\Exception $exc) {
}
}
}
The problem for me is I don't know how to differentiate on the client side if the data received from the Mercure hub is from temperature topic or pressure topic in the message event of EventSource object.
<script type="text/javascript">
$(document).ready(function () {
/**Create two graph on page ready **/
let temperatureGraphObject = createTemperatureGraph(canvasTemperaturaGraph);
let pressureGRaphObject = createPressureGraph(canvasPressureGraph);
/**
I have two function updateTemperatureGraph(temperatureGraphObject, newTemperaturaData) and updatePressureGraph(pressureGraphObject, newPresureData)
**/
/**Subscribe client to topics for data updates **/
{% set topics = ['realtime-notif/temperature/'~temperatureSensorId, 'realtime-notif/pressure/'~pressureSensorId] %}
const eventSource = new EventSource("{{ mercure(topics, { subscribe:topics})|escape('js')}}", {withCredentials: true});
eventSource.onopen = function () {
console.log('New socket connection!');
};
eventSource.onmessage = function (e) {
console.log('New data received');
var data = JSON.parse(e.data);
/**
The problem is here, how differentiate the topics data to call updateTemperaturaGraph(temperatureGraphObject, data) or updatePressureGraph(pressureGraphObject, data)
**/
};
eventSource.onerror = function () {
console.log('Socket connection lost!');
};
});
</script>
So, how differentiate the topics data to call updateTemperaturaGraph(temperatureGraphObject, data) or updatePressureGraph(pressureGraphObject, data) into onmessage event?
If I subscribe the client to only one topic all data received will be of kind of the topic graph, and of course the graph is updated correctly.
The solution is set de type attribute for Update class constructor that you want to publish in your Mercure hub from the MessageHandler related class. So, for the messages related to temperature notification we will set the type attribute = 'temperatureUpdate', and for the message related to pressure notification we will set the type attribute = 'pressureUpdate'.
The __invoke function on TemperatureMessageHandler:
public function __invoke(TemperatureMessage $message)
{
try {
$graphId=$message->getGraphId();
$lastElapsedTime=$message->getLastElapsedTime();
$em=$this->managerRegistry->getManager();
$storedData = $em->getRepository(Temperature::class)->findLastRecordsForGraph($graphId, $lastElapsedTime);
/**
Set the data source for the temperature graph to a specific format from $toredData
**/
$formatedChartData = [];
**....**
$update = new Update(
sprintf('realtime-notif/temperature/%s', $graphId),
\json_encode($$formatedChartData),
true,
null,
'temperatureUpdate'
);
$this->mercureHub->publish($update);
} catch (\Exception $exc) {
}
}
The __invoke function on PressureMessageHandler:
public function __invoke(PressureMessage $message)
{
try {
$graphId = $message->getGraphId();
$lastElapsedTime = $message->getLastElapsedTime();
$em = $this->managerRegistry->getManager();
$storedData = $em->getRepository(Pressure::class)->findLastRecordsForGraph($graphId, $lastElapsedTime);
/**
Set the data source for the pressure graph to a specific format from $toredData
**/
$formatedChartData = [];
**....**
$update = new Update(
sprintf('realtime-notif/pressure/%s', $graphId),
\json_encode($$formatedChartData),
true,
null,
'pressureUpdate'
);
$this->mercureHub->publish($update);
} catch (\Exception $exc) {
}
}
On the client side, is mandatory to create two new EventListeners for the EventSource object, with the name equals to the new types created. Every one new listener will treat the related messages types published in the Mercure hub:
<script type="text/javascript">
$(document).ready(function () {
/**Create two graph on page ready **/
let temperatureGraphObject = createTemperatureGraph(canvasTemperaturaGraph);
let pressureGRaphObject = createPressureGraph(canvasPressureGraph);
/**
I have two function updateTemperatureGraph(temperatureGraphObject, newTemperaturaData) and updatePressureGraph(pressureGraphObject, newPresureData)
**/
/**Subscribe client to topics for data updates **/
{% set topics = ['realtime-notif/temperature/'~temperatureSensorId, 'realtime-notif/pressure/'~pressureSensorId] %}
const eventSource = new EventSource("{{ mercure(topics, { subscribe:topics})|escape('js')}}", {withCredentials: true});
eventSource.onopen = function () {
console.log('New socket connection!');
};
eventSource.addEventListener("temperaturaUpdate", function (e) {
let parsedData = null;
try {
parsedData = JSON.parse(e.data);
} catch (error) {
console.log(error);
}
if (parsedData) {
updateTemperatureGraph(temperatureGraphObject, parsedData);
}
}, false);
eventSource.addEventListener("pressureUpdate", function (e) {
let parsedData = null;
try {
parsedData = JSON.parse(e.data);
} catch (error) {
console.log(error);
}
if (parsedData) {
updatePressureGraph(pressureGraphObject, parsedData);
}
}, false);
eventSource.onerror = function () {
console.log('Socket connection lost!');
};
});
</script>
In this way, the Mercure hub publishes the classified messages, and each EventListener will be in charge of processing the corresponding message in the order in which it arrives at the subscribed client, regardless of the topics to which it is subscribed.

Symfony swiftmailer send multiple mails inside loop

I need to send multiple mails with symfony swiftmailer inside a loop. This is my code.
public function __construct($name = null, \Swift_Mailer $mailer, EngineInterface $templating) {
parent::__construct($name)
$this->mailer = $mailer;
$this->templating = $templating;
}
protected function execute(InputInterface $input, OutputInterface $output) {
foreach ($ads as $ad) {
if($counter == 10) break;
$this->sendMail($ad->getUser()->getMail(), $ad, 'matching');
$counter++;
}
}
protected function sendMail($mail, $ad, $template = '') {
if($template == 'matching'){
$template = 'emails/matching-cars.html.twig';
}elseif($template == 'owner'){
$template = 'emails/matching-car-owners.html.twig';
}
$message = (new \Swift_Message('Hello Email'))
->setFrom('admin#admin.com')
->setTo($mail)
->setBody(
$this->templating->render(
$template, [
'ad' => $ad
]
), 'text/html'
);
$this->mailer->send($message);
}
Since I'm doing it inside loop it's really hard to pass mails array to swift mailer. I'm running this inside Console Command and I'm getting this error in console.
17:14:09 ERROR [app] Exception occurred while flushing email
queue: Expected response code 354 but got code "550", with message
"550 5.7.0 Requested action not taken: too many emails per second
symfony swiftmailer send multiple mails inside loop
let assume you have function called sendEmail that you use to sent email to all users that have account in you system.
Here are explanation that can help you to solve email error with code implementation:
For me it work....
public function sendEmail(\Swift_Mailer $mailer): Response
{
$entityManager=$this->getDoctrine()->getManager();
$connection= $entityManager->getConnection();
$decl12="SELECT id
FROM
user";
$statement97=$connection->prepare($decl12);
$statement97->execute();
$tap=$statement97->fetchAll();
try{
foreach($tap as $i=>$data)
{
//check what type of data taken from variable data by
//var_dump($data);
$test =$this->getDoctrine()->getRepository(User::class)->findOneBy(['id'=>$data]);
$email=$test->getEmail();
$message = (new \Swift_Message('Email Subject'))
->setFrom('admin#gmail.com')
->setTo($email)
->setBody(
"click here to get bonus",
'text/html'
);
$mailer->send($message);
}
}
catch (\Swift_TransportException $e)
{
return new Response($e->getMessage());
}
}

How to properly configure Stomp and SockJS endpoint in Spring MVC?

This is/may be duplicate of:
Websocket - InvalidStateError: The connection has not been established yet.
I am implementing Notification System. And want to initialize Socket connection when user Logged In, and show him his notifications, and also if some event happens.
My Code snippet as follows.
websocket.js :
var stompClient = null;
function connect( temp ) {
alert(temp);
//var socket = new SockJS("/websock");
//var socket = new SockJS("/websock"+temp);
var socket = new SockJS(context_path+"/websock"+temp);
//context_path == "/SupportCenter"
stompClient = Stomp.over(socket);
stompClient.connect({}, function( frame ){
console.log( "Connected :- "+frame );
stompClient.subscribe("/topic/notifications", function( notifications ) {
alert( notifications );
});
}, function( error ) {
alert( error );
});
alert();
getNotifications();
}
function getNotifications() {
stompClient.send("/app/hello", {}, "Hiiiiii");
}
WebSocketConfig.java :
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
/* (non-Javadoc)
* #see org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer#registerStompEndpoints(org.springframework.web.socket.config.annotation.StompEndpointRegistry)
*/
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/websock").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// TODO Auto-generated method stub
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
}
WebSocketController.java :
#Controller
public class WebSocketController {
#MessageMapping(value="/hello")
#SendTo("/topic/notifications")
public Notify hello() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Notify notify = new Notify();
notify.setMessage("Hello World !!!");
return notify;
}
}
Some code Hom.jsp :
<script type="text/javascript" src="<c:url value="/resources/js/sockjs.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/js/stomp.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/js/websocket.js"/>"></script>
<script type="text/javascript">
$(document).ready(function() {
//...
connect( '${nsec}');
});
Why Firefox Console giving XML Parsing Error: no root element found Location: while in Network tab status code is 200 OK.
Console Tab
Network Tab
Originaly posted to this question.
This is because stompClient.connect() method is asynchronous. I doesn't pause the execution waiting until connection is established. When you call getNotifications() right after alert() most probably connection is not established yet (it might be established if alert() takes enough time to connect).
You are supposed to call getNotifications() in stompClient.connect() callback (just like you do with stompClient.subscribe()) to be sure that connection is established by the time it gets invoked.
For example:
stompClient.connect({}, function( frame ){
console.log( "Connected :- "+frame );
stompClient.subscribe("/topic/notifications", function( notifications ) {
alert( notifications );
});
getNotifications();
}, function( error ) {
alert( error );
});
Besides of that you may consider using #SubscribeMapping annotation in your java code to get rid of explicit message from JavaScript to get initial message from the server. This way the server sends initial message as soon as subscription is established.
For example:
#MessageMapping(value="/hello")
#SubscribeMapping("/notifications")
#SendTo("/topic/notifications")
public Notify hello() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Notify notify = new Notify();
notify.setMessage("Hello World !!!");
return notify;
}
Then client code would look like following:
stompClient.connect({}, function( frame ){
console.log( "Connected :- "+frame );
stompClient.subscribe("/topic/notifications", function( notifications ) {
alert( notifications );
});
}, function( error ) {
alert( error );
});

Bukkit How do I get a argument from a string?

I'm trying to make a plugin that detects when people chat
"#say " it will broadcast a message with those arguments.
What I need to know is how to get arguments from a string.
Please help.
Main:
package com.gong.say;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin{
String sayMessage = ChatColor.GREEN + "Your message has been said!";
public void onEnable()
{
Bukkit.getLogger().info("[BukkitAPIEnhancer] Plugin started!");
Bukkit.getPluginManager().registerEvents(new ChatListener(this), this);
}
public void onDisable()
{
Bukkit.getLogger().info("[BukkitAPIEnhancer] Plugin disabled!");
}
}
ChatListener:
package com.gong.say;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
public class ChatListener implements Listener {
Main plugin;
public ChatListener(Main plugin)
{
this.plugin = plugin;
}
#EventHandler
public void onChat(AsyncPlayerChatEvent e)
{
if(e.isAsynchronous())
{
String message = e.getMessage();
if(message.contains("#say"))
{
//String[] args = Arguments after #say
//Bukkit.broadcastMessage(args);
}
}
}
}
You should usually use commands prefixed by /, so, normally you would do /say String[args], and It would be easier to get the arguments, yet if you want it to be prefixed by #, then that's another story. You could do something like this:
if(message.contains("#say")){
String messageToSend = message.replaceAll("#say", "");//get the arguments
if(messageToSend.length <= 0){//make sure there's something after #say
e.getPlayer().sendMessage("Correct usage: #say <arguments>"); //the user didn't put anything after #say
return;
}
else{
e.setCancelled(true);//cancel the event
Bukkit.getServer().broadcastMessage(messageToSend);//send the message that comes after "#say"
//you may want to add a chat color to the message to make it stand out more
}
}
So, here's what your event should look like:
#EventHandler
public void onChat(AsyncPlayerChatEvent e){
if(e.isAsynchronous()){
String message = e.getMessage();
if(message.contains("#say")){
String messageToSend = message.replaceAll("#say", "");//get the arguments
if(messageToSend.length <= 0){//make sure there's something after #say
e.getPlayer().sendMessage("Correct usage: #say <arguments>"); //the user didn't put anything after #say
return;
}
else{
e.setCancelled(true);//cancel the event
Bukkit.getServer().broadcastMessage(messageToSend);//send the message that comes after "#say"
//you may want to add a chat color to the message to make it stand out more
}
}
}
}
#EventHandler
public void onChat2(AsyncPlayerChatEvent e) {
if(e.isAsynchronous()) {
String msg = e.getMessage();
/** Verify if message starts with #say **/
if(msg.startsWith("#say")) {
/** Split message for get the args **/
String[] args = e.getMessage().split(" ");
/** Verify if have something after #say **/
if(args.length > 1) {
/** Cancel message and broadcast **/
e.setCancelled(true);
StringBuilder sb = new StringBuilder();
for(int i = 1; i <args.length; i++) {
sb.append(args[i] + " ");
}
/** Add color to broadcast */
String broadcast = ChatColor.translateAlternateColorCodes('&', sb.toString());
/** Broadcast prefix **/
String prefix = "§c[Broadcast] §r";
/** Broadcast **/
Bukkit.broadcastMessage(prefix + broadcast);
}
}
}
}

Chat for more than 1 users

This is the code for a simple chat that allows 1 user to connect with the server. The other users have to wait in a queue to connect the server, and they are allowed to connect after the user using it determinate the connection. Anyone know how I can make it work, so that more users can chat at the same time together? Here is the Server code:
// Server.java
// Server portion of a client/server stream-socket connection.
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Server extends JFrame
{
private JTextField enterField; // inputs message from user
private JTextArea displayArea; // display information to user
private ObjectOutputStream output; // output stream to client
private ObjectInputStream input; // input stream from client
private ServerSocket server; // server socket
private Socket connection; // connection to client
private int counter = 1; // counter of number of connections
// set up GUI
public Server()
{
super( "Server" );
enterField = new JTextField(); // create enterField
enterField.setEditable( false );
enterField.addActionListener(
new ActionListener()
{
// send message to client
public void actionPerformed( ActionEvent event )
{
sendData( event.getActionCommand() );
enterField.setText( "" );
} // end method actionPerformed
} // end anonymous inner class
); // end call to addActionListener
add( enterField, BorderLayout.NORTH );
displayArea = new JTextArea(); // create displayArea
add( new JScrollPane( displayArea ), BorderLayout.CENTER );
setSize( 300, 150 ); // set size of window
setVisible( true ); // show window
} // end Server constructor
// set up and run server
public void runServer()
{
try // set up server to receive connections; process connections
{
server = new ServerSocket( 12345, 100 ); // create ServerSocket
while ( true )
{
try
{
waitForConnection(); // wait for a connection
getStreams(); // get input & output streams
processConnection(); // process connection
} // end try
catch ( EOFException eofException )
{
displayMessage( "\nServer terminated connection" );
} // end catch
finally
{
closeConnection(); // close connection
++counter;
} // end finally
} // end while
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end method runServer
// wait for connection to arrive, then display connection info
private void waitForConnection() throws IOException
{
displayMessage( "Waiting for connection\n" );
connection = server.accept(); // allow server to accept connection
displayMessage( "Connection " + counter + " received from: " +
connection.getInetAddress().getHostName() );
} // end method waitForConnection
// get streams to send and receive data
private void getStreams() throws IOException
{
// set up output stream for objects
output = new ObjectOutputStream( connection.getOutputStream() );
output.flush(); // flush output buffer to send header information
// set up input stream for objects
input = new ObjectInputStream( connection.getInputStream() );
displayMessage( "\nGot I/O streams\n" );
} // end method getStreams
// process connection with client
private void processConnection() throws IOException
{
String message = "Connection successful";
sendData( message ); // send connection successful message
// enable enterField so server user can send messages
setTextFieldEditable( true );
do // process messages sent from client
{
try // read message and display it
{
message = ( String ) input.readObject(); // read new message
displayMessage( "\n" + message ); // display message
} // end try
catch ( ClassNotFoundException classNotFoundException )
{
displayMessage( "\nUnknown object type received" );
} // end catch
} while ( !message.equals( "CLIENT>>> TERMINATE" ) );
} // end method processConnection
// close streams and socket
private void closeConnection()
{
displayMessage( "\nTerminating connection\n" );
setTextFieldEditable( false ); // disable enterField
try
{
output.close(); // close output stream
input.close(); // close input stream
connection.close(); // close socket
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end method closeConnection
// send message to client
private void sendData( String message )
{
try // send object to client
{
output.writeObject( "SERVER>>> " + message );
output.flush(); // flush output to client
displayMessage( "\nSERVER>>> " + message );
} // end try
catch ( IOException ioException )
{
displayArea.append( "\nError writing object" );
} // end catch
} // end method sendData
// manipulates displayArea in the event-dispatch thread
private void displayMessage( final String messageToDisplay )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run() // updates displayArea
{
displayArea.append( messageToDisplay ); // append message
} // end method run
} // end anonymous inner class
); // end call to SwingUtilities.invokeLater
} // end method displayMessage
// manipulates enterField in the event-dispatch thread
private void setTextFieldEditable( final boolean editable )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run() // sets enterField's editability
{
enterField.setEditable( editable );
} // end method run
} // end inner class
); // end call to SwingUtilities.invokeLater
} // end method setTextFieldEditable
} // end class Server
+
// Test the Server application.
import javax.swing.JFrame;
public class ServerTest
{
public static void main( String[] args )
{
Server application = new Server(); // create server
application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
application.runServer(); // run server application
} // end main
}
And the Client code:
Client.java
// Client portion of a stream-socket connection between client and server.
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Client extends JFrame
{
private JTextField enterField; // enters information from user
private JTextArea displayArea; // display information to user
private ObjectOutputStream output; // output stream to server
private ObjectInputStream input; // input stream from server
private String message = ""; // message from server
private String chatServer; // host server for this application
private Socket client; // socket to communicate with server
// initialize chatServer and set up GUI
public Client( String host )
{
super( "Client" );
chatServer = host; // set server to which this client connects
enterField = new JTextField(); // create enterField
enterField.setEditable( false );
enterField.addActionListener(
new ActionListener()
{
// send message to server
public void actionPerformed( ActionEvent event )
{
sendData( event.getActionCommand() );
enterField.setText( "" );
} // end method actionPerformed
} // end anonymous inner class
); // end call to addActionListener
add( enterField, BorderLayout.NORTH );
displayArea = new JTextArea(); // create displayArea
add( new JScrollPane( displayArea ), BorderLayout.CENTER );
setSize( 300, 150 ); // set size of window
setVisible( true ); // show window
} // end Client constructor
// connect to server and process messages from server
public void runClient()
{
try // connect to server, get streams, process connection
{
connectToServer(); // create a Socket to make connection
getStreams(); // get the input and output streams
processConnection(); // process connection
} // end try
catch ( EOFException eofException )
{
displayMessage( "\nClient terminated connection" );
} // end catch
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
finally
{
closeConnection(); // close connection
} // end finally
} // end method runClient
// connect to server
private void connectToServer() throws IOException
{
displayMessage( "Attempting connection\n" );
// create Socket to make connection to server
client = new Socket( InetAddress.getByName( chatServer ), 12345 );
// display connection information
displayMessage( "Connected to: " +
client.getInetAddress().getHostName() );
} // end method connectToServer
// get streams to send and receive data
private void getStreams() throws IOException
{
// set up output stream for objects
output = new ObjectOutputStream( client.getOutputStream() );
output.flush(); // flush output buffer to send header information
// set up input stream for objects
input = new ObjectInputStream( client.getInputStream() );
displayMessage( "\nGot I/O streams\n" );
} // end method getStreams
// process connection with server
private void processConnection() throws IOException
{
// enable enterField so client user can send messages
setTextFieldEditable( true );
do // process messages sent from server
{
try // read message and display it
{
message = ( String ) input.readObject(); // read new message
displayMessage( "\n" + message ); // display message
} // end try
catch ( ClassNotFoundException classNotFoundException )
{
displayMessage( "\nUnknown object type received" );
} // end catch
} while ( !message.equals( "SERVER>>> TERMINATE" ) );
} // end method processConnection
// close streams and socket
private void closeConnection()
{
displayMessage( "\nClosing connection" );
setTextFieldEditable( false ); // disable enterField
try
{
output.close(); // close output stream
input.close(); // close input stream
client.close(); // close socket
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end method closeConnection
// send message to server
private void sendData( String message )
{
try // send object to server
{
output.writeObject( "CLIENT>>> " + message );
output.flush(); // flush data to output
displayMessage( "\nCLIENT>>> " + message );
} // end try
catch ( IOException ioException )
{
displayArea.append( "\nError writing object" );
} // end catch
} // end method sendData
// manipulates displayArea in the event-dispatch thread
private void displayMessage( final String messageToDisplay )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run() // updates displayArea
{
displayArea.append( messageToDisplay );
} // end method run
} // end anonymous inner class
); // end call to SwingUtilities.invokeLater
} // end method displayMessage
// manipulates enterField in the event-dispatch thread
private void setTextFieldEditable( final boolean editable )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run() // sets enterField's editability
{
enterField.setEditable( editable );
} // end method run
} // end anonymous inner class
); // end call to SwingUtilities.invokeLater
} // end method setTextFieldEditable
} // end class Client
+
// Class that tests the Client.
import javax.swing.JFrame;
public class ClientTest
{
public static void main( String[] args )
{
Client application; // declare client application
// if no command line args
if ( args.length == 0 )
application = new Client( "127.0.0.1" ); // connect to localhost
else
application = new Client( args[ 0 ] ); // use args to connect
application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
application.runClient(); // run client application
} // end main
}
After seeing ur program, this is what is going wrong.You are using exactly ONE thread as the server and u are blocking this thread when you call listener.accept().
To avoid this is what you need to do:
Make another class
Server2 - Similar to what you have now, but instead of doing the actual work of acting as an echo server, it just spawns a new Thread which starts listening on a NEW PORT (which you can select randomly), and sends the client the address for this new port. The client will then get the new port number and would try to connect to the server on the new port. 2: The Echo thread - This starts a new listener on the port passed, and does the job of echoing to whoever is listening.
OR:
You start a UDP server rather than a TCP server, and all this will not matter

Resources