Are you connecting to a named instance? In which case, the initial connection is to the SQL Server Browser service (UDP 1434) and then that dynamically allocates a port for named instances. This port is allocated on instance startup.
See this TechNet Resource on how the SQL Server Browser Service allocates a port for named instances.
There is a way to explicitly define a static port that a named instance will be listening on, but it appears that you are seeing a dynamically allocated port.
I doubt that it's easy to do hole punching with TCP or switch the process connected to a port.
Try this for a reverse proxy through the firewall:
http://www.dest-unreach.org/socat/doc/socat-gender.txt
This solutions relies on socat, a swiss-army-knife like network tool which can connect almost any two endpoints and is bidirectional, unlike netcat or the |-pipes in command processors. It should be available for Windows, too.
You can either use socat easily to forward a TCP connection, or in this case: Reverse the "gender" of it: You'll have two socats running, one on each side of the firewall. One continuously connects to the outside. On the outside, socat will wait for a connection from someone else. Only if he connects, the outside socat will accept a connection from the inside socat. Once the inside socat connects to the outside socat, it will connect to the SQL-server. Voila, you can connect from outside to the SQL-server inside, and from the point of the firewall and from the point of TCP the connection looked like it was actually from inside to outside.
To copy from the link:
1) Start the double client on the inside server
// every 10 seconds, it tries to establish a connection to the outside host.
// whenever it succeeds, it forks a sub process that connect to the internal
// service and starts to transfer data
$ socat -d -d -d -t5 tcp:outside-host:80,forever,intervall=10,fork tcp:localhost:80
2) Start double server on the outside client
// wait for a connection from a local client. whenever it accepted it, forks
// a subprocess that tries to bind to the socket where the inside double
// client tries to connect (might need to wait for a previous process to
// release the port)
$ socat -d -d -d tcp-l:80,reuseaddr,bind=127.0.0.1,fork tcp-l:80,bind=outside-host,reuseaddr,retry=10
Best Answer
I would suggest following the guildelines provided by Microsoft here: TCP/IP port numbers required to communicate to SQL overall a firewall
You brought up the above statement in your comments to the other answers. This is something you have to handle on your firewall. The client chooses the port it wants to be communicated back on. As stated later down in the article above your firewall rules have to allow the dynamic allocation to occur.
I don't work with the application side but there might be something you do to force the port it wants to talk on.