I've developed a decent amount of multiplayer games lately in java (board/turn based games running on a tcp connection with data in/out streams) but they only work locally, I want to go a step further and make them run online, what do I need to change to make it work?
It seems like there are many ways to accomplish that yet i don't know where to start, as I don't know much about networking.
Besides that I don't have a real server, I'm only using my home router and my pc.
So here is what I've tried so far:
I enabled port forwarding in my router and I think it works (I used a port forwarding checking tool online)
I created a dynamic DNS for my public ip using noip.com
So the server side should be fine at least, my problem is with the client side, the client's socket won't connect to my public ip, when I searched for a solution I concluded that the client shouldn't be in the same LAN where the server is, so I used a mobile hotspot as a second network and tried again, but got the same results. (connection refused exception)
is it because of the mobile hotspot (should I use another router) ?
or is it just some coding tweaks ?
This is a minimal server class example
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(50895);
Socket cs = ss.accept();
DataOutputStream out = new DataOutputStream(cs.getOutputStream());
DataInputStream in = new DataInputStream(cs.getInputStream());
out.writeInt(123456);
} catch (IOException e) {
e.printStackTrace();
}
}
And this a minimal client class example
public static void main(String[] args) {
try {
String ip = Inet4Address.getByName("my Dynamic IP address").getCanonicalHostName();
System.out.println(ip);
InetSocketAddress sa = new InetSocketAddress(ip, 50895);
Socket cs = new Socket();
cs.connect(sa);
DataOutputStream out = new DataOutputStream(cs.getOutputStream());
DataInputStream in = new DataInputStream(cs.getInputStream());
System.out.println("received : " + in.readInt());
} catch (IOException e) {
e.printStackTrace();
}
}
The method that i've tried always gives me a connection refused exception, so any solution would be appreciated.
I found a software solution to the problem through virtual lan (using LogMeIn Hamachi) which is basicly used for creating game servers, you just need to create an account and a network so your friends can join it, after that everything should be fine and runs as if it's locally hosted.
You can download the software through here but there are many alternatives to choose from, that one gives you up to 5 people on the network for free, it's not the best, but still a free solution.
I still want more of a java solution to this problem that doesn't require a third-party software.
Related
I'm making a simple UDP chat program, and i would like the server to be able to send to the client without receiving data from it first. Normally, when it receives data from the client, the server gets the IP and port of the client, so it can communicate with it.
My server code:
package com.ageforce;
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class ChatServer {
DatagramSocket server;
byte[] receivedData = new byte[1024];
Scanner scanner = new Scanner(System.in);
byte[] sentData;
DatagramPacket dp2 = new DatagramPacket(receivedData, receivedData.length);
public ChatServer() throws SocketException {
server = new DatagramSocket(7000);
}
public static void main(String[] args) throws SocketException {
ChatServer cs = new ChatServer();
Thread receiveMessage = new Thread(() -> {
while (true) {
try {
cs.server.receive(cs.dp2);
} catch (IOException e) {
e.printStackTrace();
}
String storedData = new String(cs.dp2.getData());
System.out.println(storedData);
}
});
Thread sendMessage = new Thread(() -> {
while (true) {
String sentMessage = cs.scanner.nextLine();
cs.sentData = sentMessage.getBytes();
// This is the area of the code where the server gets IP and port of client from the received data. I'd like this to be changed.
InetAddress getIP = cs.dp2.getAddress();
int port = cs.dp2.getPort();
DatagramPacket dp3 = new DatagramPacket(cs.sentData, cs.sentData.length, getIP, port);
try {
cs.server.send(dp3);
} catch (IOException e) {
e.printStackTrace();
}
}
});
sendMessage.start();
receiveMessage.start();
}
}
Is it possible to do this? Any reply is greatly appreciated.
How do i get the port and address of client without receiving data from it first?
UDP does not afford that possibility. It is a connectionless protocol, so the server doesn't even know that there is a client until it receives a message from it.
You could conceivably create some kind of preliminary application-level protocol whereby the client announces itself to the server before sending it any chat data, but nothing of the sort is part of UDP itself, and if that's something you want then you should consider using TCP instead, which does have a built-in concept of establishing a connection before sending any data.
In order to send an UDP message to something, you need to know the IP and port number to send it to. How? Well, you tell me.
The way your protocol works right now is that the clients know your hostname (which they let their system turn into an IP automatically), and the port number is hardcoded in the client app. The server knows which IP and port to send data back to by checking the 'sender' IP/port when it receives a call.
If you don't like your current protocol you'll need to figure out a different way to answer this question.
Even if you find a way, you'll find that this is mostly useless. The vast majority of end-users intentionally do not have a so-called publically routable IP address. You cannot reach them by design. In your current protocol, that IP/port combo you send back to isn't really the sending computer at all. It's some router, for example the router in their home. That router saw the outgoing UDP packet and is remembering for a while: Any traffic coming in on that port is supposed to go to that computer in this house.
Short of completely crazy stuff such as 'hole punching' (which skype used for a while, not sure if they still do, it's a complicated hack that doesn't always work and is surely not what you want here - you can search the web for that term), there's simply nothing you can do here. end-user systems aren't servers and cannot be reached like this.
Your run-of-the-mill chat apps will always have clients ping the server and then just keep that connection open as long as they can.
So I am working on a software that will monitor(and may alter by acting as a Forrowder) all the DNS requests made by my router.
What I did?
So for first I wrote a Java code that can listens to a specific port and prints all the requests to the console[For now I just want to test with the requests].
The code is:
import java.net.*;
import java.io.*;
public class PortLogger{
public static void main(String[] args) {
LoggerServer loggerServer = new LoggerServer(53);
loggerServer.start();
}
}
class LoggerServer extends Thread{
private int port;
public LoggerServer(int port){
this.port = port;
}
#Override
public void run(){
try{
int id = 1;
ServerSocket server = new ServerSocket(port);
System.out.println("Server Listening at port " + port);
Socket client;
while(true){
client = server.accept();
ClientHandler clientHandler = new ClientHandler(client, id++);
clientHandler.start();
}
}catch(Exception ex){
System.out.println("Exception at Server : 1 :: EX = " + ex);
}
}
}
class ClientHandler extends Thread{
private Socket client;
private int id;
public ClientHandler(Socket client, int id){
this.client = client;
this.id = id;
}
#Override
public void run(){
try {
String data = "";
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
while(true){
data = reader.readLine();
if(data.length() > 0){
System.out.println("Client : " + id + " :: " + data);
}
}
}catch(Exception ex){
System.out.println("Exception at Client : " + id + " :: EX = " + ex);
}
}
}
The sole propose of this code for now is to Show me all the requests made to the server.
I know that I also have to change the DNS Server in my router for this.
So, for that I first tried by going to internet setup and put the local IP of my computer as DNS server.
But it was showing :
DNS IP and LAN IP must be on different networks!
But I found another way to do it.
It is as follows:
I went to the setup wizard of the router and the set the DNS Server to the same IP.
Surprisingly this worked!
[I have no idea whether this is a bug in the D-Link Firmware or not.
I have also added an exception to allow all request both inbound and outbound to port 53.
What is the problem?
So now the problem is that even after successfully changing the DNS to my servers. There seemed to be no requests at all to the console. I tried a lot but nothing.
I checked that the program was working fine by voluntarily sending request to it using telnet?
Now am I doing anything wrong or there is some bug with the router(its is a old one).
NOTE: The black lines on the images are just to hide my public IP address nothing special.
EDIT: I tried a few more times then found that websites were not opening when I changed the DNS in my router but still nothing in the console!
While it is difficult to give you a complete answer why your application doesn't work I can suggest some ways to investigate:
Port 53 is a privileged port. This means on Linux binding to that port requires root privileges and the application will throw an exception due to 'permission denied' if executed as a 'normal' user. As you are using Windows I don't know what it does if you try to bind as a 'normal' user, or you might be executing as an Admin user (or whatever the equivalent of 'root' is in Windows) and you don't know it. It might even just silently fail i.e. appear to bind when in fact it hasn't and no data is passed through you your application. As an aside, defaulting to 'root' as the default execution user in Linux is not the norm because it's insecure and most Linux distributions if not all do not allow this by default i.e. you can have this but you have to tell the distribution this is what you intend during installation. I'll let you come to your own conclusions what stance Windows takes for making users 'admin'...
In a scenario such as this if it were me I would immediately go to some networking tools to see what is happening. On Linux this is tcpdump or Wireshark. You can also get Wireshark for Windows as it's a GUI application. This will let you monitor and filter network traffic and so will be independent of your application. You can filter by source or destination address and/or port number.
I would leave the DNS setting alone in the router and change the DNS settings in one machine first, call it the test client, and set its DNS address to the machine where your application is running. Using tcpdump or Wireshark you can then make requests on your test_client e.g. browser requests and see the resulting network traffic.
You never mentioned if after changing your router's DNS settings all browser requests from clients fail. This is what I would expect to see if your router can no longer get a name resolution. However there maybe some DNS caching going on in your clients so you may appear to get successful DNS requests on your test_client. Again look at network traffic or use a Linux client which will provide you with much better networking tools.
I try to develop an android app and I got stuck on this problem.
I want to send two string values(longitude, latitude) from one android device to other android devices that have the same app. From researching online, my understanding is that the android devices sends the two strings to a server and then I can use google gcm to send those strings to other devices(I think). Maybe the server can run java and I can write some program to do something with the data it receives later.
I have successfully obtained the location data from the android device, but got stuck on sending this location from android phone to a server on my computer. My professor told me that I can make my computer a server. Before connecting android and my mac, I tried connecting two computers. I successfully connected my imac and macbook using Socket&SocketServer examples found at the bottom of http://www.tutorialspoint.com/java/java_networking.htm. It took a long time because I didn't know I had to remove the firewall.
So I found another example to connect an android to a computer using Socket at http://lakjeewa.blogspot.ca/2012/03/simple-client-server-application-for.html and tried it. Although, two computers could be connected, android won't connect to my computer. I think the problem is in the code
client = new Socket(serverName, port);
I tried many things for serverName such as the computer's ip address that start with 192.168, "localhost", 10.0.2.2, http:/myIpAdressFoundByGoogling, "myComputerName.local" but none worked. What's the problem here? The android device does not have a sim card and it is connected to the same wifi as the computer. I should have XAAMPP? (I just found about this last night) and learn php first? I am completely lost what to do with server.
I ended up here because I was thinking I can test and develop the app using a computer as a server and when I finish developing, I can get a server(like amazon web services, I am not sure yet) and replace the server location in the code, and copy and paste the server java code. Can I do this? maybe Google App Engine is a viable solution for my problem?
The client code is...
public class MainActivity extends Activity {
private Socket client;
private PrintWriter printwriter;
private EditText textField;
private Button button;
private String message;
private int port = 5555;
private String serverName = "192.168.?????"; I think this is where the problem is
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textField = (EditText) findViewById(R.id.editText1);
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
message = textField.getText().toString();
textField.setText("");
try {
// connect with server
client = new Socket(serverName, port);
printwriter = new PrintWriter(client.getOutputStream(),
true);
printwriter.write(message);
printwriter.flush();
printwriter.close();
client.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
The server code is...
public class main {
private static ServerSocket serverSocket;
private static Socket clientSocket;
private static InputStreamReader inputStreamReader;
private static BufferedReader bufferedReader;
private static String message;
public static void main(String[] args) {
int port = 5555;
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
System.out.println("could not listen on port: " + port);
e.printStackTrace();
}
System.out.println("server started listening to the port " + port);
while (true) {
try {
// accept the client connection
clientSocket =serverSocket.accept();
System.out.println("someone connected to the server");
inputStreamReader = new InputStreamReader(clientSocket.getInputStream());
//get the client meesage
bufferedReader = new BufferedReader(inputStreamReader);
message = bufferedReader.readLine();
System.out.println(message);
inputStreamReader.close();
clientSocket.close();
} catch (IOException e) {
System.out.println("problem in reading the message");
e.printStackTrace();
}
}
}
You are probably unable to connect your android device to your computer wither because there is some configuration problem or something is wrong in the code at one end. I have previously successfully sent messages from an android device to a "server" that ran on my laptop, I did this with the Socket and ServerSocket technique very similar to the example you linked to. So this is more than possible. There is no need to install additional software on either side (Java run-time is enough).
If both the computer and the android device are connected on the same wifi network you should use the internal LAN address. If you are running this on the android emulator and you want to connect to the host computer (the computer that runs the emulator) you can use 10.0.2.2 or just the computers address (this works also). What port are you using? Is this port open on your router settings, firewalls, etc.?
You do not have to learn PHP or App Engine to do what you are trying to do. You can, and these will also allow you to do this (I have no experience with App Engine but I am pretty sure it can). As for the PHP you will need to install XAAMPP on your machine to be able to use this as a server.
Another question is: If you plan to actually put this on a server you should check what kind of server you will have/need. A simple hosting package will most likely not allow you to run your own java code there but will probably allow you to run PHP scripts, for example.
If you post the networking code of both the client and the server, you will be able to get more accurate feedback than just my general and top-level answer.
I am taking an Android development class at the moment as well. I think you should look into Parse, it is a free service that provides all the backend server stuff for you, for free. We have been using it in out app. Once you get the hang of how the callbacks work it's very easy to use. Otherwise I would recommend looking into some kind of web framework like ruby on rails, and write your backend in that. You can deploy all that onto heroku and then assess your backend with android async talker.
Have you tried to connect to your server via a webservice and then consume that webservice in your application? I've successfully connected my device to my laptop doing this.
Take a look at this guys tutorial:
http://android.programmerguru.com/android-webservice-example/
I believe this is the best practice when communicating device to server.
I am trying to build a very simple socket server in JAVA that my Flash application can listen to. I am using this tutorial. Everything seems to be working - the JAVA code is compiled and the server is running.
My question is: how can external applications send messages to this server using just an IP address and a port number? My goal is that flash can listen to socket messages sent by an external application.
The Java code:
import java.io.*;
import java.net.*;
class SimpleServer {
private static SimpleServer server;
ServerSocket socket;
Socket incoming;
BufferedReader readerIn;
PrintStream printOut;
public static void main(String[] args) {
int port = 8080;
try {
port = Integer.parseInt(args[0]);
} catch (ArrayIndexOutOfBoundsException e) {
// Catch exception and keep going.
}
server = new SimpleServer(port);
}
private SimpleServer(int port) {
System.out.println(">> Starting SimpleServer");
try {
socket = new ServerSocket(port);
incoming = socket.accept();
readerIn = new BufferedReader(
new InputStreamReader(
incoming.getInputStream()));
printOut = new PrintStream(incoming.getOutputStream());
printOut.println("Enter EXIT to exit.\r");
out("Enter EXIT to exit.\r");
boolean done = false;
while (!done) {
String str = readerIn.readLine();
if (str == null) {
done = true;
} else {
out("Echo: " + str + "\r");
if(str.trim().equals("EXIT"))
done = true;
}
incoming.close();
}
} catch (Exception e) {
System.out.println(e);
}
}
private void out(String str) {
printOut.println(str);
System.out.println(str);
}
}
Maybe I don't understand correctly your problem description, but if you create the server in Java, it listens to its port and not your Flash application. If you want your Flash application to wait for messages from other applications, it must have a server role and listen to a TCP port the same way as this Java server does.
You can connect to and test the given Java server easily by telnet program (available in all operating systems) by providing a host name or an IP address and a port as parameters:
telnet 127.0.0.1 8080
Any other application can connect in a similar way, using just a hostname/IP address and a port. For example in Java, you can create a client socket:
Socket clientSocket = new Socket("localhost", 8080);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
By not specifying an IP address for your socket, it will listen on 0.0.0.0 (all interfaces).
In fact, that will usually be your computer's IP / the server's IP.
Assuming that your application runs on your computer at home, there are three cases that cover most of the connection situations:
Connecting from the same machine:
Use 127.0.0.1:8080
Connecting from the same LAN (e.g. your brother's PC):
Use your LAN IP (e.g. 192.168.1.4:8080)
Connecting from WAN (outside your routers LAN) (internet e.g.):
Use your WAN IP.(e.g. 84.156.74.194). There are plenty websites, that tell you your WAN IP like this
You may have to setup your router, to forward the port 8080 to your PC
For simple connection tests, one could use a telnet client.
I think you are missing the point of client/server socket applications.
If you are building the socket server (with whatever programming language you chose), you will then need to connect with (a) socket client(s) to this server. After a connection is successfully established (persistent) between the client and the server, you can start what ever kind of communication you have implemented between them.
The server always acts as the passive, the client as active part in a socket server/client constellation.
I was checking the link that you are referring to. In that, the procedure to create a stand-alone server is mentioned which is the code that you have pasted as well.
According to the link, the application acts as the client and uses the XMLSocket methods to connect to this server. This application is the flash application that you are talking about. As mentioned in the link, by using the following code any flash application can connect and talk to the server:
var xmlsock:XMLSocket = new XMLSocket();
xmlsock.connect("127.0.0.1", 8080);
xmlsock.send(xmlFormattedData);
When you mention
My goal is that flash can listen to socket messages sent by an external application.
its actually the flash application that is the client and it cannot listen unless programmed to act as a server. I hope this provides some clarity!
I hope you can help. Im fairly new to progamming and Im playing around with java Sockets.
The problem is the code below. for some reason commSocket = new Socket(hostName, portNumber); is returning true even when it has not connected with the server (server not implemented yet!). Any ideas regarding this situation?
For hostName Im passing my local machine IP and for port a manually selected port.
public void networkConnect(String hostName, int portNumber){
try {
networkConnected = false;
netMessage = "Attempting Connection";
NetworkMessage networkMessage = new NetworkMessage(networkConnected, netMessage);
commSocket = new Socket(hostName, portNumber);
// this returns true!!
System.out.println(commSocket.isConnected());
networkConnected = true;
netMessage = "Connected: ";
System.out.println("hellooo");
} catch (UnknownHostException e){
System.out.println(e.getMessage());
} catch (IOException e){
System.out.println(e.getMessage());
}
}
Many thanks.
EDIT: new Socket(.., ..); is blocking isnt it? i thought in that case if that was processed without exceptions then we have a true connection?
EDIT: I played around with anti virus and now it is working!
Had that exact same situation a few days ago on a corporate computer, and searched for it for hours.
Check your antivirus, some antivirus (like E*** N**32) use live TCP scanning that make a connection succeed even if nothing is listening on the target port but will reset it later when you try to read/write from the socket.
Add this to your code:
commSocket.getOutputStream().write(0);
commSocket.getInputStream().read();
If you get a SocketException now, you should really consider to change your antivirus.
Alternatively, set a breakpoint in your application right after creating the socket, and then use netstat -ano (on Windows) to check which process id is associated with the other endpoint of your socket (which should be on your machine if you connect to localhost).
I would suggest you to disable your antivirus, but in some cases even that does not help to unload their broken live TCP scanning driver...
The Socket constructor connects right away and will throw an IOException if it doesn't succeed. So apparently you have connected successfully to a server (this could be one you didn't make yourself).