Advertisement

How is a real-time server port "find" known to client code

Started by March 02, 2017 11:39 AM
51 comments, last by hplus0603 7 years, 8 months ago

Following my last thread on socket programming, i have made some very good progress.

But there are still some chronic obstacles hindering me, even though these questions are raised and discussed (on the internet) for the server side, how to solve the client side mirror problem is not mentioned.

If a client is trying to connect to server, every one agrees that you need an algorithm that has to search for available ports on the server side. http://stackoverflow.com/questions/2675362/how-to-find-an-available-port One of the solutions is specifying the port number as "Zero", which then returns an available port. But this done in real time, as a Stack Overflow poster pointed out:

private Integer findRandomOpenPortOnAllLocalInterfaces() throws IOException {

try (
ServerSocket socket = new ServerSocket(0);
) {
return socket.getLocalPort();

}
}

If you need to find an open port on a specific interface check ServerSocket documentation for alternative constructors.

Warning: Any code using the port number returned by this method is subject to a race condition - a different process / thread may bind to the same port immediately after we close the ServerSocket instance.

But a bigger problem (which was not treated in the thread nor anywhere else i could find), is that, since the client has to supply the same port number, thus the client has to know before hand what the server's algorithm is searches in real-time while the client is already running!!! How is that possible.

I know there is a robust, viable and simple solution in use (otherwise the world would not work), but no one mentions it any where i could find

The only other solution I could think of is supplying the client side also with "Zero", but when I tried that, the server still kept waiting - no connection

How can i solve this? I would like to use "zero" as I can't guarantee any port i specify otherwise is not bound to a process already, yet I can't know what to port to supply at client side before hand

Even if I tried other solutions like iterating through specified ports to find available ports (at run-time), I am faced with exactly the same problem : the client doesn't know the port before hand, so it can't be included in the client code

Below is the complete Server and Client code

Client side first (android platform)


public class FSendfileActivity extends Activity {

    private static final int SELECT_PICTURE = 1;
    private String serverIP = "192.168.1.2";
    private String selectedImagePath;
    private ImageView img;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fsendfile);
        img = (ImageView) findViewById(R.id.ivPic);
       
        ((Button) findViewById(R.id.bBrowse))
                .setOnClickListener(new OnClickListener() {
                    public void onClick(View arg0) {
                        Intent intent = new Intent();
                        intent.setType("image/*");
                        intent.setAction(Intent.ACTION_GET_CONTENT);
                        startActivityForResult(
                                Intent.createChooser(intent, "Select Picture"),
                                SELECT_PICTURE);
                    }
                });
        ;
        Button send = (Button) findViewById(R.id.bSend);
        final TextView status = (TextView) findViewById(R.id.tvStatus);
        final String qcd = "qcd";
        Log.v(qcd, " --------  start 37 **-------");
        send.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View arg0) {
            	Log.v(qcd, " 00");

   //             Socket sock;
            	Log.v(qcd, " 01");
            	new Thread(new Runnable() {
                    @Override
                    public void run() {
                    	Socket sock;
                        try {
                        	Log.v(qcd, " 02");
                            sock = new Socket(serverIP, 0);      
                        	Log.v(qcd, " 03");
                            System.out.println("Connecting...");

                             // sendfile
                                  File myFile = new File (selectedImagePath); 
                                 byte [] mybytearray  = new byte [(int)myFile.length()];
                                  FileInputStream fis = new FileInputStream(myFile);
                                  BufferedInputStream bis = new BufferedInputStream(fis);
                                  bis.read(mybytearray,0,mybytearray.length);
                                  OutputStream os = sock.getOutputStream();
                                  System.out.println("Sending...");
                                  os.write(mybytearray,0,mybytearray.length);
                                  os.flush();

                                sock.close();
                        } catch (UnknownHostException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }                        
                    }
                }).start();
            }
        });
    }
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            if (requestCode == SELECT_PICTURE) {
                Uri selectedImageUri = data.getData();
                selectedImagePath = getPath(selectedImageUri);
                TextView path = (TextView) findViewById(R.id.tvPath);
                path.setText("Image Path : " + selectedImagePath);
                img.setImageURI(selectedImageUri);
            }
        }
    }

    public String getPath(Uri uri) {
        String[] projection = { MediaStore.Images.Media.DATA };
        Cursor cursor = managedQuery(uri, projection, null, null, null);
        int column_index = cursor
                .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    }
}

