I have referred documentation on Stellar.
Then I have started to run the send payment and receiving payment code after creating an account in Java.
Send payment code is working , but receiving payment code got terminated. I have mentioned code below :
public class receivePayment {
public static void main(String args[]) {
Server server = new Server("https://horizon-testnet.stellar.org");
KeyPair account = KeyPair.fromAccountId("GC2BKLYOOYPDEFJKLKY6FNNRQMGFLVHJKQRGNSSRRGSMPGF32LHCQVGF");
// Create an API call to query payments involving the account.
PaymentsRequestBuilder paymentsRequest = server.payments().forAccount(account);
// If some payments have already been handled, start the results from
// the
// last seen payment. (See below in `handlePayment` where it gets
// saved.)
/*
* String lastToken = loadLastPagingToken(); if (lastToken != null) {
* paymentsRequest.cursor(lastToken); }
*/
// `stream` will send each recorded payment, one by one, then keep the
// connection open and continue to send you new payments as they occur.
paymentsRequest.stream(new EventListener<OperationResponse>() {
#Override
public void onEvent(OperationResponse payment) {
// Record the paging token so we can start from here next time.
// savePagingToken(payment.getPagingToken());
// The payments stream includes both sent and received payments.
// We only
// want to process received payments here.
if (payment instanceof PaymentOperationResponse) {
if (((PaymentOperationResponse) payment).getTo().equals(account)) {
return;
}
String amount = ((PaymentOperationResponse) payment).getAmount();
Asset asset = ((PaymentOperationResponse) payment).getAsset();
String assetName;
if (asset.equals(new AssetTypeNative())) {
assetName = "lumens";
} else {
StringBuilder assetNameBuilder = new StringBuilder();
assetNameBuilder.append(((AssetTypeCreditAlphaNum) asset).getCode());
assetNameBuilder.append(":");
assetNameBuilder.append(((AssetTypeCreditAlphaNum) asset).getIssuer().getAccountId());
assetName = assetNameBuilder.toString();
}
StringBuilder output = new StringBuilder();
output.append(amount);
output.append(" ");
output.append(assetName);
output.append(" from ");
output.append(((PaymentOperationResponse) payment).getFrom().getAccountId());
System.out.println(output.toString());
}
}
});
}
}
I don't understand why it gets terminated. If I checked the balance from my account URL, but shows me the sending-receiving result, but it is not showing result in Eclipse.
I have also referred below reference link and follow the answer but still it is not working.
Stellar payments query
Can anyone tell me how to run this code which continuously receive the payments and maintain the logs on console. ?
The thing is this is a streaming service, so if you just run the service in main method then it will obviously terminated while running in main method and the scope will go outside and EventListener will not be able to be executed. As you said you are using eclips,one thing you can do is instead of run try debug and insert a debugpoint at this Server server = new Server("https://horizon-testnet.stellar.org"); line and press F6 and go one by one line. While debugging Once it reaches the last line of the program then wait,don't run. You will see the data in the console. this way you will understand how the program is working.
If you want to run it fast then use the code that I have added with the existing code. I have added two options. you can use any of this. This will display the output.
public class TestStellar2 {
public static void main(String args[]) {
Server server = new Server("https://horizon-testnet.stellar.org");
KeyPair account = KeyPair.fromAccountId("GC2BKLYOOYPDEFJKLKY6FNNRQMGFLVHJKQRGNSSRRGSMPGF32LHCQVGF");
PaymentsRequestBuilder paymentsRequest = server.payments().forAccount(account);
paymentsRequest.stream(new EventListener <OperationResponse>(){
#Override
public void onEvent(OperationResponse payment) {
if (payment instanceof PaymentOperationResponse) {
if (((PaymentOperationResponse) payment).getTo().equals(account)) {
return;
}
String amount = ((PaymentOperationResponse) payment).getAmount();
Asset asset = ((PaymentOperationResponse) payment).getAsset();
String assetName;
if (asset.equals(new AssetTypeNative())) {
assetName = "lumens";
} else {
StringBuilder assetNameBuilder = new StringBuilder();
assetNameBuilder.append(((AssetTypeCreditAlphaNum) asset).getCode());
assetNameBuilder.append(":");
assetNameBuilder.append(((AssetTypeCreditAlphaNum) asset).getIssuer().getAccountId());
assetName = assetNameBuilder.toString();
}
StringBuilder output = new StringBuilder();
output.append(amount);
output.append(" ");
output.append(assetName);
output.append(" from ");
output.append(((PaymentOperationResponse) payment).getFrom().getAccountId());
System.out.println(output.toString());
}
}
});
/**
* option 1
*
*/
/*try {
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
/**
* option 2
*/
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The output will look like below :
10.0000000 lumens from GC2BKLYOOYPDEFJKLKY6FNNRQMGFLVHJKQRGNSSRRGSMPGF32LHCQVGF many line like this
I have implemented a two player game with thread pool of sockets. each player connects to their own thread. I added a message Queue system according to this article.
Problem is the messages are lagging. the first respond from the first player is added to messageQueue as expected. But second player doesn't received it by calling poll() it only receives null yet.
And then the second player responds the second player receives the first players message. My intention is to send the message to second player before his/her response.
I must be doing some mistake or I have neglected some important concept.
Can you please help me find it?
My Code goes like this, there are two Classes related to this, GameRunnable.java and Game.java. I have omitted some code to simplify this mess.
In GameRunnable class ;
public static final Map<GamerRunnable, BlockingQueue<String>> messageQueues = new ConcurrentHashMap<>();
public static final Map<String, GamerRunnable> gameQueue = new ConcurrentHashMap<>();
private Game game;
public void run() {
System.out.println
(this.setGameInstance(clientSocket, readerIn, output) ? "OK": "FAILED");
messageQueues.put(this, new ArrayBlockingQueue<String>(100));
// If player 1
this.game.initGame(this);
// If Player 2
this.game.initGame(this);
}
public static GamerRunnable getGameThreadByName(String name) {
return gameQueue.get(name);
}
public String getName() {
return this.name;
}
in Game.java
public Game(Socket clientSocket, BufferedReader readIn, OutputStream output) {
this.sockGamer = clientSocket;
try {
this.out = output;
this.inGamer = readIn;
} catch(Exception e) {
e.printStackTrace();
}
public void sendToGamer(String msg) {
try {
this.out.write((msg+"\n").getBytes());
this.out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void initGame(GamerRunnable game) {
try {
boolean messageLoop = true;
String line1 = null;
String line2 = null;
while (messageLoop) {
line1 = inGamer.readLine();
if (line1 != null) {
System.out.println("Gamer says: "+line1);
GamerRunnable gamer2 = null;
if (game.getName().equals("red")) {
gamer2 = GamerRunnable.getGameThreadByName("black");
}
else if (game.getName().equals("black")) {
gamer2 = GamerRunnable.getGameThreadByName("red");
}
if (gamer2 != null) {
System.out.println("Adding to Queue");
GamerRunnable.messageQueues.get(gamer2).offer(line1);
}
}
line2 = GamerRunnable.messageQueues.get(game).poll();
if (line2 != null) {
//receiving from Queue
System.out.println(line2);
game.getGameInstance().sendToGamer(line2);
}
}
Due to network latency you may need a short delay to wait for the first message to arrive on the queue. Try poll(time, unit) instead. This will wait for the specified time for a message to appear on the queue. If nothing is there it will return a null like poll() is now. A user will probably not notice a 500ms to 1s delay if no message is available.
Following is some parts of my code, which uses Threading. The purpose is to retrieve all the records from database (approx. 5,00,000) and send them alert email messages. The problem I am facing is the variable emailRecords becomes very heavy and too much time is taken to send email message. How can I make it fast by using multi-threading such that 5,00,000 records are processed parallelly? I tried to use ExecutorService but got confused in implementing it. I got mixed up in the method checkName(), getRecords() and sendAlert(). All these 3 methods are used relevantly. So, where to use executorService ??
Please provide me the suggestion how to proceed with the following code and which part needs editing? Thanks in advance!!
public class sampledaemon implements Runnable {
private static List<String[]> emailRecords = new ArrayList<String[]>();
public static void main(String[] args) {
if (args.length != 1) {
return;
}
countryName = args[0];
try {
Thread t = null;
sampledaemon daemon = new sampledaemon();
t = new Thread(daemon);
t.start();
} catch (Exception e) {
e.printStackTrace()
}
}
public void run() {
Thread thisThread = Thread.currentThread();
try {
while (true) {
checkName(countryName);
Thread.sleep(TimeUnit.SECONDS.toMillis(10));
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void checkName(String countryName) throws Exception {
Country country = CountryPojo.getDetails(countryName)
if (country != null) {
getRecords(countryconnection);
}
}
private void getRecords(Country country, Connection con) {
String users[] = null;
while (rs.next()) {
users = new String[2];
users[0] = rs.getString("userid");
users[1] = rs.getString("emailAddress");
emailRecords.add(props);
if (emailRecords.size() > 0) {
sendAlert(date, con);
}
}
}
void sendAlert(String date, Connection con) {
for (int k = 0; k < emailRecords.size(); k++) {
//check the emailRecords and send email
}
}
}
From what i can tell is that you would most likely be single threaded data retrieval, and multi-threaded for the e-mail sending. Roughly, you'd be cycling through your result set and building a list of records. When that list hits a certain size, you make a copy and send off that copy to be processed in a thread, and clear the original list. At the end of the result set, check to see if you have unprocessed records in your list, and send that to the pool as well.
Finally, wait for the threadpool to finish processing all records.
Something along these lines:
protected void processRecords(String countryName) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(5), new ThreadPoolExecutor.CallerRunsPolicy());
List<String[]> emaillist = new ArrayList<String>(1000);
ResultSet rs = ....
try {
while (rs.next()) {
String user[] = new String[2];
users[0] = rs.getString("userid");
users[1] = rs.getString("emailAddress");
emaillist.add(user);
if (emaillist.size() == 1000) {
final List<String[]> elist = new ArrayList<String[]>(emaillist);
executor.execute(new Runnable() {
public void run() {
sendMail(elist);
}
}
emaillist.clear();
}
}
}
finally {
DbUtils.close(rs);
}
if (! emaillist.isEmpty()) {
final List<String[]> elist = emaillist;
executor.execute(new Runnable() {
public void run() {
sendMail(elist);
}
}
emaillist.clear();
}
// wait for all the e-mails to finish.
while (! executor.isTerminated()) {
executor.shutdown();
executor.awaitTermination(10, TimeUnit.DAYS);
}
}
The advantage of using the FixedThreadPool is that you don't have to do the expensive process of creating the threads again and again, its done at the beginning...see below..
ExecutorService executor = Executors.newFixedThreadPool(100);
ArrayList<String> arList = Here your Email addresses from DB will go in ;
for(String s : arList){
executor.execute(new EmailAlert(s));
}
public class EmailAlert implements Runnable{
String addr;
public EmailAlert(String eAddr){
this.addr = eAddr;
}
public void run(){
// Do the process of sending the email here..
}
}
Creating a second thread to do all of the work in instead of doing the same work in the main thread isn't going to help you avoid the problem of filling up the emailRecords list with 5 million records before processing any of them.
It sounds like your goal is to be able to read from the database and send email in parallel. Instead of worrying about the code, first think of an algorithm for the work you want to accomplish. Something like this:
In one thread, query for the records from the database, and for each result, add one job to an ExecutorService
That job sends email to one person/address/record.
or alternatively
Read records from the database in batches of N (50, 100, 1000, etc)
Submit each batch to the executorService
I made a simple program in Processing, and it works great. Im now attempting to bring my beginner Java skills to Eclipse to make the same program over again. The program takes a Rs232 string that comes from the Com port and sends it over Xmpp.
The issue I'm having is that i cant call newChat.sendMessage(message) like i can in processing from any of the Classes/Tabs. Can someone clue me in to what i should look for to fix this issue. I'm guessing Extending, or implementing the Xmpp class is what i need to do. I love how simple processing handles it..
Any help will be greatly appreciated.
Fyi: the Smack library's are in the code folder for processing.
Main Application:
import processing.net.*;
void setup() {
size(400, 200);
noStroke();
background(0);
LoadSerialPort();
OpenChatConnection();
setupFilterThread ();
}
void draw() {
}
String timestampTime() {
Calendar now = Calendar.getInstance();
return String.format("%1$tH:%1$tM:%1$tS", now);
}
String timestampDate() {
Calendar now = Calendar.getInstance();
return String.format("%1$tm/%1$td/%1$tY", now);
}
Filter Class/Tab:
FilterThread thread1;
List FilteredArchAddressList = new ArrayList();
ArrayList CallList = new ArrayList();
void setupFilterThread () {
thread1 = new FilterThread(100, "a");
thread1.start();
}
void checkForNewCalls() {
if (CallList.size() >=1) {
println("New Call In List, Size: "+CallList.size());
FilterComPortString((String) CallList.get(0));
CallList.remove(0);
}
}
void FilterComPortString(String s) {
Message message = new Message("icu1#broadcast.server", Message.Type.normal);
//message.setTo("icu1#broadcast.x-dev");
message.setSubject("MSG_TYPE_NORMAL");
message.setBody(s);
message.setProperty("systemID", "JS1");
message.setProperty("serverTime", trim(timestampTime()));
message.setProperty("serverDate", trim(timestampDate()));
try {
newChat.sendMessage(message);
}
catch (Exception e) {
println(e);
}
}
}
class FilterThread extends Thread {
boolean running; // Is the thread running? Yes or no?
int wait; // How many milliseconds should we wait in between executions?
String id; // Thread name
int count; // counter
// Constructor, create the thread
// It is not running by default
FilterThread (int w, String s) {
wait = w;
running = false ;
id = s;
count = 0;
}
int getCount() {
return count;
}
// Overriding "start()"
void start () {
// Set running equal to true
running = true ;
// Print messages
println ("Starting thread (will execute every " + wait + " milliseconds.)");
// Do whatever start does in Thread, don't forget this!
super .start();
}
// We must implement run, this gets triggered by start()
void run () {
while (running) {
checkForNewCalls();
// Ok, let's wait for however long we should wait
try {
sleep((long )(wait));
}
catch (Exception e) {
}
}
System.out.println (id + " thread is done!"); // The thread is done when we get to the end of run()
}
// Our method that quits the thread
void quit() {
System.out.println ("Quitting.");
running = false ; // Setting running to false ends the loop in run()
// IUn case the thread is waiting. . .
interrupt();
}
}
Rs232 Class/Tab:
import processing.serial.*;
Serial myPort; // Rs232, Serial Port
String inString; // Input string from serial port:
int lf = 10; // ASCII linefeed
String SelectedCom;
void LoadSerialPort() {
println(Serial.list());
println("________________________________________________________");
try {
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.bufferUntil(lf);
SelectedCom = Serial.list()[0];
println("Connected to Serial Port:");
println("________________________________________________________");
}
catch (ArrayIndexOutOfBoundsException e) {
String exception = e.toString();
if (exception.contains("ArrayIndexOutOfBoundsException: 0")) {
println("NO AVAILABLE COM PORT FOUND");
}
else {
println(e);
}
}
}
void serialEvent(Serial p) {
inString = p.readString();
CallList.add(new String(inString));
}
Xmpp Class/Tab:
String FromXmpp = "";
Chat newChat;
ChatManager chatmanager;
XMPPConnection connection;
public void OpenChatConnection() {
// This is the connection to google talk. If you use jabber, put other stuff in here.
ConnectionConfiguration config = new ConnectionConfiguration("192.168.0.103", 5222, "Jabber/XMPP");
config.setSASLAuthenticationEnabled(false);
configure(ProviderManager.getInstance());
connection = new XMPPConnection(config);
println("Connecting");
try {
//connection.DEBUG_ENABLED = true;
connection.connect();
println("Connected to: "+connection.getServiceName() );
}
catch (XMPPException e1) {
println("NOT Connected");
}
if (connection.isConnected()) {
try {
// This is the username and password of the chat client that is to run within Processing.
println("Connecting");
connection.login("System1", "test");
//connection.login("inside_processing_username#gmail.com", "yourpassword");
}
catch (XMPPException e1) {
// would probably be a good idea to put some user friendly action here.
e1.printStackTrace();
}
println("Logged in as: "+connection.getUser() );
}
chatmanager = connection.getChatManager();
// Eventhandler, to catch incoming chat events
newChat = chatmanager.createChat("icu#broadcast.server", new MessageListener() { //icu1#broadcast.x-dev //admin#x-dev
public void processMessage(Chat chat, Message message) {
// Here you do what you do with the message
FromXmpp = message.getBody();
// Process commands
//println(FromXmpp);
}
}
);
Roster roster = connection.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
System.out.println(entry);
}
}
public void configure(ProviderManager pm) {
// Private Data Storage
pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider());
// Time
try {
pm.addIQProvider("query", "jabber:iq:time", Class.forName("org.jivesoftware.smackx.packet.Time"));
}
catch (ClassNotFoundException e) {
println(("TestClient "+" Can't load class for org.jivesoftware.smackx.packet.Time"));
}
// Roster Exchange
pm.addExtensionProvider("x", "jabber:x:roster", new RosterExchangeProvider());
// Message Events
pm.addExtensionProvider("x", "jabber:x:event", new MessageEventProvider());
// Chat State
pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("composing", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
// XHTML
pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());
// Group Chat Invitations
pm.addExtensionProvider("x", "jabber:x:conference", new GroupChatInvitation.Provider());
// Service Discovery # Items
pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
// Service Discovery # Info
pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
// Data Forms
pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
// MUC User
pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user", new MUCUserProvider());
// MUC Admin
pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin", new MUCAdminProvider());
// MUC Owner
pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner", new MUCOwnerProvider());
// Delayed Delivery
pm.addExtensionProvider("x", "jabber:x:delay", new DelayInformationProvider());
// Version
try {
pm.addIQProvider("query", "jabber:iq:version", Class.forName("org.jivesoftware.smackx.packet.Version"));
}
catch (ClassNotFoundException e) {
// Not sure what's happening here.
}
// VCard
pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
// Offline Message Requests
pm.addIQProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());
// Offline Message Indicator
pm.addExtensionProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());
// Last Activity
pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
// User Search
pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
// SharedGroupsInfo
pm.addIQProvider("sharedgroup", "http://www.jivesoftware.org/protocol/sharedgroup", new SharedGroupsInfo.Provider());
// JEP-33: Extended Stanza Addressing
pm.addExtensionProvider("addresses", "http://jabber.org/protocol/address", new MultipleAddressesProvider());
// FileTransfer
pm.addIQProvider("si", "http://jabber.org/protocol/si", new StreamInitiationProvider());
pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
// Privacy
pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider());
pm.addIQProvider("command", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider());
pm.addExtensionProvider("malformed-action", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.MalformedActionError());
pm.addExtensionProvider("bad-locale", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadLocaleError());
pm.addExtensionProvider("bad-payload", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadPayloadError());
pm.addExtensionProvider("bad-sessionid", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadSessionIDError());
pm.addExtensionProvider("session-expired", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.SessionExpiredError());
}
If you know your way around eclipse, first you need to create a new Java project and add Processing's core.jar to the build path and start by creating a sublcass of PApplet. If not, there's a really easy to work with eclipse plugin called Proclipsing and video guide to get started with it.
Bringing your code from Processing to eclipse:
You need to understand that all the code you have in a Processing sketch (including tabs) gets merged into a single .java file and the sketch name is the class name. Any classes you define in tabs in the sketch become nested classes in a main single class. Easiest way to see this is to export an applet and have a look in the generated folder for the SketchName.java file. Remember, a tab is not a class (although it can contain one).
So you've got a few options. Here are two basic approaches:
Create a new PApplet subclass and start pasting your whole code in it (including content of tabs). You will need to de explicit about accessors (e.g. public void setup() instead of just void setup()) and be careful with double/float values (e.g. if eclipse complains about 1.0, be explicit: 1.0f)
The other option is to create multiple classes, as I imagine you intend to do if you're using eclipse. The catch is, in Processing, variables defined in tabs are actually 'global' variables. Your code would work the same way if you declare those in the main tab at the top. Also, using PApplet functions needs to be refactored. Some functions can be called using Java's classes (not PApplet's) to break the dependency: e.g. System.out.println() instead of println(), but of others you might want your classes to have access to a PApplet instance:
e.g.
public class FilterThread extends Thread {
boolean running; // Is the thread running? Yes or no?
int wait; // How many milliseconds should we wait in between executions?
String id; // Thread name
int count; // counter
YourSketchClass parent;
// Constructor, create the thread
// It is not running by default
FilterThread (int w, String s,YourSketchClass p) {
wait = w;
running = false ;
id = s;
count = 0;
parent = p;
}
int getCount() {
return count;
}
// Overriding "start()"
public void start () {
// Set running equal to true
running = true ;
// Print messages
System.out.println ("Starting thread (will execute every " + wait + " milliseconds.)");
// Do whatever start does in Thread, don't forget this!
super .start();
}
// We must implement run, this gets triggered by start()
public void run () {
while (running) {
parent.checkForNewCalls();
// Ok, let's wait for however long we should wait
try {
sleep((long )(wait));
}
catch (Exception e) {
}
}
System.out.println (id + " thread is done!"); // The thread is done when we get to the end of run()
}
// Our method that quits the thread
void quit() {
System.out.println ("Quitting.");
running = false ; // Setting running to false ends the loop in run()
// IUn case the thread is waiting. . .
interrupt();
}
}
Every class which uses the chat-functionality, needs to know the chat-object. You can put all your variables and functions which are not in a separate class in the main source-file.
When you declare a variable in Processing, it can be used from any tab, in java global variables do not exist. You can get a similar functionality like this: Create a new file GlobalVars.java with this content:
public class GlobalVars {
public static Chat myChat;
}
Now everytime you want to access the Chat-functions, you have to write e.g. GlobalVars.myChat.sendMessage("Hi!");
Btw, You have to include the net.jar-file. On Mac this is located here:
Processing.app/Contents/Resources/Java/modes/java/libraries/net/library/net.jar
I'm making a multyplayer game and I'm using java sockets for the server, the server is working very well but I think it needs some tweaks regarding the way I'm parsing/handling the requests.
I would like to know if there is a better way of parse the requests instead of splitting input lines by (,) commas like I'm doing.
Should the data sent between the client and server be encrypted in some way? Just a small encryption to obfuscate the requests on sniffers eyes. What is the best way of doing that?
And regarding the thread safe is it OK with Collections.synchronizedList and synchronized blocks on iterations? Or is there a better/cleaner way?
Finally is there any java sockes library that will do all those things above? If so should I use it or is that an overkill for a small java sokets game server.
Is any thing else that I should improve?
Thanks :)
Bellow is the basic structure of my server.
public class Servidor extends Thread {
private ServerSocket serverSocket;
public static boolean LISTENING = true;
private final List<Client> clients = Collections.synchronizedList(new ArrayList<Client>());
private final List<Game> games = Collections.synchronizedList(new ArrayList<Game>());
public Servidor() {
try {
serverSocket = new ServerSocket(SERVER_PORT);
} catch (IOException e) {
Log.add("error starting server: " + e);
}
}
#Override
public void run() {
// Wait for players to connect
while (LISTENING) {
try {
Client c = new Client(serverSocket.accept());
clients.add(c);
c.start();
} catch (IOException e) {}
}
}
class Client extends Thread {
private Socket socket;
private PrintWriter out;
private BufferedReader in;
private boolean loggedin;
private Player player;
private Game game;
public Cliente(Socket sock) {
socket = sock;
try {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8")), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
} catch (IOException e) {
Log.add("error connecting to player!");
}
}
/* Send to this player */
public void send(String s) {
out.println(s);
}
/* Send to all players on the server */
public void boardcast(String s) {
synchronized (clients) {
for (Client c : clients) {
c.send(s);
}
}
}
/* Get player by ID */
private Player getPlayerByID(int playerID) {
synchronized (clients) {
for (Client c : clients) {
if (c.player != null) {
if (c.player.getID() == playerID) {
return c.player;
}
}
}
}
return null;
}
/* Split inputLine */
private String[] splitInput(String input, int argsNumber) {
String[] args = null;
try {
args = input.split(",", -1);
if (args.length != argsNumber) {
args = null;
}
} catch (Exception ex) {
Log.add("error splitting input");
} finally {
return args;
}
}
#Override
public void run() {
char cmd;
String inputLine, outputLine;
String[] args;
try {
loop:
while ((inputLine = in.readLine()) != null) {
// check if inputLine have 2 chars (CMD_TYPE:)
if (inputLine.length() < 2) {
kickPlayer();
break loop;
}
// get CMD
cmd = inputLine.charAt(0);
// remove (CMD_TYPE:) from inputLine
inputLine = inputLine.substring(2);
// check e player is logged (L:username,password)
if (!loggedin) {
// check if the input string have 2 arguments
if ((args = splitInput(inputLine, 2)) == null) {
kickPlayer();
break loop;
}else{
// ... TESTE LOGIN ON DATABASE ...
// set player data
player = new Player(query.getInt("player_id"), query.getString("username"), query.getInt("level"))
}
}else{
// Commands
switch (cmd) {
// P:CARD_ID,TARGET_ID eg:(P:5:3)
case CMD_PLAY:
// check if the input string have 2 arguments
if ((args = splitInput(inputLine, 2)) == null) {
kickPlayer();
break loop;
} else {
// ... VALIDATE OTHER PARAMETERS ...
// update game
game.addCard(args[0], args[0]);
// update players
boardcast(CMD_PLAY + ":" + player.getID+ "," + game.LastCard());
}
break;
// ... TEST OTHER COMMANDS ...
default:
Log.add("invalid command";
break loop;
}
}
}
} catch (IOException e) {
Log.add("connection lost";
} finally {
removeClient();
}
}
}
}
I am a programmer for 3 years now but now I am really trying to learn network server programming.
Some tips that might help you:
You should take a look about some design patterns to make you code more organized and extensible. You can specify a class for every message type and wrap it. It can use text delimiters or message size for framing the messages.
Keep the message handling code separated from the Client class you created, this makes easier to add support to new messages.
This would be something like:
class ClientHandler
{
public void run()
{
while(isConnected())
messageRouter.handleMessage(getNextMessage());
}
}
interface MessageHandler
{
public boolean canHandle(Message m);
public void handleMessage(Message m);
}
class MessageRouter
{
private List<MessageHandler> handlers;
public void handleMessage(Message msg)
{
for(MessageHandler m : handlers)
{
if(m.canHandle(msg))
{
m.handle(msg);
return;
}
}
throw UnsupportedMessage();
}
}
Something like that...
BTW: I am Brazilian too
good luck with your project
Adding basic security to communication is fairly simple and can be achieved without a lot of hassle. It took me a day of "googling" to figure out how to use it ... I have forgotten most of the details but if you want I can upload the source of my project for you to have a look at ... I personally would recommend some encryption before actually releasing the game...
A link for java ssl sockets can be found here
A link to create your own keystore can be found here
That should help you get started and message me if you want me to upload my source code.
As far as the lists are concerned most of the time you will only be reading the lists so I would suggest that you make a custom lock so that writers must acquire write lock whereas multiple readers can acquire the lock concurrently.. Will require you to design a new class or perhaps you can find a solution on-line.
As far as parsing the string is concerned, to the best of my knowledge objects can be passed remotely so you can transmit array of string instead of having to parse them. I personally broke my communication into several calls to read and write to input streams (probably the slowest approach :) )
and any ways good luck with your project. Message me if you need the source :)
.