I can't discuss subnetting w/o discussing IP routing, because one begets the other. This is more answer than you wanted, but what I'm willing to give.

IP subnets exist to allow routers to choose appropriate destinations for packets. You can use IP subnets to break up larger networks for logical reasons (firewalling, etc), or physical need (smaller broadcast domains), but in the end, IP routers use your IP subnets to make routing decisions.


Counting to 1
-------------

Shame on you if you aren't fluent in binary (base 2) notation! Okay-- that may be a bit harsh, but it is really, really easy to learn to count in binary and to learn shortcuts to convert binary to decimal and back.

Counting in binary is simple. Think of a car "odometer", only each digit can only count up to 1. The car is fresh from the factory, and the odometer reads "00000000". You drive 1 mile, and the odometer reads "00000001". So far, so good.

When you turn over mile 2, the first digit rolls back over to "0", and the next digit rolls over to "1", giving for "00000010". It looks like the number ten in decimal, but it's actually 2 in binary. 

When you drive your next mile, the odometer reads "00000011". And, finally, when you drive through mile 4 both digits that are reading "1" roll back over to zero and the 3rd digit becomes a "1", giving us "00000100" for a binary representation of the number 4.

You can memorize all of that but you really should get into your head and understanding of how the little odometer "rolls over" (which is exactly the same with a decimal odometer, except that each digit has the numbers 0-9 available) as the number gets bigger. Doing addition and subtraction in binary is fairly easy after you get the "odometer" concept going in your head.

Converting binary values to decimal would be very tedious if you had to "roll back" the odometer tick-by-tick and keep a manual count. You could do it, but it wouldn't be very efficient. It's easier to learn a little algorithm to do it.

Converting the binary number "1101011" to decimal is a simple process. Start by counting the number of bits in the number. In this case, there are 7. Make 7 divisions on a sheet of paper (in your mind, in a text file, etc) and begin filling them in from right to left. In the rightmost slot, enter the number "1", because we'll always start with "1". In the next slot to the left, enter double the value in the slot to the right (so, "2" in the next one, "4" in the next one) and continue until all the slots are full. (BTW, you'll memorize these numbers-- which are the powers of 2-- as you do this more and more. I'm alright up to 131,072 in my head but I usually need a calculator or paper after that).

So, you should have the following on your paper in your little slots.

     64    |    32    |    16    |    8    |    4    |    2    |    1    |

Transcribe the bits from the binary number below the slots, like so:

     64    |    32    |    16    |    8    |    4    |    2    |    1    |
      1          1          0         1         0         1         1

Now, add some computational symbols and compute the answer to the problem:

     64    |    32    |    16    |    8    |    4    |    2    |    1    |
    x 1        x 1        x 0       x 1       x 0       x 1       x 1
    ---        ---        ---       ---       ---       ---       ---
           +          +          +         +         +         +         =

Doing all the math, you should come up with:

     64    |    32    |    16    |    8    |    4    |    2    |    1    |
    x 1        x 1        x 0       x 1       x 0       x 1       x 1
    ---        ---        ---       ---       ---       ---       ---
     64    +    32    +     0    +    8    +    0    +    2    +    1    =   107


That's got it. "1101011" in decimal is 107. It's easy math.

Converting decimal to binary is just as easy and is the same basic algorithm, run in reverse. Say that we want to convert the number 218 to binary. Starting on the right of a sheet of paper, write the number "1". To the left, double that value (so, "2") and continue moving toward the left of the paper doubling the last value. If the number you are about to write is greater than the number being converted from decimal to binary stop writing-- otherwise continue (convering 34,157,216,092 to binary in this method can be a little tedious, but not impossible).

So, you should have on your paper:

     128    |    64    |    32    |    16    |    8    |    4    |    2    |    1    |

Beginning from the left number, write "218" above 128 and ask yourself: "Is 218 larger than or equal to 128?" If the answer is yes, scratch a "1" below "128". Above "64", write the result of 218 minus 128 (90).