^

Server side code (pure Java, NOT Android platform)

Adjust int filesize=550000; to the size of the image you will select


public class FileServer {

    public static void main(String[] args) throws IOException {
        int filesize=550000; // filesize temporary hardcoded

        long start = System.currentTimeMillis(); 
        int bytesRead;
        int current = 0;
  
        ServerSocket servsock = new ServerSocket(0);
        InetAddress IP=InetAddress.getLocalHost();
        System.out.println("IP "+IP.getHostAddress()+"  ***%% :"+servsock.getLocalPort());
        while (true) {
          System.out.println("Waiting...");

          Socket sock = servsock.accept();
          System.out.println("Accepted connection : " + sock);

       // receive file
            byte [] mybytearray  = new byte [filesize];
            InputStream is = sock.getInputStream();
            FileOutputStream fos = new FileOutputStream("C:\\myRoot_folder\\image000028.jpg"); // destination path and name of file
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            bytesRead = is.read(mybytearray,0,mybytearray.length);
            current = bytesRead;

            do {
               bytesRead =
                  is.read(mybytearray, current, (mybytearray.length-current));
               if(bytesRead >= 0) current += bytesRead;
            } while(bytesRead > -1);

            bos.write(mybytearray, 0 , current);
            bos.flush();
            long end = System.currentTimeMillis();
            System.out.println(end-start);
            bos.close();

            sock.close(); 
            servsock.close();
          }
    }
}

^

xml layout


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <TextView
        android:id="@+id/tvPath"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Path: " />

    <Button
        android:id="@+id/bBrowse"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Browse" >
    </Button>

    <Button
        android:id="@+id/bSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send" />

    <ImageView
        android:id="@+id/ivPic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
    </ImageView>

</LinearLayout>
 

^

add to Androidmanifest.xml


    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>  
 

output


IP 192.168.1.2 ***%% :53240
Waiting...

can't help being grumpy...

Just need to let some steam out, so my head doesn't explode...

It's a lot simpler than all that. When you open a listening socket on a server, you need to pick a port and listen on that. For example, web servers are port 80. FTP is on port 21. There are a bunch of port numbers that are pre-agreed, so that client software knows in advance which port to connect to. For example, your browser always tries port 80 by default, unless you tell it otherwise. Your software should be the same: pick a port, and have your server and client connect to it. The expectation is that you own your server and have control over which ports are in use. There are documented lists of what is likely to be using each one to help you pick one that is unlikely to collide with an existing service.

The statement that "every one agrees that you need an algorithm that has to search for available ports on the server side" is false. The usual behaviour is to have agreed a port in advance. The only time I can see when it's useful to open a random listening port is when you already have some other connection open, through which you can communicate that chosen port. I'm sure there are use cases for that but I can't think of one right now, and you don't need it.

Advertisement

Both the question and the answer on the other side are ill-advised (trying not to say "uninformed"), for the exact reason that you state yourself:

While it is in principle possible to open port "zero" (and get a random one that is free) or iterate a range of port numbers until a free one is found, this is impractical. Someone is going to connect to that port, and how is this going to happen if the port number is not known? If this is supposed to work, one needs a separate communication channel for agreeing on the port.

Reality has it that a server application uses a well-known port. Fullstop.

So, how do you know another application isn't using that well-known port that you've chosen? Well, you don't know until you get a socket-in-use error. However, since you normally have control of the server, that is normally not an issue. Also, there are few, very few, ports that are likely to be in use on a normal computer, and they are all below port number 1024.

There exists a long, long list of "well-known port numbers", and although best practice says you are to choose a number that isn't taken, this is a bit unrealistic. On the other hand, for the most part you can just ignore that list because 99% of it is shit.

