Speaking the TCP protocol, we communicate between "ports" in the local and remote ends. Each of these port fields are 16 bits in the protocol header so they can hold values between 0 - 65535. (IPv4 or IPv6 are the same here.) We usually do HTTP on port 80 and we do HTTPS on port 443 and so on. We can even play around and use them on various other custom ports when we feel like it.
But what about port 0 (zero) ? Sure, IANA lists the port as "reserved" for TCP and UDP but that's just a rule in a list of ports, not actually a filter implemented by anyone.
In the actual TCP protocol port 0 is nothing special but just another number. Several people have told me "it is not supposed to be used" or that it is otherwise somehow considered bad to use this port over the internet. I don't really know where this notion comes from more than that IANA listing.
Frank Gevaerts helped me perform some experiments with TCP port zero on Linux.
In the Berkeley sockets API widely used for doing TCP communications, port zero has a bit of a harder situation. Most of the functions and structs treat zero as just another number so there's virtually no problem as a client to connect to this port using for example curl. See below for a printout from a test shot.
Running a TCP server on port 0 however, is tricky since the bind() function uses a zero in the port number to mean "pick a random one" (I can only assume this was a mistake done eons ago that can't be changed). For this test, a little iptables trickery was run so that incoming traffic on TCP port 0 would be redirected to port 80 on the server machine, so that we didn't have to patch any server code.
Entering a URL with port number zero to Firefox gets this message displayed:
This address uses a network port which is normally used for purposes other than Web browsing. Firefox has canceled the request for your protection.
... but Chrome accepts it and tries to use it as given.
The only little nit that remains when using curl against port 0 is that it seems glibc's getpeername() assumes this is an illegal port number and refuses to work. I marked that line in curl's output in red below just to highlight it for you. The actual source code with this check is here. This failure is not lethal for libcurl, it will just have slightly less info but will still continue to work. I claim this is a glibc bug.
$ curl -v http://10.0.0.1:0 -H "Host: 10.0.0.1"
* Rebuilt URL to: http://10.0.0.1:0/
* Hostname was NOT found in DNS cache
* Trying 10.0.0.1...
* getpeername() failed with errno 107: Transport endpoint is not connected
* Connected to 10.0.0.1 () port 0 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.38.1-DEV
> Accept: */*
> Host: 10.0.0.1
< HTTP/1.1 200 OK
< Date: Fri, 24 Oct 2014 09:08:02 GMT
< Server: Apache/2.4.10 (Debian)
< Last-Modified: Fri, 24 Oct 2014 08:48:34 GMT
< Content-Length: 22
< Content-Type: text/html
Why doing this experiment? Just for fun to to see if it worked.
(Discussion and comments on this post is also found at Reddit.)