tags:

views:

392

answers:

9

I am running a server, and I would like to have a users dictionary, and give each user a specific number.

Dictionary<int,ServerSideUser> users = new Dictionary<int,ServerSideUser>();

The key represents the user on the server, so when people send messages to that user, they send them to this number. I might as well have used the users IP number, but that's not that a good idea.

I need to allocate such a number for each user, and I'm really not sure how to do so. Someone suggested something like

Enumerable.Range(int.MinValue, int.MaxValue)
.Except(users.Select(x => x.Key)).First();

but I really don't think it's the optimal way. Also, I have the same problem with a List (or LinkedList) somewhere else.

Any ideas?

+9  A: 

If the size of the "number" doesn't matter, take a Guid, it will always be unique and non-guessable.

Lucero
Although that could make for some interesting hashing in the dictionary -> lots of numbers so the hash function coudl be a little slow!
Ed Woodcock
Hashing a GUID is not expensive, and it has the benefit of a usually very good distribution, so that the hashtable load is well distributed across the buckets.
Lucero
I'm.. not sure it fits here, it might, but.. I don't think I need something *that* big, I don't think I'll have *that* many users.
Nefzen
Non-guessable is not, and should never be, a consideration for choosing Guids. As its not true. Non-guessable is equivalent to cryptographically secure, which Guids are not.
Will
Guid.GetHashCode() is implemented as "return ((this._a ^ ((this._b << 0x10) | ((ushort) this._c))) ^ ((this._f << 0x18) | this._k));", it won't take long at all.
David
@Nefzen: it's not just about the number of users, but about not having to worry about finding a free number.@Will: if you create new GUIDs, not using the serial GUID functionality for mass-creation of GUIDs, guessing them is hardly possible. I didn't say they were cryptographically secure, but compared to an integer index which is increased they are much harder to guess.
Lucero
@Will: Guid generation in Windows (UuidCreate() or Guid.NewGuid()) has been a cryptographically secure 128-bit number since the Windows 2000 time frame. If you want the old sequential Guid generation behavior based on MAC address, you have to call UuidCreateSequential().
Brian Reiter
@Brian - Not according to anything I can find. The only ref I can find to v4 says that it is NOT cryptographically secure. I'm interested to see a link if you have one. The only thing about v4 re security is that it isn't mac based. According to sources online I've found its still *pseudo-random* which is NOT the same as cryptographically secure.
Will
+1  A: 

Why don't you keep track of the current maximum number and increment that number by one every time a new user is added?

VVS
might reach int.MaxValue?
Nefzen
2^31 users seem like a stretch.
Jason
put it long not int
Ahmed Said
yea, but consider one user has been in the server for a long period, and so long that 2^32 users have connected and disconnected. With, say MSN, it might happen. then you might get someone with the same number as this guy. So what, we reboot the server every once in a while?
Nefzen
If you worry about an overflow (I personally don't believe that about 1/3 of world's population can or will join your server) you can start over at 0 and loop-check until you found a free number.
VVS
yes, that just might be the optimal solution.
Nefzen
2^31 is approximately two billion. That's enough for a connection per second for sixty-seven years (~2 billion / ~.03 billion seconds per year). I don't think you need to worry about this. If you must, you can always code in reuse of IDs. Keep it simple.
Jason
+3  A: 

If you want a dictionary that uses an arbitrary, ordered integer key, you may also be able to use a List<ServerSideUser>, in which the list index serves as the key.

Is there a specific reason you need to use a Dictionary?

Using a List<> or similar data structure definitely has limitations. Because of concurrency issues, you wouldn't want to remove users from the list at all, except when cycling the server. Otherwise, you might have a scenario in which user 255 sends a message to user 1024, who disconnects and is replaced by a new user 1024. New user 1024 then receives the message intended for old user 1024.

If you want to be able to manage the memory footprint of the user list, many of the other approaches here work; Will's answer is particularly good if you want to use ints rather than Guids.

Jeff Sternal
it might as well be a list or a LinkedList, it just searches faster I reckon. The key represents the user on the server, so when people send messages to that user, they send them to this number. I might as well have used the users IP number, but that's not that a good idea.
Nefzen
@Nefzen: An array or list lookup by index will always be faster than a dictionary lookup by key.
LukeH
yes, but index size can be quite large, even though number of actual users may be quite small.
Nefzen
+1  A: 

