Datagram Operations

Datagrams are connectionless methods of communication. A sender merely addresses each packet with its destination NetBIOS name and sends it on its way. No checking is performed to ensure data integrity, order of arrival, or reliability.

There are three ways to send a datagram. The first is to direct the datagram at a specific (unique or group) name. This means that only one process can receive that datagram—the process that registered the destination name. The second method is to send a datagram to a group name. Only those processes that registered the given group name will be able to receive the message. Finally, the third way to send a datagram is to broadcast it to the entire network. Any process on any workstation on the local area network can receive the datagram. Sending a datagram to either a unique or a group name uses the NCBDGSEND command, while broadcasts use the NCBDGSENDBC command.

Using any of the datagram send commands is a simple process. Set the ncb_num field to the name number returned from an NCBADDNAME command or an NCBADDGRNAME command. This is the number that identifies the message sender. Set ncb_buffer to the address of the buffer with the data you need to send, and set ncb_length to the number of bytes to send. Next, set the ncb_lana_num field to the LANA over which you want the datagram to be transmitted. The last step is to set ncb_callname to the name of the destination NetBIOS name. This can be a unique name or a group name. If you are sending broadcast datagrams, you perform all of the above steps except for the last one: since all workstations will receive the message, no ncb_callname is required.

Of course, in each of the sender scenarios mentioned above, there must be a corresponding datagram receive command to actually receive the data. Datagrams are connectionless; if a datagram reaches a client but the client does not have a receive command already pending, the data is lost and there is no way for the client to recover that data (unless the server resends the data). That is the disadvantage of sending datagrams. However, datagrams are much faster than connection-based methods because they don't require the overhead of error checking, connection setup, and so on.

There are also three methods for receiving datagrams. The first two receive types use the NCBDGRECV command. First, you can issue a datagram receive command for messages destined for a specific name—unique or group. Second, you can issue a datagram receive command for any datagram destined for any name in the process's NetBIOS name table. Third, you can issue a receive command for a broadcast datagram with the NCBDGRECVBC command.

NOTE
It is not possible to post a receive command for datagrams destined for a name registered by a different process unless both processes have registered a group name, in which case both processes can receive the same message.

To issue a receive command, set the ncb_num field to the name number returned from a successful NCBADDNAME or NCBADDGRNAME call. This name number specifies which name you are listening on for incoming datagrams. If you set this field to 0xFF, you will receive datagrams destined for any name in this process's NetBIOS name table. Additionally, create a buffer to receive data and set ncb_buffer to the buffer's address. Set ncb_length to the size of the buffer. Finally, set ncb_lana_num to the LANA number on which to wait for datagrams. When a successful call to Netbios with the NCBDGRECV command (or the NCBDGRECVBC command) returns, ncb_length will contain the actual number of bytes received and ncb_callname will contain the NetBIOS name of the sending process.

The code in Figure 1-7 contains basic datagram functions. All the sends are blocking calls—once the command is issued and the data is on the wire, the function returns, and you shouldn't experience blocking because of data overload. The receive calls are asynchronous events because we do not know what LANA numbers data will come in on. The code is similar to that for the session-oriented server using events. For each LANA, the code posts an asynchronous NCBDGRECV (or NCBDGRECVBC) and waits until one succeeds, at which point it checks all posted commands, prints the messages for those that succeed, and cancels those commands that are still pending. The example provides functions for both directed and broadcast sends and receives. The program can be compiled into a sample application that can be configured to send or receive datagrams. The program accepts several command line parameters that allow the user to specify the number of datagrams to send or receive, the delay between sends, the use of broadcasts instead of directed datagrams, the receipt of datagrams for any name, and so on.

