diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java
index 5d4e595a93cb3f8de23d589a1dbe6bdd720bdd51..db220b51220282505f11edf4206889dc2abb5e86 100644
--- a/api/net/sf/briar/api/db/DatabaseComponent.java
+++ b/api/net/sf/briar/api/db/DatabaseComponent.java
@@ -98,6 +98,12 @@ public interface DatabaseComponent {
 	void generateTransportUpdate(ContactId c, TransportWriter t) throws
 	DbException, IOException;
 
+	/**
+	 * Returns an outgoing connection number for the given contact and
+	 * transport.
+	 */
+	long getConnectionNumber(ContactId c, int transportId) throws DbException;
+
 	/**
 	 * Returns the connection reordering window for the given contact and
 	 * transport.
diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java
index 648f231d779d6eeb48e0faf3b92a1803bc3fcb3b..6f1141d27ef3873c161befb3793928e78c67cffc 100644
--- a/components/net/sf/briar/db/Database.java
+++ b/components/net/sf/briar/db/Database.java
@@ -165,6 +165,15 @@ interface Database<T> {
 	 */
 	Collection<BatchId> getBatchesToAck(T txn, ContactId c) throws DbException;
 
+	/**
+	 * Allocates and returns a connection number for the given contact and
+	 * transport.
+	 * <p>
+	 * Locking: contacts read, windows write.
+	 */
+	long getConnectionNumber(T txn, ContactId c, int transportId)
+	throws DbException;
+
 	/**
 	 * Returns the connection reordering window for the given contact and
 	 * transport.
diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java
index 7ef8202db821f47106ff7df66f4128c27e4afe19..25e90252778f07b204279190d25259daab64d98a 100644
--- a/components/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/components/net/sf/briar/db/DatabaseComponentImpl.java
@@ -632,6 +632,30 @@ DatabaseCleaner.Callback {
 			LOG.fine("Added " + transports.size() + " transports to update");
 	}
 
+	public long getConnectionNumber(ContactId c, int transportId)
+	throws DbException {
+		contactLock.readLock().lock();
+		try {
+			if(!containsContact(c)) throw new NoSuchContactException();
+			windowLock.writeLock().lock();
+			try {
+				T txn = db.startTransaction();
+				try {
+					long outgoing = db.getConnectionNumber(txn, c, transportId);
+					db.commitTransaction(txn);
+					return outgoing;
+				} catch(DbException e) {
+					db.abortTransaction(txn);
+					throw e;
+				}
+			} finally {
+				windowLock.writeLock().unlock();
+			}
+		} finally {
+			contactLock.readLock().unlock();
+		}
+	}
+
 	public ConnectionWindow getConnectionWindow(ContactId c, int transportId)
 	throws DbException {
 		contactLock.readLock().lock();
diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java
index 8e29f1f942255c81d17ce0bb97b9d413130c19b2..65f8b40e16e0d98727e44ad0af3fac4bdc958dbb 100644
--- a/components/net/sf/briar/db/JdbcDatabase.java
+++ b/components/net/sf/briar/db/JdbcDatabase.java
@@ -194,6 +194,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 		+ " transportId INT NOT NULL,"
 		+ " centre BIGINT NOT NULL,"
 		+ " bitmap INT NOT NULL,"
+		+ " outgoing BIGINT NOT NULL,"
 		+ " PRIMARY KEY (contactId, transportId),"
 		+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
 		+ " ON DELETE CASCADE)";
@@ -805,6 +806,56 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	public long getConnectionNumber(Connection txn, ContactId c,
+			int transportId) throws DbException {
+		PreparedStatement ps = null;
+		ResultSet rs = null;
+		try {
+			String sql = "SELECT outgoing FROM connectionWindows"
+				+ " WHERE contactId = ? AND transportId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setInt(1, c.getInt());
+			ps.setInt(2, transportId);
+			rs = ps.executeQuery();
+			if(rs.next()) {
+				long outgoing = rs.getLong(1);
+				if(rs.next()) throw new DbStateException();
+				rs.close();
+				ps.close();
+				sql = "UPDATE connectionWindows SET outgoing = ?"
+					+ " WHERE contactId = ? AND transportId = ?";
+				ps = txn.prepareStatement(sql);
+				ps.setLong(1, outgoing + 1);
+				ps.setInt(2, c.getInt());
+				ps.setInt(3, transportId);
+				int affected = ps.executeUpdate();
+				if(affected != 1) throw new DbStateException();
+				ps.close();
+				return outgoing;
+			} else {
+				rs.close();
+				ps.close();
+				sql = "INSERT INTO connectionWindows"
+					+ " (contactId, transportId, centre, bitmap, outgoing)"
+					+ " VALUES(?, ?, ?, ?, ?)";
+				ps = txn.prepareStatement(sql);
+				ps.setInt(1, c.getInt());
+				ps.setInt(2, transportId);
+				ps.setLong(3, 0L);
+				ps.setInt(4, 0);
+				ps.setLong(5, 0L);
+				int affected = ps.executeUpdate();
+				if(affected != 1) throw new DbStateException();
+				ps.close();
+				return 0L;
+			}
+		} catch(SQLException e) {
+			tryToClose(rs);
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	public ConnectionWindow getConnectionWindow(Connection txn, ContactId c,
 			int transportId) throws DbException {
 		PreparedStatement ps = null;
@@ -1733,23 +1784,29 @@ abstract class JdbcDatabase implements Database<Connection> {
 				sql = "UPDATE connectionWindows SET centre = ?, bitmap = ?"
 					+ " WHERE contactId = ? AND transportId = ?";
 				ps = txn.prepareStatement(sql);
+				ps.setLong(1, w.getCentre());
+				ps.setInt(2, w.getBitmap());
+				ps.setInt(3, c.getInt());
+				ps.setInt(4, transportId);
 				int affected = ps.executeUpdate();
 				if(affected != 1) throw new DbStateException();
+				ps.close();
 			} else {
 				rs.close();
 				ps.close();
 				sql = "INSERT INTO connectionWindows"
-					+ " (contactId, transportId, centre, bitmap)"
-					+ " VALUES(?, ?, ?, ?)";
+					+ " (contactId, transportId, centre, bitmap, outgoing)"
+					+ " VALUES(?, ?, ?, ?, ?)";
 				ps = txn.prepareStatement(sql);
 				ps.setInt(1, c.getInt());
 				ps.setInt(2, transportId);
 				ps.setLong(3, w.getCentre());
 				ps.setInt(4, w.getBitmap());
+				ps.setLong(5, 0L);
 				int affected = ps.executeUpdate();
 				if(affected != 1) throw new DbStateException();
+				ps.close();
 			}
-			ps.close();
 		} catch(SQLException e) {
 			tryToClose(rs);
 			tryToClose(ps);