UDP 是 User Datagram Protocol 的简称, 中文名是用户数据报协议,是 OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768 是UDP的正式规范。UDP 在 IP 报文的协议号是17。UDP 协议与 TCP 协议一样用于处理数据包,在 OSI 模型中,两者都位于传输层,处于 IP 协议的上一层。UDP 有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP 用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用 UDP 协议。UDP 报文没有可靠性保证、顺序保证和流量控制字段等,可靠性较差。但是正因为 UDP 协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如 DNS、TFTP、SNMP 等。
UDP API 用于 UDP 通信。 它为实现无连接、面向数据报的通信提供了一个高级接口。具体的函数信息可以从下方得到:
-
UdpClose 函数
主要是关闭 UDP Socket。函数成功执行后,传递的 socket 不再有效。具体的使用范例请在文章末尾查看。具体的使用结构如下:
函数的返回值的情况如下:
0: 函数执行成功
WSA_INVALID_PARAMETER (87): 指定的 socket 无效
SOCKET_ERROR (-1): 函数执行失败。错误内容使用函数 IpGetLastError 查询 -
UdpConnect 函数
主要是将此 socket 连接到给定的远程端点。socket 连接后,必须使用 UdpSend 而不是 UdpSendTo。在服务器端,当第一次在 socket 上接收数据时,可以连接到远程端点。 之后,可以创建一个新的未连接的 Socket 以等待下一个传入的 udp 连接。具体的使用结构如下:
函数的返回值的情况如下:
0: 函数执行成功
WSA_INVALID_PARAMETER (87): 指定的 socket 无效
SOCKET_ERROR (-1): 函数执行失败。错误内容使用函数 IpGetLastError 查询
示例
// Client.can
variables
{
UdpSocket gSocket1;
UdpSocket gSocket2;
char gBuffer[1500];
}
on start
{
gSocket1 = UdpSocket::Open( IP_Endpoint(0.0.0.0:0) );
gSocket2 = UdpSocket::Open( IP_Endpoint(0.0.0.0:0) );
gSocket1.Connect(IP_Endpoint(192.168.1.3:40002));
gSocket2.Connect(IP_Endpoint(192.168.1.3:40002));
}
on key '1'
{
// send on the connected socket
SendCommand(gSocket1, "Request");
// wait for response
gSocket1.ReceiveFrom(gBuffer, elcount(gBuffer));
}
on key '2'
{
// send on the connected socket
SendCommand(gSocket2, "Request");
// wait for response
gSocket1.ReceiveFrom(gBuffer, elcount(gBuffer));
}
on key 'c'
{
SendCommand(gSocket1, "End");
gSocket1.Close();
SendCommand(gSocket2, "End");
gSocket1.Close();
}
SendCommand(UdpSocket socket, char command[])
{
socket.Send(command, strlen(command));
}
OnUdpReceiveFrom(dword socket, long result, IP_Endpoint remoteEndpoint, char buffer[], dword size)
{
// handle the response from the server here
}
//Server.can
variables
{
// context to handle the state of the different connections
struct connectionContext
{
int connectionNumber;
};
dword gListeningSocket;
struct connectionContext connections[long];
char gBuffer[1500];
dword gConnectionCount;
}
on start
{
gConnectionCount = 0;
// open a socket and wait for the first data
gListeningSocket = UdpOpen( IP_Endpoint(0.0.0.0:40002) );
UdpReceiveFrom(gListeningSocket, gBuffer, elcount(gBuffer));
}
OnUdpReceiveFrom(dword socket, long result, IP_Endpoint remoteEndpoint, char buffer[], dword size)
{
if(socket == gListeningSocket)
{
// create a context for this connection and open a new socket for the
// next connection
UdpConnect(socket, remoteEndpoint);
connections[socket].connectionNumber = ++gConnectionCount;
gListeningSocket = UdpOpen( IP_Endpoint(0.0.0.0:40002) );
UdpReceiveFrom(gListeningSocket, gBuffer, elcount(gBuffer));
}
AnswerRequest(socket, buffer, size);
}
AnswerRequest(dword socket, char buffer[], dword size)
{
char response[100];
if(strncmp(buffer, "Request", size) == 0)
{
snprintf(response, elcount(response), "Response for connection #%d", connections[socket].connectionNumber);
UdpSend(socket, response, strlen(response));
UdpReceiveFrom(socket, gBuffer, elcount(gBuffer));
}
else if(strncmp(buffer, "End", size) == 0)
{
connections.remove(socket);
UdpClose(socket);
}
}
- UdpSend 函数
主要是在连接的 UDP socket 上发送数据。 调用这个函数前需要先用 UdpConnect 连接socket。具体的使用结构如下:
函数的返回值的情况如下:
0: 函数执行成功
WSA_INVALID_PARAMETER (87): 指定的 socket 无效
SOCKET_ERROR (-1): 函数执行失败。错误内容使用函数 IpGetLastError 查询
示例
variables
{
UdpSocket gSocket;
}
on start
{
gSocket = UdpSocket::Open( IP_Endpoint(0.0.0.0:0) );
gSocket.Connect(ip_Endpoint(192.168.1.3:40002));
gSocket.Send( "Request", 7 );
}
- UdpReceiveFrom 函数
主要是接收数据到指定的缓冲区。 如果接收操作没有立即完成,操作将异步执行,函数将返回 SOCKET_ERROR (-1)。 使用 IpGetLastSocketError 获取更具体的错误代码。 如果特定的错误代码是 WSA_IO_PENDING (997),则 CAPL 回调 OnUdpReceiveFrom 将在完成时调用(成功与否),前提是它在同一个 CAPL 程序中实现。具体的使用范例请在文章末尾查看。具体的使用结构如下:
函数的返回值的情况如下:
0: 函数执行成功
WSA_INVALID_PARAMETER (87): 指定的 socket 无效
SOCKET_ERROR (-1): 函数执行失败。错误内容使用函数 IpGetLastError 查询
- UdpSendTo 函数
主要是将数据发送到指定位置。 如果发送操作没有立即完成,操作将异步执行,函数将返回 SOCKET_ERROR (-1)。 使用 IpGetLastSocketError 获取更具体的错误代码。 如果具体的错误代码是 WSA_IO_PENDING (997),并且在完成(成功与否)时会调用 CAPL 回调 OnUdpSendTo,前提是它是在同一个 CAPL 程序中实现的。如果操作已被同步处理,则不会调用 CAPL 回调 OnUdpSendTo。具体的使用格式如下:
函数的返回值的情况如下:
0: 函数执行成功
WSA_INVALID_PARAMETER (87): 指定的 socket 无效
SOCKET_ERROR (-1): 函数执行失败。错误内容使用函数 IpGetLastError 查询
示例
variables
{
UdpSocket gSocket;
char gRxBuffer[1000];
}
on start
{
gSocket = UdpSocket::Open( IP_Endpoint(0.0.0.0:40001) );
gSocket.SendTo( IP_Endpoint(192.168.0.2:40002), "Hello", 5 );
gSocket.ReceiveFrom( gRxBuffer, elcount( gRxBuffer) );
}
OnUdpReceiveFrom( UdpSocket socket, long result, IP_Endpoint remoteEndpoint, char buffer[], dword size)
{
if (result == 0)
{
write( "Received: %s", buffer );
gSocket.ReceiveFrom( gRxBuffer, elcount( gRxBuffer) );
}
}
- OnUdpReceiveFrom 函数
主要是当 UDP socket 上的异步接收操作完成时调用。堆栈包含一个数据队列,一旦数据位于该队列中,该队列就会被 UdpReceiveFrom 减少。因此,为了将来从数据队列中接收额外的数据用于 socket,必须在回调中再次调用 UdpReceiveFrom。具体的使用格式如下:
示例
variables
{
UdpSocket gSocket;
char gRxBuffer[1000];
}
on start
{
gSocket = UdpSocket::Open( IP_Endpoint(0.0.0.0:40001) );
gSocket.SendTo( IP_Endpoint(192.168.0.2:40002), "Hello", 5 );
gSocket.ReceiveFrom( gRxBuffer, elcount( gRxBuffer) );
}
OnUdpReceiveFrom( UdpSocket socket, long result, IP_Endpoint remoteEndpoint, char buffer[], dword size)
{
if (result == 0)
{
write( "Received: %s", buffer );
gSocket.ReceiveFrom( gRxBuffer, elcount( gRxBuffer) );
}
}
- OnUdpSendTo 函数
主要是当 UDP 套接字上的异步发送操作完成时调用。具体的使用格式如下
示例
// ---------------------------------------------------
// node global variables.
// ---------------------------------------------------
variables
{
const int gMTU = 1500; // tcp mtu
const int gSERVER_REPLY_WELCOME = 1; // server reply for welcome.
const int gSERVER_REPLY_ANSWER = 2; // server reply for answer.
const dword gIPV4_STR_SIZE = 16; // IPv4 string size
const dword gINVALID_SOCKET = ~0; // invalid socket constant
dword gListenPort = 12345; // port to send udp to.
dword gListenSocket = gINVALID_SOCKET; // server side: server listen socket
dword gServerClientSocket = gINVALID_SOCKET; // server side: a client's socket
char gServerTcpBuffer[gMTU]; // tcp receive buffer of server
}
// ---------------------------------------------------
// Interaction: Server disconnects client...
// ---------------------------------------------------
on key 'x'
{
serverDisconnectClient();
}
// ---------------------------------------------------
// on measurement start. (initialize and start demo)
// ---------------------------------------------------
on start
{
serverStart();
}
// ---------------------------------------------------
// before measurement stops.
// ---------------------------------------------------
on preStop
{
// close server ( this implies closing all server and client sockets. )
serverClose();
}
// ---------------------------------------------------
// Callback when client connects to server's listen socket.
// ---------------------------------------------------
void OnTcpListen( dword socket, long result)
{
dword errCode;
writeLineEx(1, 1, " [ S: OnTcpListen called. (result: %d)]", result);
if (gServerClientSocket != gINVALID_SOCKET)
{
writeLineEx(1, 2, "S: A client is already connected. Not accepting.");
return;
}
if (result == 0)
{
if (socket == gListenSocket)
{
write("S: Incoming client.", socket);
gServerClientSocket = TcpAccept( gListenSocket );
if (gServerClientSocket == gINVALID_SOCKET)
{
errCode = IpGetLastSocketError(gListenSocket);
if (errCode == 87)
{
writeLineEx( 1, 3, "S: TcpAccept: Invalid listen socket given.");
// handle error...
}
else
{
writeLineEx( 1, 3, "S: TcpAccept: Socket error: %d", IpGetLastSocketError(gListenSocket) );
// handle error...
}
}
else
{
write("S: Accepted client on socket %d to socket %d.", gListenSocket, gServerClientSocket);
// start to receive data on the server's client socket...
startReceive(gServerClientSocket, gServerTcpBuffer);
// do more stuff on server to initialize the incoming accepted client...
// welcome client.
serverSend(gSERVER_REPLY_WELCOME, gServerClientSocket);
}
}
else
{
writeLineEx( 1, 3, "S: OnTcpListen: Unexpected connection on socket %d. (result:%d)", socket, result);
// handle error...
}
}
}
// ---------------------------------------------------
// When asynchronous TcpSend completes...
// ---------------------------------------------------
void OnTcpSend( dword socket, long result, char buffer[], dword size)
{
writeLineEx(1, 1, " [ S: OnTcpSend called. (result: %d)]", result);
if (result == 0)
{
if (socket != gINVALID_SOCKET)
{
if (socket == gServerClientSocket)
{
write("S: Server sent %d bytes to client done. (socket %d, result: %d)", size, socket, result);
}
}
}
}
// ---------------------------------------------------
// When receiving data on socket...
// ---------------------------------------------------
void OnTcpReceive( dword socket, long result, dword address, dword port, char buffer[], dword size)
{
writeLineEx(1, 1, " [ S: OnTcpReceive called. (result: %d)]", result);
if (result == 0)
{
if (socket == gServerClientSocket)
{
// server receives from client...
write("S: Server received %d bytes from client: %s (result: %d)", size, buffer, result);
// check client's request...
if (strstr(buffer, "REQUEST") >= 0)
{
write("S: Client request OK, sending answer.");
serverSend(gSERVER_REPLY_ANSWER, gServerClientSocket); // answer
}
// continue receiving data.
startReceive(gServerClientSocket, gServerTcpBuffer);
}
else if (socket != gINVALID_SOCKET)
{
writeLineEx(1, 3, " [ S: UNIMPLEMENTED: Received %d bytes on socket %d from 0x%x:%d with data: %s (result: %d) ]", size, socket, address, port, buffer, result);
}
}
}
// ---------------------------------------------------
// TCP socket receives a close notification
// (remote closed)
// ---------------------------------------------------
void OnTcpClose( dword socket, long result)
{
if (socket == gServerClientSocket)
{
TcpClose(gServerClientSocket);
gServerClientSocket = gINVALID_SOCKET;
writeLineEx(1, 1, " [ S: OnTcpClose called. (socket: %d, result: %d) ]", socket, result);
}
}
// ---------------------------------------------------
// server disconnects client
// ---------------------------------------------------
void serverDisconnectClient()
{
if (gServerClientSocket != gINVALID_SOCKET)
{
write("S: Server disconnects client. (socket %d)", gServerClientSocket);
TcpClose(gServerClientSocket);
gServerClientSocket = gINVALID_SOCKET;
}
}
// ---------------------------------------------------
// start receiving on given socket into given buffer.
// ---------------------------------------------------
void startReceive ( dword socket, char buffer[] )
{
long result;
result = TcpReceive( socket, buffer, elcount(buffer) );
if (result == -1)
{
result = IpGetLastSocketError(socket);
if (result != 997) // not asynchronous
{
// failure
writeLineEx( 1, 3, "S: TcpReceive error %d", result);
}
}
else if (result != 0) // synchronous sending failed
{
// failure
writeLineEx( 1, 3, "S: TcpReceive error %d", result);
}
}
// ---------------------------------------------------
// start server
// ---------------------------------------------------
void serverStart()
{
writeLineEx(1, 1, " [ S: Open TCP server socket at port %d... ]", gListenPort);
gListenSocket = TcpOpen(0, gListenPort);
if (gListenSocket == gINVALID_SOCKET)
{
writeLineEx( 1, 3, " [ S: TcpOpen: Error opening TCP Socket on port %d. (Error %d) ]", gListenPort, IpGetLastError() );
// handle error...
}
else
{
if (TcpListen( gListenSocket ) != 0)
{
writeLineEx(1, 3, " [ S: TcpListen: Error listening on socket. ]");
TcpClose(gListenSocket);
gListenSocket = gINVALID_SOCKET;
// handle error...
}
else
{
writeLineEx(1, 1, " [ S: Start listening on server socket %d... ]", gListenSocket);
}
}
}
// ---------------------------------------------------
// close server
// ---------------------------------------------------
void serverClose()
{
if (gListenSocket != gINVALID_SOCKET)
{
writeLineEx(1, 1, " [ Server shutdown. ]");
serverDisconnectClient();
TcpClose(gListenSocket);
gListenSocket = gINVALID_SOCKET;
}
}
// ---------------------------------------------------
// server sends data to client
// ---------------------------------------------------
void serverSend(int replyNr, dword socket)
{
if (socket != gINVALID_SOCKET)
{
writeLineEx(1, 1, "S: Sending data to client. (socket %d)", socket);
switch(replyNr)
{
case gSERVER_REPLY_WELCOME:
{
sendTcpData( socket, " Server data: WELCOME.");
break;
}
case gSERVER_REPLY_ANSWER:
{
sendTcpData(socket, "Server data: ANSWER.");
break;
}
default:
{
writeLineEx(1, 3, " [ serverSend: UNSUPPORTED REPLY NR %d ]", replyNr);
break;
}
}
}
}
// ---------------------------------------------------
// send tcp data.
// ---------------------------------------------------
void sendTcpData( dword socket, char data[] )
{
long result;
dword size;
size = elcount(data);
result = TcpSend(socket, data, size);
if (result == 0)
{
// sending took place immediately.
writeLineEx(1, 1, " [ S: Synchronous sending: '%s' on socket %d ]", data, socket);
OnTcpSend(socket, result, data, size); // trigger callback manually
}
else
{
if (result == -1)
{
result = IpGetLastSocketError(socket);
if (result == 997)
{
// sending is done asynchronously.
writeLineEx(1, 1, " [ S: Asynchronous sending: '%s' on socket %d ]", data, socket);
// => OnTcpSend is called when done sending.
}
else
{
writeLineEx( 1, 3, " [ S: sendTcpData: Error sending data. (%d) ]", result);
}
}
else
{
writeLineEx( 1, 3, " [ S: sendTcpData: Error sending data. (%d) ]", result);
}
}
}