Another option: Use a factory to generate ServerSideUser instances, which assigns a new, unique ID to each user.

In this example, the factory is the class itself. You cannot instantiate the class, you must get a new instance by calling the static Create method on the type. It increments the ID generator and creates a new instance with this new id. There are many ways to do this in a thread safe way, I'm doing it here in a rudimentary 1.1-compatible way (c# pseudocode that may actually compile):

public class ServerSideUser
{
  // user Id
  public Id {get;private set;}
  // private constructor
  private ServerSideUser(){}
  private ServerSideUser(int id) { Id = id; }

  // lock object for generating an id
  private static object _idgenLock = new Object();
  private static int _currentId = 0; // or whatever
  // retrieves the next id; thread safe 
  private static int CurrentId
  {
    get{ lock(_idgenLock){ _currentId += 1; return _currentId; } }
  }  

  public static ServerSideUser Create()
  {
    return new ServerSideUser(CurrentId);
  }
}
Will
it's not the way I do it, like factory method or whatnot, it's how do I produce unique Ids. I thought of this method, but what if 2^32 users come and go and we stumble upon an Id that's already taken?
Nefzen
Plus, more seriously, you may not want the IDs to be a guessable sequence either depending on what you are doing with them.
jerryjvl
you don't have any privileges if you know someone's number, only the ability to send messages to it.
Nefzen
@Nefzen 2^32 users is more than you think... It's about 150 users per second for a year. When you get that popular you can hire someone to rewrite the algorithm.
Karl
Use a long or a double if you're Twitter, then. You can increment a double as easily as you can an int. This method produces a unique id within a given AppDomain (you're not running more than one, are you?).
Will
Also, any "sequence" can be guessable. For instance, you can "guess" anybody's twitter account and send them a message. If you're concerned about guessing people's ids, you should watch for behavior that suggests this, such as many calls to different IDs that fail within a given period of time.
Will
Just as a reminder, you can use System.Threading.Interlocked.Increment(ref count) to do an atomic increment, if that's all you want to make thread-safe.
flq
Nefzen: regardless of privileges, being able to guess an ID might not be desirable, it depends on the purpose of the app... in your case you may not want a user to be able to guess that there are going to be other valid IDs close to their own that they can spam with unwanted messages.Will: I wholly agree that a sequence is guessable by its' very nature... why use a sequence tho?
jerryjvl
Jerry: Because its easy. That's pretty much it.
Will
A: 

I think it depends on how you define the "uniqueness" of the clients.

For example if you have different two clients from the same machine do you consider them two clients or one?

I recommend you to use long value represents the time of connection establishment like "hhmmss" or even you can include milliseconds

Ahmed Said
once client per socket/TCP connection/etc. Using time is even more iffy, what if someone connects tomorrow at the exact same time (not that unlikely?)
Nefzen
use yyyyMMddhhmmss
Ahmed Said
you know, using only 2 year digits, that *just* makes it into an int32.
Nefzen
+1  A: 

I suggest the combination of your approach and incremental.

Since your data is in memory, it is enough to have the identifier of type int. Make a variable for the next user and a linked list of free identifiers.

When new user is added, use an Id from the list. If the list is empty — use the variable and increment it.

When a user is removed, add its identifier to the Dictionary.

P.S. Consider using a database.

modosansreves
+1  A: 

First of all, I'd also start by seconding the GUID suggestion. Secondly, I'd assume that you're persisting the user information on the server somehow, and that somehow is likely a database. If this is the case, why not let the database pick a unique ID for each user via a primary key? Maybe it's not the best choice for what you're trying to do here, but this is the kind of problem that databases have been handling for years, so, why re-invent?

goheen
A: 

Why not just start from 1 and count upwards?

lock(dict) {
   int newId = dict.Count + 1;
   dict[newId] = new User();
}

If you're really concerned about half the worlds population turning up at your one server, try using long:s instead.. :-D

Per Erik Stendahl
A: 

Maybe a bit brutal, but could DateTime.Now.Ticks be something for you? As an added bonus, you know when the user was added to your dict.

From the MSDN docs on Ticks...

A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.

The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001, which represents DateTime..::.MinValue.

flq