Figure 1-7. NetBIOS datagram sample (Nbdgram.c)

 // Nbdgram.c #include <windows.h> #include <stdio.h> #include <stdlib.h> #include "..\Common\nbcommon.h" #define MAX_SESSIONS 254 #define MAX_NAMES 254 #define MAX_DATAGRAM_SIZE 512 BOOL bSender = FALSE, // Send or receive datagrams bRecvAny = FALSE, // Receive for any name bUniqueName = TRUE, // Register my name as unique? bBroadcast = FALSE, // Use broadcast datagrams? bOneLana = FALSE; // Use all LANAs or just one? char szLocalName[NCBNAMSZ + 1], // Local NetBIOS name szRecipientName[NCBNAMSZ + 1]; // Recipient's NetBIOS name DWORD dwNumDatagrams = 25, // Number of datagrams to send dwOneLana, // If using one LANA, which one? dwDelay = 0; // Delay between datagram sends // // Function: ValidateArgs // // Description: // This function parses the command line arguments // and sets various global flags indicating the selections // void ValidateArgs(int argc, char **argv) { int i; for(i = 1; i < argc; i++) { if (strlen(argv[i]) < 2) continue; if ((argv[i][0] == '-') || (argv[i][0] == '/')) { switch (tolower(argv[i][1])) { case 'n': // Use a unique name bUniqueName = TRUE; if (strlen(argv[i]) > 2) strcpy(szLocalName, &argv[i][3]); break; case 'g': // Use a group name bUniqueName = FALSE; if (strlen(argv[i]) > 2) strcpy(szLocalName, &argv[i][3]); break; case 's': // Send datagrams bSender = TRUE; break; case 'c': // # of datagrams to send or receive if (strlen(argv[i]) > 2) dwNumDatagrams = atoi(&argv[i][3]); break; case 'r': // Recipient's name for datagrams if (strlen(argv[i]) > 2) strcpy(szRecipientName, &argv[i][3]); break; case 'b': // Use broadcast datagrams bBroadcast = TRUE; break; case 'a': // Receive datagrams on any name bRecvAny = TRUE; break; case 'l': // Operate on this LANA only bOneLana = TRUE; if (strlen(argv[i]) > 2) dwOneLana = atoi(&argv[i][3]); break; case 'd': // Delay (millisecs) between sends if (strlen(argv[i]) > 2) dwDelay = atoi(&argv[i][3]); break; default: printf("usage: nbdgram ?\n"); break; } } } return; } // // Function: DatagramSend // // Description: // Send a directed datagram to the specified recipient on the // specified LANA number from the given name number to the // specified recipient. Also specified is the data buffer and // the number of bytes to send. // int DatagramSend(int lana, int num, char *recipient, char *buffer, int buflen) { NCB ncb; ZeroMemory(&ncb, sizeof(NCB)); ncb.ncb_command = NCBDGSEND; ncb.ncb_lana_num = lana; ncb.ncb_num = num; ncb.ncb_buffer = (PUCHAR)buffer; ncb.ncb_length = buflen; memset(ncb.ncb_callname, ' ', NCBNAMSZ); strncpy(ncb.ncb_callname, recipient, strlen(recipient)); if (Netbios(&ncb) != NRC_GOODRET) { printf("Netbios: NCBDGSEND failed: %d\n", ncb.ncb_retcode); return ncb.ncb_retcode; } return NRC_GOODRET; } // // Function: DatagramSendBC // // Description: // Send a broadcast datagram on the specified LANA number from the // given name number. Also specified is the data buffer and the number // of bytes to send. // int DatagramSendBC(int lana, int num, char *buffer, int buflen) { NCB ncb; ZeroMemory(&ncb, sizeof(NCB)); ncb.ncb_command = NCBDGSENDBC; ncb.ncb_lana_num = lana; ncb.ncb_num = num; ncb.ncb_buffer = (PUCHAR)buffer; ncb.ncb_length = buflen; if (Netbios(&ncb) != NRC_GOODRET) { printf("Netbios: NCBDGSENDBC failed: %d\n", ncb.ncb_retcode); return ncb.ncb_retcode; } return NRC_GOODRET; } // // Function: DatagramRecv // // Description: // Receive a datagram on the given LANA number directed toward the // name represented by num. Data is copied into the supplied buffer. // If hEvent is not 0, the receive call is made asynchronously // with the supplied event handle. If num is 0xFF, listen for a // datagram destined for any NetBIOS name registered by the process. // int DatagramRecv(PNCB pncb, int lana, int num, char *buffer, int buflen, HANDLE hEvent) { ZeroMemory(pncb, sizeof(NCB)); if (hEvent) { pncb->ncb_command = NCBDGRECV | ASYNCH; pncb->ncb_event = hEvent; } else pncb->ncb_command = NCBDGRECV; pncb->ncb_lana_num = lana; pncb->ncb_num = num; pncb->ncb_buffer = (PUCHAR)buffer; pncb->ncb_length = buflen; if (Netbios(pncb) != NRC_GOODRET) { printf("Netbos: NCBDGRECV failed: %d\n", pncb->ncb_retcode); return pncb->ncb_retcode; } return NRC_GOODRET; } // // Function: DatagramRecvBC // // Description: // Receive a broadcast datagram on the given LANA number. // Data is copied into the supplied buffer. If hEvent is not 0, // the receive call is made asynchronously with the supplied // event handle. // int DatagramRecvBC(PNCB pncb, int lana, int num, char *buffer, int buflen, HANDLE hEvent) { ZeroMemory(pncb, sizeof(NCB)); if (hEvent) { pncb->ncb_command = NCBDGRECVBC | ASYNCH; pncb->ncb_event = hEvent; } else pncb->ncb_command = NCBDGRECVBC; pncb->ncb_lana_num = lana; pncb->ncb_num = num; pncb->ncb_buffer = (PUCHAR)buffer; pncb->ncb_length = buflen; if (Netbios(pncb) != NRC_GOODRET) { printf("Netbios: NCBDGRECVBC failed: %d\n", pncb->ncb_retcode); return pncb->ncb_retcode; } return NRC_GOODRET; } // // Function: main // // Description: // Initialize the NetBIOS interface, allocate resources, and then // send or receive datagrams according to the user's options // int main(int argc, char **argv) { LANA_ENUM lenum; int i, j; char szMessage[MAX_DATAGRAM_SIZE], szSender[NCBNAMSZ + 1]; DWORD *dwNum = NULL, dwBytesRead, dwErr; ValidateArgs(argc, argv); // // Enumerate and reset the LANA numbers // if ((dwErr = LanaEnum(&lenum)) != NRC_GOODRET) { printf("LanaEnum failed: %d\n", dwErr); return 1; } if ((dwErr = ResetAll(&lenum, (UCHAR)MAX_SESSIONS, (UCHAR)MAX_NAMES, FALSE)) != NRC_GOODRET) { printf("ResetAll failed: %d\n", dwErr); return 1; } // // This buffer holds the name number for the NetBIOS name added // to each LANA // dwNum = (DWORD *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(DWORD) * lenum.length); if (dwNum == NULL) { printf("out of memory\n"); return 1; } // // If we're going to operate on only one LANA, register the name // on only that specified LANA; otherwise, register it on all // LANAs // if (bOneLana) { if (bUniqueName) AddName(dwOneLana, szLocalName, &dwNum[0]); else AddGroupName(dwOneLana, szLocalName, &dwNum[0]); } else { for(i = 0; i < lenum.length; i++) { if (bUniqueName) AddName(lenum.lana[i], szLocalName, &dwNum[i]); else AddGroupName(lenum.lana[i], szLocalName, &dwNum[i]); } } // We are sending datagrams // if (bSender) { // Broadcast sender // if (bBroadcast) { if (bOneLana) { // Broadcast the message on the one LANA only // for(j = 0; j < dwNumDatagrams; j++) { wsprintf(szMessage, "[%03d] Test broadcast datagram", j); if (DatagramSendBC(dwOneLana, dwNum[0], szMessage, strlen(szMessage)) != NRC_GOODRET) return 1; Sleep(dwDelay); } } else { // Broadcast the message on every LANA on the local // machine // for(j = 0; j < dwNumDatagrams; j++) { for(i = 0; i < lenum.length; i++) { wsprintf(szMessage, "[%03d] Test broadcast datagram", j); if (DatagramSendBC(lenum.lana[i], dwNum[i], szMessage, strlen(szMessage)) != NRC_GOODRET) return 1; } Sleep(dwDelay); } } } else { if (bOneLana) { // Send a directed message to the one LANA specified // for(j = 0; j < dwNumDatagrams; j++) { wsprintf(szMessage, "[%03d] Test directed datagram", j); if (DatagramSend(dwOneLana, dwNum[0], szRecipientName, szMessage, strlen(szMessage)) != NRC_GOODRET) return 1; Sleep(dwDelay); } } else { // Send a directed message to each LANA on the // local machine // for(j = 0; j < dwNumDatagrams; j++) { for(i = 0; i < lenum.length; i++) { wsprintf(szMessage, "[%03d] Test directed datagram", j); printf("count: %d.%d\n", j,i); if (DatagramSend(lenum.lana[i], dwNum[i], szRecipientName, szMessage, strlen(szMessage)) != NRC_GOODRET) return 1; } Sleep(dwDelay); } } } } else // We are receiving datagrams { NCB *ncb=NULL; char **szMessageArray = NULL; HANDLE *hEvent=NULL; DWORD dwRet; // Allocate an array of NCB structure to submit to each recv // on each LANA // ncb = (NCB *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(NCB) * lenum.length); // // Allocate an array of incoming data buffers // szMessageArray = (char **)GlobalAlloc(GMEM_FIXED, sizeof(char *) * lenum.length); for(i = 0; i < lenum.length; i++) szMessageArray[i] = (char *)GlobalAlloc(GMEM_FIXED, MAX_DATAGRAM_SIZE); // // Allocate an array of event handles for // asynchronous receives // hEvent = (HANDLE *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(HANDLE) * lenum.length); for(i = 0; i < lenum.length; i++) hEvent[i] = CreateEvent(0, TRUE, FALSE, 0); if (bBroadcast) { if (bOneLana) { // Post synchronous broadcast receives on // the one LANA specified // for(j = 0; j < dwNumDatagrams; j++) { if (DatagramRecvBC(&ncb[0], dwOneLana, dwNum[0], szMessageArray[0], MAX_DATAGRAM_SIZE, NULL) != NRC_GOODRET) return 1; FormatNetbiosName(ncb[0].ncb_callname, szSender); printf("%03d [LANA %d] Message: '%s' " "received from: %s\n", j, ncb[0].ncb_lana_num, szMessageArray[0], szSender); } } else { // Post asynchronous broadcast receives on each LANA // number available. For each command that succeeded, // print the message; otherwise, cancel the command. // for(j = 0; j < dwNumDatagrams; j++) { for(i = 0; i < lenum.length; i++) { dwBytesRead = MAX_DATAGRAM_SIZE; if (DatagramRecvBC(&ncb[i], lenum.lana[i], dwNum[i], szMessageArray[i], MAX_DATAGRAM_SIZE, hEvent[i]) != NRC_GOODRET) return 1; } dwRet = WaitForMultipleObjects(lenum.length, hEvent, FALSE, INFINITE); if (dwRet == WAIT_FAILED) { printf("WaitForMultipleObjects failed: %d\n", GetLastError()); return 1; } for(i = 0; i < lenum.length; i++) { if (ncb[i].ncb_cmd_cplt == NRC_PENDING) Cancel(&ncb[i]); else { ncb[i].ncb_buffer[ncb[i].ncb_length] = 0; FormatNetbiosName(ncb[i].ncb_callname, szSender); printf("%03d [LANA %d] Message: '%s' " "received from: %s\n", j, ncb[i].ncb_lana_num, szMessageArray[i], szSender); } ResetEvent(hEvent[i]); } } } } else { if (bOneLana) { // Make a blocking datagram receive on the specified // LANA number // for(j = 0; j < dwNumDatagrams; j++) { if (bRecvAny) { // Receive data destined for any NetBIOS name // in this process's name table // if (DatagramRecv(&ncb[0], dwOneLana, 0xFF, szMessageArray[0], MAX_DATAGRAM_SIZE, NULL) != NRC_GOODRET) return 1; } else { if (DatagramRecv(&ncb[0], dwOneLana, dwNum[0], szMessageArray[0], MAX_DATAGRAM_SIZE, NULL) != NRC_GOODRET) return 1; } FormatNetbiosName(ncb[0].ncb_callname, szSender); printf("%03d [LANA %d] Message: '%s' " "received from: %s\n", j, ncb[0].ncb_lana_num, szMessageArray[0], szSender); } } else { // Post asynchronous datagram receives on each LANA // available. For all those commands that succeeded, // print the data; otherwise, cancel the command. // for(j = 0; j < dwNumDatagrams; j++) { for(i = 0; i < lenum.length; i++) { if (bRecvAny) { // Receive data destined for any NetBIOS // name in this process's name table // if (DatagramRecv(&ncb[i], lenum.lana[i], 0xFF, szMessageArray[i], MAX_DATAGRAM_SIZE, hEvent[i]) != NRC_GOODRET) return 1; } else { if (DatagramRecv(&ncb[i], lenum.lana[i], dwNum[i], szMessageArray[i], MAX_DATAGRAM_SIZE, hEvent[i]) != NRC_GOODRET) return 1; } } dwRet = WaitForMultipleObjects(lenum.length, hEvent, FALSE, INFINITE); if (dwRet == WAIT_FAILED) { printf("WaitForMultipleObjects failed: %d\n", GetLastError()); return 1; } for(i = 0; i < lenum.length; i++) { if (ncb[i].ncb_cmd_cplt == NRC_PENDING) Cancel(&ncb[i]); else { ncb[i].ncb_buffer[ncb[i].ncb_length] = 0; FormatNetbiosName(ncb[i].ncb_callname, szSender); printf("%03d [LANA %d] Message: '%s' " "from: %s\n", j, ncb[i].ncb_lana_num, szMessageArray[i], szSender); } ResetEvent(hEvent[i]); } } } } // Clean up // for(i = 0; i < lenum.length; i++) { CloseHandle(hEvent[i]); GlobalFree(szMessageArray[i]); } GlobalFree(hEvent); GlobalFree(szMessageArray); } // Clean things up // if (bOneLana) DelName(dwOneLana, szLocalName); else { for(i = 0; i < lenum.length; i++) DelName(lenum.lana[i], szLocalName); } GlobalFree(dwNum); return 0; } 

