Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

The WinMock Library


July 1996: The WinMock Library

An off-line tool for testing and debugging WinSock apps

Joe works for IBM Software Solutions in Research Triangle Park, NC, although he currently lives in Warsaw, Poland. He can be contacted at [email protected].


Developers of distributed applications face many problems, not the least of which include internetwork errors ("The LAN's down!"), intermittent problems difficult to reproduce for a debugger, and even the lack of network access during development. To tackle these and other problems, I've developed a tool to test and debug Windows Sockets applications without a network connection. I call it "WinMock," or the "Windows Sockets Mockup Tool." The WinMock library is a 16-bit Windows DLL built with Microsoft C 6.0 and the small DLL library.

The WinMock library is not an emulation of Windows Sockets calls. Instead, it is intended to become a fully "Windows Sockets Compliant" interface, albeit without underlying network support. While this library is based on the Windows Sockets 1.1 spec, nothing should preclude testing Windows Sockets 1.0 applications with it. In this article, I'll discuss only those APIs necessary to use WinSock 1.1 applications. Source code for the WinMock library is available electronically. To build changes to the WinMock source, you will need a compatible version of winsock.h. (You may, of course, use the library as-is, without winsock.h.)

The WinSock API

The Windows Sockets API is a network-programming interface based on BSD sockets. It contains a number of BSD routines (socket(), for instance) and extensions that allow you to take advantage of Windows' message-driven architecture. While the Windows Sockets API is meant to be used with the Internet Protocol Suite (TCP/IP), some vendors have implemented Windows Sockets wrappers around other protocol stacks. Since the Windows Sockets API is the same in all cases, WinMock should let you test and debug non-TCP/IP Windows Sockets interface apps, as well.

A socket is a tag for a communication endpoint. Normally, each conversation (connection) would have two such endpoints, one on the client side and the other on the server. WinMock will generate sockets either from the client or the server side, as requested, but makes no attempt to associate any client socket with any existing server socket.

The two types of sockets-stream and datagram-are normally bidirectional. A stream socket is conceptually similar to file I/O: It guarantees that information is delivered in order, that duplicate packets have been removed, and that the information has not been corrupted in transmission.

A datagram socket is less reliable: Information is not guaranteed to be in order, and it may be duplicated. However, for record-oriented data, datagrams guarantee that record boundaries will be preserved if the underlying network supports it (in other words, if the record is not too large for the underlying packet). Datagram sockets can also be used to broadcast packets if the underlying network supports broadcasting (sending a packet to multiple hosts).

Network byte order is Big-endian and involves a particular order of contiguous bytes in memory that represent numbers. Any local number sent to a remote location should be translated to network byte order using the Windows Sockets functions htons() (for short integers) and htonl() (for long integers). All numbers sent across the Internet (port numbers, addresses, and the like) must be in network byte order.

Host byte order is the byte order of the local machine (which may or may not match that of the network). Numbers received from the Internet must be converted using ntohs() (for short integers) or ntohl() (for long integers). Machines with Intel processors are Little-endian, but in the interests of portability, you should always convert with ntohs() and ntohl(). As an added complication, certain processors (the PowerPC, for instance) can operate in either endian mode.

Blocking calls do not return to the caller until the underlying function has completed (for example, a recv() command, which may need to wait until data are sent by the remote system). Calls don't block just because they can. For example, a recv() call will return immediately if data are waiting on the socket. There is a subset of calls like htons() that, by their nature, don't block (they involve no remote requests).

Modifying WinSock API Return Codes

The WinMock DLL can give explicit success or failure return codes for supported WinSock APIs. The user can create a WinMock.ini file with a section for each WinSock API; see Listing Two. The contents of each section are API specific, but most sections can include an RC key of the form RC=<value>. Acceptable values are those found in the WinMock ErrorList[] data structure. The wmConvertErrorString() function translates the given value (assumed to be a text string, for example, "ZERO") to its integer equivalent (in this example, the integer 0).

Certain WinMock APIs that return structures can read data values for certain fields of these structures from the WinMock.ini file. For example, you can return a wVersion number of 0x101 (257 decimal) from WSAStartup() by placing wVersion=257 in the [wsastartup] section of your WinMock.ini file.

