I created a socket server that I wanted to assign an address to so that the socket server is global and available to clients. However, I tried for a long time to associate the server with the address and failed, since it is always a local address when I specify it.
(IP is a string, which is outside of this code snippet)
Here is my code:
new Thread(new Runnable() {
#Override
public void run() {
try {
InetAddress address = InetAddress.getByName(IP);
ServerSocket server = new ServerSocket(8080,0,address);
while(true) {
Socket client = server.accept();
System.out.println("Client connected!");
}
} catch(IOException err) {
err.printStackTrace();
}
}
}).start();
So is it possible to host the socket server globally or is the SocketServer only for localhosting?
Related
A TCP connection is identified by four elements(maybe protocol included): client port, client address, server port, server address. So it's possible for a client with one port to connect to multiple different servers.Because they're different TCP connections.
Here's my demo: a local client on port 9999 conncecting to two local servers on port 12345 and port 12346. But the code isn't correct.
Can anybody help me? Tell me how to correct it, please.(not use SO_REUSEPORT or fork)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class SocketTest {
public static void main(String[] args) throws IOException {
startServer(12345);
startServer(12346);
Socket socket = new Socket();
socket.bind(new InetSocketAddress(9999));
System.out.println("client: " + socket.getLocalSocketAddress().toString());
startClient(socket, 12345);
startClient(socket, 12346);
}
public static void startClient(Socket socket, int port) {
(new Thread() {
#Override
public void run() {
try {
// Problem: connect() can be called only once
socket.connect(new InetSocketAddress(port));
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public static void startServer(int port) {
new Thread() {
#Override
public void run() {
ServerSocket ss;
try {
ss = new ServerSocket(port);
System.out.println("listen on: " + ss.getLocalSocketAddress());
while (true) {
Socket s = ss.accept();
System.out.println("accept from: " + s.getRemoteSocketAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
You can bind 2 different sockets to the same local port (that is what SO_REUSEADDR is made for), but one single socket can only be connected to one single destination.
If you want to send one single message, and you want that message to be received by two servers, then you need multicast. multicast can only use UDP (not TCP) and one of the special multicast addresses (224.0.0.0 to 239.255.255.255) that isn't listed as reserved by IANA.
I am making an app that checks for certain connections (looking for a list of SSIDs) and creates an hotspot for said connection if it doesn't find any. Basically it should act as a local area network.
I am successful in creating the hotspot, and after that it listens on a server socket for incoming connections:
Thread socketThread = new Thread(new Runnable() {
#Override
public void run() {
int port = Constants.PORT + socketList.size()-1;
try {
ServerSocket serverSocket = null;
serverSocket = new ServerSocket(port);
Log.d(TAG, "Socket listening for connections: " + port);
Socket client = serverSocket.accept();
Log.d(TAG, "Server: connection done");
InputStream inputstream = client.getInputStream();
DataInputStream commStream = new DataInputStream(inputstream);
byte[] b = new byte[16];
while (commStream.read(b,0, 16) != -1)
Log.d(TAG, new String(b, "ASCII"));
serverSocket.close();
Log.d(TAG, "Server socket done");
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
});
socketList.add(socketThread);
socketThread.start();
If I create the hotspot and connect to it with my pc I am able to use netcat and connect with said socket:
netcat 192.168.43.1 8988
Where 192.168.43.1 is the default Android IP address for the hotspot and 8988 is the port I'm using.
However, when I try to do the same through another device running the app and connecting to the hotspot, it doesn't work.
Here's the client code:
clientThread = new Thread(new Runnable() {
#Override
public void run() {
int port = Constants.PORT ;
try {
Socket socket =new Socket();
socket.bind(null);
socket.connect((new InetSocketAddress("192.168.43.1", port)), 20000);
OutputStream stream = socket.getOutputStream();
DataOutputStream commStream = new DataOutputStream(stream);
commStream.write("1234567890123456".getBytes());
socket.close();
Log.d(TAG, "Client socket done");
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
});
clientThread.start();
It doesn't even connect to the server socket, it just waits until timeout. Is there anything I'm doing wrong here?
Thanks in advance.
You cannot create a server socket with IP(192.168.43.1) on the device which has created the hotspot. So just reverse the server socket on another device that has been connected to the hotspot.
e.g. Let's say you have created hotspot on Device A and Device B has been connected to A so create the server socket on Device B with assigned local IP and create Socket instance in A by finding the IP of Device B.
I have a void method called startServerConnection() that connects the server to a port, in this case 7777. This method is called inside an action listener for a button in another class, ClientGUI.
I'm pretty sure the code is correct, but for some reason the only output I get is "Waiting for connection..."
public void startServerConnection(){
try{
serverSocket = new ServerSocket(portNumber);
while(true){
System.out.println("Waiting for a connection...");
Socket clientSocket = serverSocket.accept();
System.out.println("Connection established on port: "+clientSocket.getLocalPort());
ClientConnection clientConnection = new ClientConnection(clientSocket);
Thread thread = new Thread(clientConnection);
thread.start();
}
}
catch(Exception e){
e.printStackTrace();
return;
}
}
EDIT
Client class, connectClient method:
public void connectClient(String user){
try{
host = clientSocket.getInetAddress();
clientSocket = new Socket(host,port);
new ClientHandler(clientSocket).run();
String accepted = "Connection for host "+host+" accepted on port: "+clientSocket.getPort();
}
catch(Exception e){
//sendMessage("Connection error: "+e);
//serverGUI.appendEventsLog("Client "+new ClientGUI(username, port)+" failed to connect");
}
}
Any ideas on what's wrong?
Update:
public void connectClient(String user){
try{
clientSocket = new Socket(host,port);
// Use PrintWriter to send data out to server
// Use BufferedReader to receive data from server
}
catch(Exception e){
//sendMessage("Connection error: "+e);
//serverGUI.appendEventsLog("Client "+new ClientGUI(username, port)+" failed to connect");
}
}
host is the IP address or the hostname, if server/client are running on the same machine, you can either use "127.0.0.1" or "localhost"; port is a value of int, in your case is 7777
Original:
accept() is a blocking function. the code afterwards would not go thorough until a connection is established.
You have to build a client side and request for connection, once the server and client is connected, you will see "Connection established on port.."
public Socket accept() throws IOException
Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.
I have a TcpServer class that is responsible to, well, act like a tcp server. You can find the class below :
public class TcpServer {
private ServerSocket serverSocket;
private Socket socket;
private int locallyBoundPort;
public TcpServer() {
}
public TcpServer(int locallyBoundPort) {
try {
this.serverSocket = new ServerSocket(locallyBoundPort);
serverSocket.setReuseAddress(true);
} catch (IOException e) {
System.out.println("Error at binding to port TCP : " + locallyBoundPort + "...cause : " + e.getMessage());
}
socket = null;
}
public void accept() {
try {
socket = serverSocket.accept();
socket.setReuseAddress(true);
} catch (IOException e) {
System.out.println("Error at accept : " + locallyBoundPort);
}
}
public void send(Data data) throws IOException {
if(socket != null) {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(data);
}
}
public Data receive() throws ClassNotFoundException, IOException {
if(socket != null) {
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
return (Data) in.readObject();
} else {
return null;
}
}
public boolean bind(int port) throws IOException {
try {
this.serverSocket = new ServerSocket(port);
this.locallyBoundPort = port;
} catch(IOException e) {
return false;
}
return true;
}
public void close() {
try {
serverSocket.close();
socket.close();
} catch (IOException e) {
OzumUtils.print("IOException in close, TcpServer");
}
}
public int getLocallyBoundPort() {
return locallyBoundPort;
}
public Socket getSocket() {
return socket;
}
public ServerSocket getServerSocket() {
return serverSocket;
}
}
And I have a code piece that does this :
TcpServer tcpServer = new TcpServer(LocalPort);
while(1)
{
tcpServer.accept();
Thread thread = new Thread(new runnable(tcpServer));
thread.start();
tcpServer = new TcpServer(LocalPort);
}
However I am getting a port already in use error. I thought two different socket instances could listen to the same port as multiplexing allows two connections through the same port when the connector has different ip or port ?
What am I missing?
You cannot bind two tcp server sockets to the same port. reuseAddress is really for client sockets, and it does not work the way you think it does ... and the way you are using it would not do anything at all either way (because you are setting it after binding).
You don't really need to bind twice to the same port either. Just remove this line tcpServer = new TcpServer(LocalPort); from the bottom of your while loop, and you'll be all set.
The way this works is that you bind your server socket once and listen to the port. When a connection arrives, it forks a client socket for you to communicate with the client, and the original server socket continues to listen for more connections.
Basically, you need to remove the socket member (and any other state) from your TcpServer, and make the accept method return the accepted socket. Then make your runnable take that socket as a parameter instead of the TcpServer, and use that to serve the client connection. Then just keep calling accept in the loop, and forking threads for new connections same way you do know, except do not recreate the server every time.
Or, alternatively, remove the server socket and port from TcpServer, create the socket outside the loop, then while(true) call accept on it, create a new TcpServer with the returned client socket, and use it in a thread to process the connection.
Do not forget to close client sockets after you are done with them.
No, you can't use a port already in listening state. However any number of clients can connect to this same port. You don't need to listen to the port again, you just spawn a new thread to process the current connection and wait for a new one. For example, supposing you have a class TcpConnectionHanlder that implements Runnable and takes the Socket as parameter, the loop would look like
while (true) { //while(1) is not valid Java syntax
final Socket tcpSocket = tcpServer.accept(); // Get socket for incoming connection
final Thread thread = new Thread(new TcpConnectionHanlder(tcpSocket)); // Create a thread for this socket/client connection
thread.start(); // Launch the thread
// tcpServer = new TcpServer(LocalPort); <- not needed, port still listening.
}
Then in your TcpConnectionHanlder instance you handle this particular client (socket).
I am trying to build a java microblogging app
I have finished the code but cannot connect to my own computer due to the following error (I google it and someone said I need to change the port number. I changed the port number and nothing happened)
Exception in thread "Thread-4" java.net.BindException: Address already in use: JVM_Bind
Below is the code for the server:
public class server extends Thread {
public Socket client = null;
public ServerSocket server = null;
private int port = 4444;
public void run(){
while (true){ //loop waits for incomming connection
try { //start the server and listen to a port
server = new ServerSocket(port);
}catch (IOException e){
System.err.println(e);
System.exit(-1);
}
try { //establish a connection when receiving request
client = server.accept();
}catch (IOException e){
System.err.println(e);
System.exit(1);
}
Thread t = new Thread(new Connection(client));
t.start();
}
}
}
And this is the code to start the server and listen to port 4444
Server server = new Server();
server.start(); //to listen to a port
Thank you
You must create the ServerSocket before entering the loop. At present you are trying to create it every iteration, which doesn't make sense, and you aren't closing it, so the second creation fails.