Looking at "64", ask yourself: "Is 90 larger than or equal to 64?" It is, so you'd write a "1" below "64", then subtract 64 from 90 and write that above "32" (26).

When you get to "32", though, you find that 32 is not greater than or equal to 26. In this case, write a "0" below "32", copy the number (26) from above 32" to above "16" and then continue asking yourself the same question with the rest of the numbers.

When you're all done, you should have:

     218         90         26         26        10         2         2         0
     128    |    64    |    32    |    16    |    8    |    4    |    2    |    1    |
       1          1          0          1         1         0         1         0

The numbers at the top are just notes used in computation and don't mean much to us. At the bottom, though, you see a binary number "11011010". Sure enough, 218, converted to binary, is "11011010". 

Following these very simple procedures you can convert binary to decimal and back again w/o a calculator. The math is all very simple and the rules can be memorized with just a bit of practice.


Splitting Up Addresses
----------------------

Think of IP routing like pizza delivery. 

When you're asked to deliver a pizza to "123 Main Street" it's very clear to you, as a human, that you want to go to the building numbered "123" on the street named "Main Street". It's easy to know that you need to go to the 100-block of Main Street because the building number is between 100 and 199. You "just know" how to split the address up.

Doing the same thing with IP addresses isn't, perhaps, so clear, but routing packets hinges on the same problem. IP routers "just know" how to split up IP addresses and make routing decisions. You should know how to do this, too.

Each host on an IP network is configured with a "building number" (like "123" in the example above) called the "host ID" and a "street name" (like "Main Street" in the example above) called the "network ID". For our human eyes, it's easy to see where the building number and the street name are in "123 Main Street", but harder to see in "10.13.216.41, subnet mask 255.255.192.0". 

Extracting the "host ID" and the "network ID" out of an IP / subnet combination is actually really easy. Start by writing out the IP address in binary (use a calculator if you can't convert decimal-to-binary in your head, but make a note to go learn how to do it-- it's really, really easy and impresses women at parties):

          10.      13.     216.      41
    00001010.00001101.11011000.00101001

Write out the subnet mask in binary, too:

    11111111.11111111.11000000.00000000

Written side-by-side, you can see that the point in the subnet mask where the "1's" stop "lines up" to a point in the IP address. That's the point that the network ID and the host ID split. So, in this case:

          10.      13.     216.      41
    00001010.00001101.11011000.00101001 - IP address
    11111111.11111111.11000000.00000000 - subnet mask
    00001010.00001101.11000000.00000000 - Portion of IP address covered by 1's in subnet mask, remaining bits set to 0
    00000000.00000000.00011000.00101001 - Portion of IP address covered by 0's in subnet mask, remaining bits set to 0

By using the subnet mask to "mask out" the bits covered by 1's in the IP address (replacing the bits that are not "masked out" with 0's) you get the network ID:

          10.      13.     192.       0
    00001010.00001101.11000000.00000000 - Network ID

Likewise, by using the subnet mask to "mask out" the bits covered by 0's in the IP address (replacing the bits that are not "masked out" with 0's again) you get the host ID:

           0.       0.      24.      41
    00000000.00000000.00011000.00101001 - Portion of IP address covered by 0's in subnet mask, remaining bits set to 0

It's not as easy for our human eyes to see the "break" between the network ID and the host ID as it is between the "building number" and the "street name" in physical addresses during pizza delivery, but the ultimate effect is the same.

Now that you can split up IP addresses and subnet masks into host ID's and network ID's you can deliver pizza (route IP).


Some Terminology
----------------

You're going to see subnet masks written all over the Internet and throughout the rest of this answer as (IP/number). This is Classless Inter-Domain Routing notation (CIDR) notation. 255.255.255.0 has 24 bits of 1's at the beginning, and it's faster to write that as "/24" than as "255.255.255.0". To convert a CIDR number (like "/16") to a dotted-decimal subnet mask, just write out that number of 1's, split it into groups of 8 bits, and convert it to decimal. (A "/16" is "255.255.0.0", for instance.)

