as much as i know when a packet gets send in a network there are 2 addresses in packet:
- the first is the source address witch refers to the sender machine
- second is the destination address witch refers to receiver machine
in unity netcode you can just specify a single port and ip
if you start host or server the address that you had defined will be set to the source address
if you start client it will sets as destination address and source address will be filled automatically
this images will explain everything:

- server is the top left window
- client is the top right window
- host is the bottom right window
the situation is that the host and client are behind different NATs over internet and they want to connect to each other directly
server’s task - witch it port is open - is to provide client and host’s public IP and port to each other
after that client and host can directly connect to each other
(as you can see the IPs are invalid and local. but the udp hole punching sector has tested in real situation before)
here is the code:
async void ConnectAsync(){
int node = NodeType.value;
disconnect = false;
IPEndPoint recivedIp = null;
IPEndPoint lep = new IPEndPoint(IPAddress.Parse(ClientIP.text),ushort.Parse(ClientPort.text));
string Message = await Task.Run(() => {
string message = "message is empty";
if(node == 0){//Client
udp.Send(ServerIP.text,us(ServerPort.text),"Client",lep);
string response = udp.Listen(lep,out recivedIp);
IPPort resp = Deserialize(response);
//
recivedIp.Address = IPAddress.Parse(resp.IP);
recivedIp.Port = (ushort)resp.Port;
string recMsg = "-1";
string packet;
int hand = 0;
packet = hand.ToString();
while(true){
try
{
udp.Send(recivedIp.Address.ToString(),(ushort)recivedIp.Port,packet,lep);
recMsg = udp.Listen(lep,out recivedIp,rnd.Next(0,5000));
}
catch{}
finally{
}
if(int.Parse(recMsg) == 0){
hand = 1;
packet = hand.ToString();
}
else if(int.Parse(recMsg) == 2){
break;
}
}
message = recMsg;
}
else if(node == 1){//Server
string response;
IPEndPoint recivedIp;
IPEndPoint lep = new IPEndPoint(IPAddress.Parse(ServerIP.text),ushort.Parse(ServerPort.text));
IPPort Client = new IPPort();
IPPort Host = new IPPort();
for (int i = 0; i < 2; i++)
{
response = udp.Listen(lep,out recivedIp);
IPPort resp = new IPPort{
IP = recivedIp.Address.ToString(),
Port = recivedIp.Port,
NodeType = response
};
if(response == "Client"){
Client = resp;
}
else{
Host = resp;
}
}
udp.Send(Client.IP,(ushort)Client.Port,Serialize(Host),lep);
udp.Send(Host.IP,(ushort)Host.Port,Serialize(Client),lep);
return "OperationEnded";
}
else if(node == 2){//Host
udp.Send(ServerIP.text,us(ServerPort.text),"Host",lep);
string response = udp.Listen(lep,out recivedIp);
IPPort resp = Deserialize(response);
//
recivedIp.Address = IPAddress.Parse(resp.IP);
recivedIp.Port = (ushort)resp.Port;
string recMsg = "-1";
string packet;
int hand = 0;
packet = hand.ToString();
while(true){
try
{
udp.Send(recivedIp.Address.ToString(),(ushort)recivedIp.Port,packet,lep);
recMsg = udp.Listen(lep,out recivedIp,rnd.Next(0,5000));
}
catch{}
finally{
}
if(int.Parse(recMsg) == 1){
hand = 2;
packet = hand.ToString();
udp.Send(recivedIp.Address.ToString(),(ushort)recivedIp.Port,packet,lep);
break;
}
}
message = recMsg;
}
return message;
});
this.MessageText.text = Message;
IPEndPoint hostIp = recivedIp;
IPEndPoint clientIp = null;
if(node == 0){//client
unityTransport.ConnectionData.Address = lep.Address.ToString();
unityTransport.ConnectionData.Port = (ushort)lep.Port;
NetworkManager.Singleton.StartClient();
this.MessageText.text = "client started";
byte[] rec;
await Task.Run(()=>{
udp.Listen(lep,out recivedIp);
});
clientIp = recivedIp;
Debug.Log("host: " + hostIp.Address + " | " + hostIp.Port);
Debug.Log("client: " + clientIp.Address + " | " + clientIp.Port);
while(!disconnect){
rec = await Task.Run(()=>{
return udp.ListenBytes(lep,out recivedIp);
});
if(recivedIp.Address.ToString() == hostIp.Address.ToString()
&& recivedIp.Port == hostIp.Port){
udp.SendBytes(clientIp.Address.ToString(),(ushort)clientIp.Port,rec,lep);
}
else if(recivedIp.Address.ToString() == clientIp.Address.ToString()
&& recivedIp.Port == clientIp.Port){
udp.SendBytes(hostIp.Address.ToString(),(ushort)hostIp.Port,rec,lep);
}
else{
Debug.Log("Unknow IP and Port " + "| " + recivedIp.Address.ToString() + " | " + recivedIp.Port);
}
}
}
else if(node == 2){//host
unityTransport.ConnectionData.Address = lep.Address.ToString();
unityTransport.ConnectionData.Port = (ushort)lep.Port;
NetworkManager.Singleton.StartHost();
this.MessageText.text = "host started";
}
Debug.Log("ConnectAsync Done!");
}
the code is using udp socket to listen on the port and ip witch is given to client and shows the address in console log.
here is the result:

as you can see to port of the client is 51378 witch is never given to any machine.
(note that ip is still remain as 192.168.1.100 and that is because it is tested on machine’s local network. in actual test it will sets automatically. like the port was)
for udp hole punching i have to specify both client and host’s ip and port addresses
otherwise udp hole punch will fails
unity doesn’t let you to perform this.
there is only one connection data in network transport:

(the server listen address is completely ineffective. also you cant specify port for it)