The [lasterror] Section

To return the API's default error-return code (specific to the particular WinMock API) and to set a specific last-error value, include in your WinMock.ini file a [lasterror] section that contains the key <apiname>=<value>. This section is used to set the mylasterror field of the task data structure. A Windows Sockets application would retrieve the error by calling the Windows Sockets function WSAGetLastError().

For example, you can fail all recv() calls made by any task with a SOCKET_ERROR RC and a lasterror value of WSAECONNABORTED by placing recv=WSAECONNABORTED in the [lasterror] section. This is an easy way of checking how well a Windows Sockets application responds to errors.

WinMock APIs first check for a [lasterror] key for the given API. If a key is found, then the task's mylasterror value is set to the key's value, and the function returns the default error-return code without checking for an RC key in the API's section. If no [lasterror] key is found for the given API, WinMock will check for the RC key, if applicable (see the sample WinMock.ini file in Listing One.).

Since the WinMock library is a work in progress, not all APIs currently support these return-code .ini file entries. All APIs will soon support the [lasterror] section and may be assumed to support the RC key unless the documentation specifically prohibits it. The RC key, for example, is prohibited in the case of the database functions such as GetHostByName(), where the return value is an address of a structure and not a numeric value.

Base Data Structures for WinMock

The WinMock task list is a simple array of MYTASK structures. The number of structures in the array is decided at compile time by the MAXTASKS #define in winmock.h. The wmInitializeTaskList() routine is responsible for verifying that all task list elements have a known (and available) state. It could easily be modified to use a singly linked list and a more flexible task-list size. The fields for a MYTASK structure are currently defined as: mytask, the Windows task handle for the application in question; myhostent, the host-entry structure used for this application (or NULL, if none has been allocated); myprotoent, the protocol-entry structure (or NULL); myservent, the service-entry structure (or NULL) used for this application; isblocking, a flag noting whether a blocking call is currently being made for this task; mylasterror, the task-level error code for this application; and WSAStartupCount, the number of times this task has called the WSAStartup() function that initializes the task to the WinMock library. This count can be greater than 1 because the Windows Sockets API specifically permits multiple calls to WSAStartup().

All structure memory handles (myhostent, myprotoent, and the like) are initialized to NULL. These memory blocks are only allocated if the application requests them by calling a function that returns a HOSTENT, PROTOENT, or SERVENT structure. These blocks of memory are allocated on a one-per-task basis, and this structure will be reused if the application again calls a function requiring this block. If the value is non-NULL, a memory block has been allocated. All blocks (if any) will be freed during final WSACleanup() processing for the application.

The isblocking flag is not used in this WinMock version. It notes whether or not the given task has begun a "blocking" operation. A Windows Sockets-compliant interface must not begin a second operation until the first has completed. The default value is False (not blocking).

The mylasterror field contains the value to be returned by WSAGetLastError(). It is kept on a per-task basis. Any value set should either be 0 (no error) or one of the WSA error codes defined in winsock.h. The WinSock specification defines exactly which error codes may occur for each Windows Sockets API. The default value is 0.

WSAStartupCount is the number of times that this application (or, possibly, a DLL for this application) has called WSAStartup(). If this count is not at least 1, the application will not be on our task list (and Windows Sockets API calls will fail). If this count falls to 0, any resources allocated for the application by the WinMock library are freed.

The key used to identify an application on the task list is the mytask field, which contains the HTASK returned by the GetCurrentTask() API. This value distinguishes any single currently running Windows task from any other.

The Socket List

The socket list is also a simple array of MYSOCKET structures. The number of structures is decided at compile time by the MAXSOCKETS #define in winmock.h. The wmInitializeSocketList() routine could also be modified to use a more flexible allocation strategy. The fields for a MYSOCKET structure are currently defined as: mytask, the task owning this socket; isconnected, a flag that marks whether or not this socket is currently part of a conversation; isstillreceiving, which marks whether or not this application has receive data waiting for it; socketblocking, which marks whether or not this socket is blockable; socketfamily, this socket's address family (as defined by Windows Sockets); socketprotocol, the application's protocol family (as defined by Windows Sockets); and sockettype, the type of socket (stream or datagram).