Even among the protocols in the special port range (below 1024), only a mere half dozen or so are really in use, all others are either deprecated, not used by anyone, or only used by a handful of geeks on the globe, or blocked for the immense security risks that they create. Or, something. As long as you pay attention that you don't pick exactly one of the few active services (HTTPS, SIP, and SSH come to mind, but FTP might also be such a candidate) you're good to go. In fact, although there are better choices, you could even use the same port as e.g. HTTPS if you are absolutely inclined to do so (and have appropriate privileges on the server) as long as you know that you're not running a webserver. Which should be the case if you own the server -- you had better know whether or not a webserver or a mailserver or any such thing is running on your own server!

You should in any case avoid the ports used by file sharing software and high-profile malware, though, since they're sometimes blocked by firewalls. So, this may come as a nasty surprise after going public when a particular group of users comes with complaints about "things not working" and you have no idea why, because as far as you can tell, there's no error. Ports being blocked may, by the way, be a valid reason for "abusing" an existing low port such as HTTPS, since for example the internet access in many hotels will filter out all but "web", and possibly "mail". Thus, if your protocol masquerades itself (illegitimately, but who cares) as HTTPS, your users will be able to play your game from a hotel room whereas otherwise they quite possibly won't.

So, formally, using a port that is used by a different service is bad, of course. But what's going to happen? On your end, exactly nothing. Nobody has any valid reason to try and open e.g. a SIP connection to your server (why would they?), but even so, if someone tries... your protocol is hopefully robust enough so your server won't crash if it gets a "garbage request" (actually a correct request, but from your point of view, it's garbage). Other servers are using that same port for something different? Well, what do you care. Sure, in an ideal world, things would be different, but... really, no practical problem there.

As others have said, you pick a port-number at your own server which you normally control sufficiently to that kind of thing.

Ports below 1024 are normally reserved for system services, eg at a Unix system you must be root to open such a port.

What may not be clear is that a port at the server is for listening to new connections only.

When a client connects, the server accepts the connection (iirc), and at that moment, a new port at the server is used for the connection with that client. The next client can again connect to the listen port (and on acceptance gets a new port at the server) etc.

At server-side you thus keep an eye on that listen port, as well as all accepted connections with clients (which normally have data being transferred in one or both directions).

When the client closes the connection or you don't want to talk to the client anymore, you rescue any final data if you want, and then also close the connection.

With TCP, a disruption in network connectivity does not necessarily close a connection (at least in theory, maybe the mobile network overrides that?). In theory, you can configure behavior of the connection in case of network breakdown. I have never bothered to try such things, so I cannot tell you what the options are exactly, but it might be good to check.

The expectation is that you own your server and have control over which ports are in use. There are documented lists of what is likely to be using each one to help you pick one that is unlikely to collide with an existing service. .....

....... The usual behaviour is to have agreed a port in advance.

Reality has it that a server application uses a well-known port. Fullstop. So, how do you know another application isn't using that well-known port that you've chosen? Well, you don't know until you get a socket-in-use error. However, since you normally have control of the server, that is normally not an issue. Also, there are few, very few, ports that are likely to be in use on a normal computer, and they are all below port number 1024.

When a client connects, the server accepts the connection (iirc), and at that moment, a new port at the server is used for the connection with that client. The next client can again connect to the listen port (and on acceptance gets a new port at the server) etc. At server-side you thus keep an eye on that listen port, as well as all accepted connections with clients (which normally have data being transferred in one or both directions).

Thanks a lot for the replies

What is clear from these is that the programmer at the server-side should be able to pre-determine and be in control of available ports. And I think this is where my failing(s) or at least one of my failings is ... since whichever port I select based its availability it fails to connect

OR Maybe as @[member='Alberth'], mentioned in the previous thread - a firewall maybe responsible, just can't tell now. I've got to use the process of elimination to check this out.

Just by chance if anyone has an Android device and they are willing to ... they can run a test with the code in the original post. Its got the complete code and no work needs to be added or done (apart from header imports, which the IDE will flag anyway. and use internal IP pls )

If it works I will know its a firewall on my side Thanks

can't help being grumpy...

Just need to let some steam out, so my head doesn't explode...

A couple comments regarding connection issues:

If you're having trouble connecting to your server on the port that it has bound, turn off any firewall software on both devices and be sure that the router on the server side is forwarding the port in question to the correct machine. Typically if you're hosting a service you should be using a static IP address for that machine on your subnet, or using some other feature of the router that ensures that incoming traffic on that port is sent to the correct machine. Once you establish a connection you can start re-enabling firewalls and etc one at a time and work out issues as you encounter them.

Secondly, regarding Android, on previous devices I never had trouble hosting services such as FTP on my Android devices, but my most recent device sometimes simply refuses to accept incoming connections. I've not been able to work out what the issue is, but if you have some other device (an older Android or just another computer) that you could test connections on that may help to identify whether or not Android is the issue.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
Advertisement

Also regarding connections, there is one that is left as zero / unspecified, but that's just to the way the tuple works.

Every connection has a pair of IP/Port for the server and a pair for the address. Three of those four are usually specified:

{ Host IP address, Host Port, Client IP address, Client Unused Port }

Alternatively:

{ fixed, fixed, fixed, variable }

By leaving the value of the client's port unassigned you can open up thousands of connections between two machines if you want to. Even though both IP addresses are fixed and the host's port is fixed, there will (or should be) an unused port on the machine attempting to connect.

Thanks a lot for the replies
What is clear from these is that the programmer at the server-side should be able to pre-determine and be in control of available ports. And I think this is where my failing(s) or at least one of my failings is ... since whichever port I select based its availability it fails to connect

Keep in mind if the port is in use or something like that then the code should break when you try to set up the socket. BSD sockets and winsock will give an error when you try to bind it to a port that is in use. So if everything works then you can assume that the socket is fine and that the connecting part is the problem.

OR Maybe as @[member=Alberth], mentioned in the previous thread - a firewall maybe responsible, just can't tell now. I've got to use the process of elimination to check this out.
Just by chance if anyone has an Android device and they are willing to ... they can run a test with the code in the original post. Its got the complete code and no work needs to be added or done (apart from header imports, which the IDE will flag anyway. and use internal IP pls )
If it works I will know its a firewall on my side Thanks

Firewalls are generally the culprit if you can't connect. The two obvious ones are your router firewall(forward the port) and your local firewall(add a port exception.) Usually connecting to localhost should work without much issue though. I couldn't say if mobile might have any OS related issues for blocking connections though.

Thanks @[member='frob'], et al.

Firewalls are generally the culprit if you can't connect.

Sadly for me I have been turning windows defender off at various runs but still the same, no connection.

I started to look at servsock documentation and I found a "bind" that i never used. I remembered either @rip-off or @hplus0603 mentioned that i need to also bind. Now on observing bind's parameters i noticed an immediate problem, The doc says....

the parameter is the IP Address and the port number, but there is only room for one parameter:- endpoint! Does mean I have to add the strings of IP Address and port number together? If so why can't the doc just say that instead of leaving the coder to be doing guess work?

And more crucially - whats the delimiter between the IP and port number? space? / ? | ? \ ? : ? , ? Now just resorting to guess work :(

Edit: my bad, reading further i can see that "SocketAddress" is class of its own that can instantiated, STILL there is no info yet on how to do this with the IP address and port number. I reckon "SocketAddress" has a method somewhere that takes the IP and port as parameters and returns a "SocketAddress" object


  • bind
    
    public void bind(SocketAddress endpoint)
              throws IOException
    Binds the ServerSocket to a specific address (IP address and port number).

    If the address is null, then the system will pick up an ephemeral port and a valid local address to bind the socket.


    Parameters:
    endpoint - The IP address and port number to bind to

can't help being grumpy...

Just need to let some steam out, so my head doesn't explode...

SocketAddress is an abstract class that you generally instantiate as an InetSocketAddress. An InetSocketAddress contains both address and port, though for binding you can generally just use the constructor that takes a single port number.

This topic is closed to new replies.

Advertisement