Back in the "old days", subnet masks weren't specified but were derived by bits in the IP address. An IP address starting with 0 - 127 had an implied subnet mask of 255.0.0.0 (a "class A" IP address). These implied subnet masks aren't used today and I don't recommend even learning about them anymore unless you have the misfortune of dealing with very old equipment or old protocols (like RIPv1) that don't support classless IP addressing. (I try not to even mention these "classes" of addresses to students because it's inapplicable today and can be confusing.)

Some devices use "wildcard masks". A "wildcard mask" is nothing more than a subnet mask with all 0's where there would be 1's, and 1's where there would be 0's. The "wildcard mask" of a /26 is:

     11111111.11111111.11111111.11000000 - /26 subnet mask
     00000000.00000000.00000000.00111111 - /26 "wildcard mask"

Typically you see "wildcard masks" used to match host IDs in access-control lists or firewall rules. We won't discuss them any further here.


How a Router Works
------------------

IP routers have a similar job. When presented with a packet bound for address 192.168.10.2, the IP router needs to determine which of its interfaces will best get that packet to its destination.

Let's say that you are an IP router, and you have interfaces connected to you numbered:

 - Ethernet0 - 192.168.20.1, subnet mask /24
 - Ethernet1 - 192.168.10.1, subnet mask /24

If you receive a packet inbound with a destination address of "192.168.10.2", it's pretty easy to tell (with your human eyes) that the packet should be sent out the interface Ethernet1, because its address corresponds to the packet's destination address.

For a router, this route selection process is done by building a routing table. A routing table contains IP network id and destination interfaces. You already know how to obtain a network ID from an IP address and subnet mask, so you're on your way to building a routing table. Here's our routing table for this router:

 - Network ID: 192.168.20.0 (11000000.10101000.00010100.00000000) - 24 bit subnet mask - Interface Ethernet0
 - Network ID: 192.168.10.0 (11000000.10101000.00001010.00000000) - 24 bit subnet mask - Interface Ethernet1

For our incoming packet bound for "192.168.10.2", we need only convert that packet's address to binary (as humans-- the router gets it as binary off the wire to begin with) and attempt to match it to each address in our routing table (up to the number of bits in the subnet mask) until we match an entry.

 - Incoming packet destination: 11000000.10101000.00001010.00000010

Comparing that to the entries in our routing table:

    11000000.10101000.00001010.00000010 - Destinaton address for packet
    11000000.10101000.00010100.00000000 - Interface Ethernet0
    !!!!!!!!.!!!!!!!!.!!!????!.xxxxxxxx - ! indicates matched digits, ? indicates no match, x indicates not checked (beyond subnet mask)

    11000000.10101000.00001010.00000010 - Destinaton address for packet
    11000000.10101000.00001010.00000000 - Interface Ethernet1, 24 bit subnet mask
    !!!!!!!!.!!!!!!!!.!!!!!!!!.xxxxxxxx - ! indicates matched digits, ? indicates no match, x indicates not checked (beyond subnet mask)

The entry for Ethernet0 matches the first 19 bits fine, but then stops matching. That means it's not the proper destination interface. You can see that the interface Ethernet1 matches 24 bits of the destination address. Ah, ha! The packet is bound for interface Ethernet1.

In a real-life router, the routing table is sorted in such a manner that the longest subnet masks are checked for matches first (i.e. the most specific routes), and numerically so that as soon as a match is found the packet can be routed and no further matching attempts are necessary (meaning that 192.168.10.0 would be listed first and 192.168.20.0 would never have been checked). Here, we're simplifying that a bit. Fancy data structures and algorithms make faster IP routers, but simple algorithms will produce the same results.


Static Routes
-------------

Up to this point, we've talked about our hypothetical router as having networks directly connected to it. That's not, obviously, how the world really works.

Let's start by calling our router from the earlier examples "Router A". You already know RouterA's routing table as:

 - Network ID: 192.168.20.0 (11000000.10101000.00010100.00000000) - subnet mask /24 - Interface RouterA-Ethernet0
 - Network ID: 192.168.10.0 (11000000.10101000.00001010.00000000) - subnet mask /24 - Interface RouterA-Ethernet1

Suppose that there's another router, "Router B", at IP address 192.168.10.254, subnet mask /24, and it has the following routing table:

 - Network ID: 192.168.10.0 (11000000.10101000.00001010.00000000) - subnet mask /24 - Interface RouterB-Ethernet0
 - Network ID: 192.168.30.0 (11000000.10101000.00011110.00000000) - subnet mask /24 - Interface RouterB-Ethernet1

You can see that Router B knows how to "get to" a network, 192.168.30.0/24, that Router A knows nothing about. 
Suppose that a PC on a network attached to router A (the PC is 192.168.20.13) sends a packet to Router A bound for the destination 192.168.30.46 (a device on a network attached to Router B). With the routing table shown above, neither entry in Router A's routing table matches the destination 192.168.30.46, so Router A will return the packet to the sender with the message "Destination network unreachable".

To make Router A "aware" of the existance of the 192.168.30.0/24 network, we add the following entry to the routing table on Router A:

 - Network ID: 192.168.30.0 (11000000.10101000.00011110.00000000) - subnet mask /24 - Destination router: 192.168.10.254

In this way, Router A has a route that matches the 192.168.30.46 destination of our example packet. This routing table entry effectively says "If you get a packet bound for 192.168.30.0/24, send it on to 192.168.10.254 because he knows how to deal with it."

The converse of this static route is also true, by the way. If Router B wants to deliver packets to the 192.168.20.0 subnet mask 255.255.255.0 network, it will need an entry in its routing table:

 - Network ID: 192.168.20.0 (11000000.10101000.00010100.00000000) - subnet mask /24 - Destination router: 192.168.10.1 (Router A's IP address in the 192.168.10.0 network)

This would create a symmetric path between the 192.168.30.0/24 network and the 192.168.20.0/24 network across the 192.168.10.0/24 network between these routers.

You can get a lot of mileage out of static routes. Dynamic routing protocols like EIGRP, RIP, etc, are really nothing more than a way for routers to exchange routing information between each other that could, in fact, be configured with static routes. One large advantage to using dynamic routing protocols over static routes, though, is that dynamic routing protocols can *dynamically* change the routing table based on network conditions (bandwidth utilizaiton, an interface "going down", etc) and, as such, using a dynamic routing protocol can result in a configuration that "routes around" failures or bottlenecks in the network infrastructure. Dynamic routing protocols are *WAY* above the level of this answer, though.


You Can't Get There From Here
-----------------------------

In the case of our example Router A, what happens when a packet bound for "172.16.31.92" comes in?

Looking at the Router A routing table, neither destination interface or static route matches the first 24 bits if 172.18.31.92 (which is 10101100.00010000.00011111.01011100, BTW). 

As we already know, Router A would return the packet to the sender via a "Destination network unreachable" message.

Say that there's another router (Router C) sitting at the address "192.168.20.254". Router C has a connection to the Internet! It would be nice if Router A could route packets that do not match any local interface up to Router C such that Router C can send them on to the Internet. Enter the "default gateway" route.

Add an entry at the end of our routing table like this:

 - Network ID: 0.0.0.0 (00000000.00000000.00000000.00000000) - subnet mask /0 - Destination router: 192.168.20.254

When we attempt to match "172.16.31.92" to each entry in the routing table we end up hitting this new entry. It's a bit perplexing, at first. We're looking to match zero bits of the destination address with... wait... what? Matching zero bits? So, we're not looking for a match at all. This routing table entry is saying, basically, "If you get here, rather than giving up on delivery, send the packet on to the router at 192.168.20.254 and let him handle it". 

Fortunately, 192.168.20.254 is a destination that we *DO* know how to deliver a packet to. When confronted with a packet bound for a destination for which we have no specific routing table entry, then, the "default gateway" entry will always match (since it matches zero bits of the destination address!) and gives us a "last restort" place that we can send packets for delivery.

Real routers typically store the default gateway as the last route in their routing table such that it matches packets after they've failed to match all other entries in the table.

If you tried to specify a default gateway with a destination address of, say, 192.168.50.254, in Router A (rather than 192.168.20.254), thing wouldn't work so well. 192.168.50.254 isn't an address that Router A knows how to deliver packets to, so it would be ineffective as a default gateway. This brings up a nice rule of thumb: Your default gateway must be set to a device that is already reachable without having a default gateway specified.


Urban Planning and Pizza Delivery
---------------------------------

Let's say that you're building a city. You know that you'd like Main Street to be 30 blocks long, with each block having up to 99 buildings in each. It's pretty easy to plan your street numbering such that each block in Main Street has a range of street numbers from 1 to 99, and you can tell very easily what the "starting number" in each block should be.

This is the same problem with planning IP subnets, but instead of working in decimal, you work in binary.

Your ISP gives you the range the network ID 192.168.40.0/24 (11000000.10101000.00101000.00000000). You know that you'd like to use a firewall / router device to limit communication between different parts of your network (servers, client computers, network equipment) and, as such, you'd like to break these various parts of your network up into IP subnets (which the firewall / router device can then route between).

You have:

 - 12 server computers, but you might get up to 50% more
 - 9 switches
 - 97 client computers, but you might get more

What's a good way to break up 192.168.40.0/24 into these pieces?

Thinking in even powers of two, and working with the larger numbers of possible devices, you can come up with:

 - 18 server computers - Next largest power of two is 32
 - 9 switches - Next largest power of two is 16
 - 97 client computers - Next largest power of two is 128

In a given IP subnet, there are two addresses reserved that can't be used as valid device IP addresses-- the address with all zeros in the host ID portion and the address with all ones in the host ID portion. As such, for any given IP subnet, the number of host addresses available is two to the power of the quantity of 32 minus the number of bits in the subnet mask, minus 2. So, in the case of 192.168.40.0/24 we can see that the subnet mask has 24 bits. That leaves 8 bits available for host IDs. We know that 2 to the 8th power is 256-- meaning that 256 possible combinations of bits fit into a slot 8 bits wide. Since the "11111111" and "00000000" combinations of those 8 bits aren't allowable for host IDs, that leaves us with 254 possible hosts that can be assigned in the 192.168.40.0/24 network.

Of those 254 hosts, it looks like we can fit the client computers, switches, and server computers into that space, right? Let's try.

You have 8 bits of subnet mask to "play with" (the remaining 8 bits of the IP address 192.168.40.0/24 not covered by the subnet mask provided by your ISP). We have to work out a way to use those 8 bits to create a number of unique network IDs that can accomodate the devices above.

Start with the largest network - the client computers. You know that the next larger power of two from the number of possible devices is 128. The number 128, in binary, is "10000000". Fortunately for us, that fits into the 8 bit slot we have free (if it didn't, that would be an indicatation that our starting subnet is too small to accomodate all our devices). 

Let's take our network ID, as provided by our ISP, and add a single bit of subnet mask to it, breaking it up into two networks:

    11000000.10101000.00101000.00000000 - 192.168.40.0 network ID
    11111111.11111111.11111111.00000000 - Old subnet mask (/24)

    11000000.10101000.00101000.00000000 - 192.168.40.0 network ID
    11111111.11111111.11111111.10000000 - New subnet mask (/25)

    11000000.10101000.00101000.10000000 - 192.168.40.128 network ID
    11111111.11111111.11111111.10000000 - New subnet mask (/25)

Look over that until it makes sense. We increased the subnet mask by one bit in length, causing the network ID to cover one bit that would have been used for host ID. Since that one bit can be either zero or one, we've effectively split our 192.168.40.0 network into two networks. The first valid IP address in the 192.168.40.0/25 network will be the first host ID with a "1" in the right-most bit:

    11000000.10101000.00101000.00000001 - 192.168.40.1 - First valid host in the 192.168.40.0/25 network

The first valid host in the 192.168.40.128 network will, likewise, be the first host ID with a "1' in the right-most bit:

    11000000.10101000.00101000.10000001 - 192.168.40.129 - First valid host in the 192.168.40.128/25 network

The last valid host in each network will be the host ID with every bit *except* the right-most bit set to "1":

    11000000.10101000.00101000.01111110 - 192.168.40.126 - First valid host in the 192.168.40.0/25 network
    11000000.10101000.00101000.11111110 - 192.168.40.254 - First valid host in the 192.168.40.128/25 network

So, in this way, we've created a network large enough to hold our client comptuers, and a second network that we can then apply the same principle to break down into yet smaller networks. Let's make a note:

 - Client computers - 192.168.40.0/25 - Valid IPs: 192.168.40.1 - 192.168.40.126

Now, to break down the second network for our servers and switches, we do the same thing. 

We have 12 server computers, but we might buy up to 6 more. Let's plan on 18, which leaves us the next highest power of 2 as 32. In binary, 32 is "100000", which is 6 bits long. We have 7 bits of subnet mask left in 192.168.40.128/25, so we have enough bits to continue "playing". Adding one more bit of subnet mask gives us two more networks:

    11000000.10101000.00101000.10000000 - 192.168.40.128 network ID
    11111111.11111111.11111111.10000000 - Old subnet mask (/25)

    11000000.10101000.00101000.10000000 - 192.168.40.128 network ID
    11111111.11111111.11111111.11000000 - New subnet mask (/26)
    11000000.10101000.00101000.10000001 - 192.168.40.129 - First valid host in the 192.168.40.128/26 network
    11000000.10101000.00101000.10111110 - 192.168.40.190 - Last valid host in the 192.168.40.128/26 network

    11000000.10101000.00101000.11000000 - 192.168.40.192 network ID
    11111111.11111111.11111111.11000000 - New subnet mask (/26)
    11000000.10101000.00101000.11000001 - 192.168.40.193 - First valid host in the 192.168.40.192/26 network
    11000000.10101000.00101000.11111110 - 192.168.40.254 - Last valid host in the 192.168.40.192/26 network

So, now we've broken up 192.168.40.128/26 into two more networks, each of which has 26 bits of subnet mask, or a total of 62 possible host IDs-- 2 ^ (32 - 26) - 2.

That means that both of those networks have enough addresses for our servers and switches! Let's make notes:

 - Servers - 192.168.40.128/26 - Valid IPs: 192.168.40.129 - 192.168.40.190
 - Switches - 192.168.40.192/26 - Valid IPs: 192.168.40.193 - 192.168.40.254

This technique is called variable-length subnet masking (VLSM) and, if properly applied, causes "core routers" to have smaller routing tables (through a process called "route summarizaiton"). In the case of our ISP in this example, they can be totally unaware of how we've subnetted 192.168.40.0/24. If their router has a packet bound for 192.168.40.206 (one of our switches), they need only know to pass it to our router (since 192.168.40.206 matches the network id and subnet mask 192.168.40.0/24 in their router's routing table) and our router will get it to the destination. This keeps our subnet routes out of their routing tables. (I'm simplifying here, but you get the idea.)

You can plan very geographically large networks in this same way. As long as you do the right "urban planning" up front (anticipating the number of hosts in each sub-network with some accuracy and an eye to the future) you can create a large routing hierarchy that, at the core routers, "summarizes" to a very small number of routes. As we saw above, the more routes that are in a router's routing table the slower it performs its job. Designing an IP network with VLSM and keeping routing tables small is a Good Thing(tm).


The Unrealism of Examples
-------------------------

The fictional world in this answer is, obviously, fictional. Typically you can make subnets on modern switched Ethernet with more hosts than 254 (traffic profile dependent). As has been pointed out in comments, using /24 networks between routers isn't consistent with Real Life(tm). It makes for cute examples, but is a waste of address space. Typically, a /30 or a /31 (see http://www.faqs.org/rfcs/rfc3021.html for details on how /31's work-- they are beyond the scope of this answer for sure) network is used on links that are strictly point-to-point between two routers.