Once you've compiled the example, run the following tests to get an idea of how datagrams work. For learning purposes, you should run two instances of the applications, but on separate machines. If you run them on the same machine, they'll work, but this hides some important concepts. When run on the same machine, the LANA numbers for each side correspond to the same protocol. It's more interesting when they don't. Table 1-5 lists some commands to try. Additionally, Table 1-6 lists all the command line options available for use with the sample program.

Table 1-5. Nbdgram.c commands

Client Command Server Command
Nbdgram /n:CLIENT01 Nbdgram /s /n:SERVER01 /r:CLIENT01
Nbdgram /n:CLIENT01 /b Nbdgram /s /n:SERVER01 /b
Nbdgram /g:CLIENTGROUP Nbdgram /s /r:CLIENTGROUP

For the third command, run several clients on various machines. This illustrates one server sending one message to a group, and each member of the group waiting for data will receive the message. Also, try various combinations of the listed commands with the /l:x command line option, where x is a valid LANA number. This command line option switches the program's mode from performing the commands on all LANAs to performing the commands on the listed LANA only. For example, the command Nbdgram /n:CLIENT01 /l:0 makes the application listen only for incoming datagrams on LANA 0 and ignore any data arriving on any other LANA. Additionally, option /a is meaningful only to the clients. This flag causes the receive command to pick up incoming datagrams destined for any NetBIOS name registered by the process. In our example, this isn't very meaningful because the client registers only one name, but you can at least see how this would be coded. You might want to try modifying the code so that it registers a name for every /n:name option in the command line. Start up the server with the recipient flag set to only one of the names that the client registered. The client will receive the data, even though the NCBDGRECV command does not specifically refer to a particular name.

Table 1-6. Command parameters for Nbdgram.c

Flag Meaning
/n:my-name Register the unique name my-name.
/g:group-name Register the group name group-name.
/s Send datagrams (by default, the sample receives datagrams).
/c:n Send or receive n number of datagrams.
/r:receiver Specify the NetBIOS name to send the datagrams to.
/b Use broadcast datagrams.
/a Post receives for any NetBIOS name (set ncb_num to 0xFF).
/l:n Perform all operations on LANA n only (by default, all sends and receives are posted on each LANA).
/d:n Wait n milliseconds between sends.



Network Programming for Microsoft Windows
Linux Server Hacks, Volume Two: Tips & Tools for Connecting, Monitoring, and Troubleshooting
ISBN: 735615799
EAN: 2147483647
Year: 1998
Pages: 159

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net