MYSOCKET has four additional fields: hRecv, the global-memory handle (or NULL) for the task-receive buffer; wRecvSize, the size of the information received; wReceiveOffset, the offset in the received block that the user has not yet read (initially, 0); and hSend, the global-memory handle for sent text.

The key used to distinguish a socket on the socket list is the mys field-a number generated by WinMock to distinguish any one socket from all those currently using the WinMock library. A socket mys field may be of any value other than SOCKET_ERROR. The value in the mys field is actually the index of the MYSOCKET structure in the socket list, if it is in use; otherwise the mys field will contain SOCKET_ERROR.

Base Functions for WinMock

Eventually, WinMock will support all Windows Sockets 1.1 APIs.

The current version supports a limited subset; see winmins.c (available electronically). This file contains mostly BSD sockets-like routines used by the World Wide Web browsers I've used as test "data."

The WinMock DLL begins execution in the main() procedure, an assembly-language stub (found in dllentry.asm) that does heap initialization and calls the LibMain() routine.

A General Note About WinMock Functions

Unless otherwise noted, two conditions will cause all WinMock APIs to fail with the error code specified as the API error-return code (with no further processing):

  • The current task has not called WSAStartup (that is, the current task is not on our task list).
  • The socket given as an argument is invalid (the socket was not found on our socket list), or the socket given is not owned by the calling task (current task not equal to socket's task).

Keep both conditions in mind when reading the WinMock API descriptions that follow. For brevity, these rules are not repeated in the individual functional descriptions. The first condition does not apply during WSAStartup() processing, of course, nor for resource-free API calls (such as htons()). The second condition applies only to those APIs that take a socket as an input parameter.

LibMain

The LibMain routine initializes the socket and task arrays via calls to wmInitializeSocketList() and wmInitializeTaskList(); see Listing One. Both routines simply initialize their respective lists to a known, neutral state. Following are WinMock's function prototypes (see Table 2).

  • closesocket(). This API is used to free the socket structure so that it will be available to any requesting application. In WinMock, this means that the socket structure is cleared to neutral values and marked as available. This is done through a call to wmClearSocketStruct(), which takes a pointer to the socket structure to be cleared.
  • The [closesocket] section of winmock.ini may contain a line of the format rc=<error string>. The default value is rc=ZERO. The WinMock API generates a hard close, and the socket is immediately available for reuse. The default error-return code for this procedure is SOCKET_ERROR.
  • connect(). Used to create a network connection. The [connect] section of winmock.ini may contain a line of the format rc=<error string>. The default value is rc=ZERO. The default error-return code is SOCKET_ERROR. The function may fail under the conditions given previously or if the given socket is already connected (TRUE == isconnected). This routine will set isconnected for the given socket to True, if successful.
  • gethostbyname(). Used to find out host-name information. The [gethostbyname] section of winmock.ini may contain lines of the formats h_addrtype=<type number>, for which the default value is h_addrtype=2 (PF_INET); h_length=<address length>, for which the default value is h_length=4; and h_addr_list=<address list entry>, for which the default value is h_addr_list="11.22.33.44".
This implementation of gethostbyname() can return only one address-list entry. The h_name value cannot be changed by the user-it is copied from the hostname parameter given by the caller.

The default error-return code is NULL. Because this function returns an address, any rc= line in the [gethostbyname] section will be ignored. The user may not modify the return value other than through a [lasterror] key for this function. An online WinSock implementation usually resolves the host information using a name server. If a name cannot be resolved, the function may fail. The WinMock implementation of this function, however, cannot normally fail.

  • gethostname(). Returns the host name for the local host. The [gethostname] section of winmock.ini may contain a line of the format hostname=<hostname>. The default value is hostname=myhostname. The [gethostname] section of winmock.ini also may contain a line of the format rc=<error string>. The default value is rc=ZERO. The default error-return code is SOCKET_ERROR. The WinMock version of this function does not normally fail.
  • getprotobyname(). Used to find out protocol information. The [getprotobyname] section of winmock.ini may contain lines of the format p_proto=<protocolnumber>, where the default is p_proto=6 (IPPROTO_TCP). The default error-return code is NULL. Because this function returns an address, any rc= line in the [getprotobyname] section will be ignored. The user may not modify the return value other than through a [lasterror] key. An online WinSock implementation would usually resolve the protocol information using a protocol database and may fail if the protocol name cannot be found there. The WinMock implementation cannot normally fail.
  • getservbyname(). Used to find out service information. The [getservbyname] section of winmock.ini may contain lines of the format s_proto=<protocol used by service> (default is http) or s_port=<port used by service> (default is 0x8000).
The s_name value cannot be changed by the user; it is copied from the service-name parameter given by the caller. The default error-return code is NULL. Because this function returns an address, any rc= line in the [getservbyname] section will be ignored. The user may not modify the return value other than through a [lasterror] key. An online WinSock implementation would usually resolve the service information using a service database, and may fail if the given service name cannot be found in the database. The WinMock implementation of this function cannot normally fail. (The s_aliases field is ignored in this version of the WinMock library.)

recv(). Reads data from the connected socket and is one of the most complicated commands in the WinMock library. It uses the socket flag isstillreceiving to see if data are available for the socket in question. A number of variables in the socket structure keep track of the data available on the socket: If it is not NULL, the hRecv variable contains a handle to a global-memory block that contains the data received on the socket. The wRecvSize variable contains the size of the data (in bytes), and the wRecvOffset variable contains the offset of the first byte in the hRecv buffer that the caller has not read yet. (If the caller has read no data on this socket, wRecvOffset will be 0 or a near pointer to the first byte in the buffer.)

After verifying that there are data to be returned to the caller on the socket, the equation: wLeftToRecv=pthissocket->wRecvSize-pthissocket->wRecvOffset calculates the number of bytes remaining in the buffer. Then, either all data will fit into the caller's buffer or only some of them will fit. If the number of bytes left to receive is smaller than the size of the buffer passed in by the caller (wLeftToRecv<len), you can return all of the data in the buffer. The function copies wLeftToReceive bytes starting at wRecvOffset into the caller's buffer, and sets the isstillreceiving flag to False (thus marking that all data have been received). The function will return wLeftToReceive, the number of characters copied to the caller's buffer.

If the number of bytes left to receive does not fit in the user buffer, the data must be returned in chunks to the caller, so WinMock:

1. Copies len (size of caller buffer) bytes starting at wRecvOffset into the user buffer.

2. Sets wRecvOffset to the next unread byte by adding len to the current value.

3. Returns len as the number of bytes written to the caller's buffer.

If no data are available (FALSE==isstillreceiving), WinMock:

1. Sets isstillreceving to True (so the next recv() call will find data available on this socket).

2. Sets wRecvOffset to 0 (so that the next recv() call will begin reading at the beginning of the buffer.

3. Returns 0 bytes copied to buffer.

The [recv] section of winmock.ini may contain a line of the format buffer= <string of received text> or buffer=.<file name containing text to be received>. If the first character of the string given as the value of the buffer key is a period, it is assumed that the rest of the string is a (possibly fully qualified) path to a file that contains text to be returned as the received text.

The default error-return code is SOCKET_ERROR. The user may not modify the return value of recv() other than through a [lasterror] key. This command will fail if the socket is not connected with a WSAENOTCONN error.

  • recvfrom(). This reads data from the socket and is functionally similar to recv(). The [recvfrom] section of winmock.ini may contain lines of the formats: sin_family=2, sin_port=0, buffer=This is received text.\r\n.\r\n, s_proto=<protocol used by service>. The default is http. The default error-return code is SOCKET_ERROR. The user may not modify the return value of recvfrom() except through a [lasterror] key.
  • send(). Writes data to the connected socket. The [send] section of winmock.ini currently has no entries. The default error-return code is SOCKET_ ERROR. The user may not modify the return value of send() except through a [lasterror] key.
  • sendto(). Writes data to the socket. The [sendto] section of winmock.ini currently has no entries. The default error-return code is SOCKET_ERROR. The user may not modify the return value of sendto() except through a [lasterror] key.
  • socket(). Creates an endpoint of communication and is one of the more-complicated commands in WinMock. The socket() command only permits AF_INET or AF_UNSPEC as the address-family parameter. This is the Internet address format. After verifying the family parameter, the socket() command retrieves a socket by calling wmGetFreeSocket(). The type and protocol parameters must agree. Table 1 lists valid combinations. Invalid combinations cause an appropriate error (WSAEPROTOTYPE or WSAEPROTONOSUPPORT).
The socket() command also fills in the socket's appropriate family, type, and protocol fields. It marks the calling task as the owner of the socket by placing the task handle retrieved from GetCurrentTask() in the mytask field of the socket structure. The mys field contents are returned to the caller as the socket tag. The [socket] section of winmock.ini currently has no entries. The default error-return code is INVALID_ SOCKET. The user may not modify the return value of socket() other than through a [lasterror] key. A protocol parameter gives a last error of WSAEPROTONOSUPPORT, and a type parameter gives a last error of WSAEPROTOTYPE. (These parameters are not in Table 1.) If no free socket is available, INVALID_SOCKET is returned with a last error of WSAEMFILE.

  • WSAGetLastError(). Retrieves the error value for the last failing WinSock operation. If the function cannot retrieve task information for the calling task, the error code WSANOTINITIALISED is returned; otherwise, the contents of the mylasterror field in the current task's task structure are returned. The [WSAGetLastError] section of winmock.ini currently has no entries. This routine cannot fail and does not support the [lasterror] section.
  • WSACleanup(). Deregisters the calling task from the WinMock library. It must be called one time for every call to WSAStartup(). A "naive application" (as the WinSock API specification refers to it) may call WSACleanup() repeatedly until the call returns SOCKET_ERROR. WSACleanup() calls wmRemoveTaskStruct() for the current task structure. wmRemoveTaskStruct() decrements the WSAStartupCount field of the task structure. If this value falls to 0, WinMock frees all resources allocated for this task and resets the task structure as available. wmClearSocketsForThisTask() frees all memory blocks allocated by WinMock for this task and releases all sockets owned by this task. The [WSACleanup] section of winmock.ini has no entries. This routine will fail if the calling task is not registered with WinMock (or has already deregistered). The default error code is SOCKET_ERROR. This routine does not support the [lasterror] section. If a blocking routine is in process (TRUE==isblocking in the task structure), the routine will fail with a last error of WSAEINPROGRESS.
  • WSAStartup(). This is the first Windows Sockets API called by a Windows Sockets application. The [WSAStartup] section of winmock.ini may contain wVersion, the Windows Sockets version that the caller will use (default is 1.1), and wHighVersion, the highest version of Windows Sockets supported by the WinMock library. These functions fill in the WSAData structure allocated by the caller: wVersion, the default; szDescription, a description of the Windows Sockets implementation; and szSystemStatus, status or configuration information.

Additional elements of the WSAData structure are: iMaxSockets, which is set to the value of the MAXSOCKETS #define; iMaxUdpDg, which is set to 512 (the minimum value permitted by the Windows Sockets specification); lpVendorInfo, which is NULL.

This routine will fail with a return code of WSAVERNOTSUPPORTED if the caller's requested version of the Windows Sockets cannot be supported.

Conclusion

The WinMock library is not yet complete and only supports a subset of the Windows Sockets 1.1 API. However, this subset is enough to bring up a number of available Web browsers and simulate online interaction. I am currently upgrading the WinMock library to a fully enabled, Windows Sockets-compliant interface that I'll release in the near future.

Table 1: Valid socket() type and protocol pairs. All other combinations of protocols and types are invalid in the WinMock library implementation.

      Protocol      0          UDP           TCP
     

      0             INVT       OK (DGRAM)    OK (STREAM)

      DGRAM         OK (UDP)   OK            INVT

      STREAM        OK (TCP)   INVT          OK



      OK = valid                                OK (DGRAM) = valid, type set to DGRAM
      OK (UDP) = valid, protocol set to UDP     OK (STREAM) = valid, type set to STREAM
      OK (TCP) = valid, protocol set to TCP     INVT = invalid, incompatible type



Table 2: WinMock library-function prototypes

int APIENTRY          closesocket (SOCKET s)
int APIENTRY          connect (SOCKET s, LPSOCKADDR name, int namelen)
LPHOSTENT APIENTRY    gethostbyname(LPSTR hostname)
int APIENTRY          gethostname (LPSTR hostname, int namelen)
LPPROTOENT APIENTRY   getprotobyname(LPSTR protocolname)
LPSERVENT APIENTRY    getservbyname(LPSTR servicename, LPSTR proto)
int APIENTRY          recv (SOCKET s, LPSTR buf, int len, int flags)
int APIENTRY          recvfrom (SOCKET s, LPSTR buf, int len, int flags, LPSOCKADDR from, LPINT fromlen)
int APIENTRY          send (SOCKET s, LPSTR buf, int len, int flags)
int APIENTRY          sendto (SOCKET s, LPSTR buf, int len, int flags, LPSOCKADDR to, int tolen)
SOCKET APIENTRY       socket (int af, int type, int protocol)
int APIENTRY          WSAGetLastError(void)
int APIENTRY          WSACleanup(void)
int APIENTRY          WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData)

Listing One

WORD wmInitializeTaskList()
{
   PMYTASK pthistask = pataskshead ;
   int i ;
   // mark all our tasks as unused
   for ( i = 0; i < MAXTASKS; i++)
   {
      pthistask[i].mytask = (HTASK) NULL ;
      pthistask[i].myhostent = NULL ;
      pthistask[i].myprotoent = NULL ;
      pthistask[i].myservent = NULL ;
      pthistask[i].isblocking = FALSE ;
      pthistask[i].mylasterror = 0 ;
      pthistask[i].WSAStartupCount = 0 ;
      pthistask[i].ptnext = NULL ;
   }
   return( TRUE ) ;
}
WORD wmInitializeSocketList()
{
   PMYSOCKET pthissocket = pasocketshead ;
   int i ;
   // mark all our sockets as unused...
   for ( i = 0; i < MAXSOCKETS; i++)
   {
      pthissocket[i].mys = SOCKET_ERROR ;
      pthissocket[i].mytask = (HTASK) NULL ;
      pthissocket[i].isconnected = FALSE ;
      pthissocket[i].isstillreceiving = FALSE ;
      pthissocket[i].socketblocking = FALSE ;
      pthissocket[i].socketfamily = 0 ;
      pthissocket[i].socketprotocol = 0 ;
      pthissocket[i].sockettype = 0 ;
      pthissocket[i].hRecvBuffer = NULL ;
      pthissocket[i].iRecvSize = 0 ;
      pthissocket[i].iRecvOffset = 0 ;
      pthissocket[i].hSendBuffer = NULL ;
      pthissocket[i].psnext = NULL ;
   }
  return( TRUE ) ;
}

Listing Two

; each section (application name) corresponds to the given Windows Sockets API 
; each keyname corresponds to the WinSock API parameter or structure field
; place this file in your WINDOWS directory

[closesocket]

[connect]

[gethostbyname]
 h_name=hostname
 h_addrtype=2
 h_length=4
 h_addr_list=11.22.33.44

[gethostname]
 hostname=host.company.com

[getprotobyname]
 p_proto=6

[getservbyname]
 p_proto=tcp
; the following is in network byte order
 p_port=8000 ;

[recv]
 buffer=This is received text.\r\n.\r\n
;buffer=.temp.fil

[recvfrom]
 sin_family=2
 sin_port=0
 buffer=This is received text.\r\n.\r\n

[send]

[sendto]

[socket]

[wsastartup]
; 257 decimal is 0x0101
 wVersion=257
 wHighVersion=257
 szDescription=WinMock Windows Sockets Off-Line Tester
 szSystemStatus=
;iMaxSockets= MAXSOCKETS

[lasterror]
;closesocket=WSAENOTSOCK
;connect=WSAEISCONN
;gethostbyname=WSAHOST_NOT_FOUND
;gethostname=WSAEINPROGRESS
;getprotobyname=WSANO_DATA
;getservbyname=WSANO_DATA
;recv=WSAECONNABORTED 
;recvfrom=WSAEFAULT
;send=WSAECONNRESET
;sendto=WSAEADDRNOTAVAIL
;socket=WSAENOBUFS
;wsacleanup=
;wsagetlasterror=
;wsastartup=


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.