[Sockets] Select für mehr als 64 Sockets



  • Hallo

    ich möchte, wie der Titel schon sagt, Select auf mehrere Sockets anwenden.

    Da der Code unter Realbedingungen schwer zu testen ist, wollte ich einfach nur
    mal ein kurzes Feedback ob das so richtig ist vom Prinzip her.

    fd_set *bigset = reinterpret_cast<fd_set *>(new char[sizeof(u_int) + sizeof(SOCKET) * numsockets]);
    
    FD_ZERO(bigset);
    
    #ifdef FD_SETSIZE
    #undef FD_SETSIZE
    #endif
    #define FD_SETSIZE numsockets
    
    for (unsigned i = 0; i < numsockets; ++i)
    	FD_SET(sockets[i], bigset);
    
    timeval tv;
    tv.tv_usec = (milliseconds % 1000) * 1000;
    tv.tv_sec = milliseconds / 1000;
    
    select(0, bigset, 0, 0, &tv);
    
    // aufräumcode gekürzt
    

    ob das standartkonform ist, ist mir egal es muss nur unter windows xp
    laufen 🙂



  • eher andersrum
    du darfst definieren und die winsock2.h macht

    #ifndef FD_SETSIZE
    #define FD_SETSIZE	64
    #endif
    

    du mußt das als definieren, bevor du die winsock2.h inkludierst oder irgendeine *.h, die sie inkludiert.



  • @volkard:
    Was ich weiss muss man nur die Struktur richtig zusammenbauen.
    Also dass als erstes ein u_int mit N kommt, und dann halt die SOCKETs.

    @steckverbindung:
    Bei x64 wird's deinen Code vermutlich aufstellen, weil du das Structure-Alignment/Padding nicht berücksichtigst.
    u_int ist auf x64 AFAIK weiterhin 4 Byte, aber SOCKET (UINT_PTR) ist 8 Byte. Default Packing vom VS is 8, d.h. VS wird zwischen dem u_int und dem SOCKET Array 4 Byte Padding reinknallen (damit das Array bei Offset 8 anfängt).

    Die ganz einfache Variante wäre IMO sowas in der Art:

    std::vector<SOCKET> my_fd_set;
    // evtl.: my_fd_set.reserve(...);
    my_fd_set.push_back(0); // erstmal dummy für fd_count
    
    // SOCKETs reinstecken
    for (...)
        my_fd_set.push_back(...);
    
    my_fd_set[0] = my_fd_set.size() - 1; // fixup fd_count
    
    timeval tv;
    tv.tv_usec = (milliseconds % 1000) * 1000;
    tv.tv_sec = milliseconds / 1000;
    
    int rc = select(0, reinterpret_cast<fd_set*>(&(my_fd_set[0]), 0, 0, &tv);
    

    Das sollte mit x86 und x64 funktionieren, und ist IMO etwas schöner.

    Oder man kapselt gleich die ganzen hässlichen Details in einer Klasse weg:

    class big_fd_set
    {
    public:
    	static size_t const npos = ~size_t(0);
    
    	explicit big_fd_set(size_t reservation = 500)
    	{
    		m_vec.reserve(reservation + 1);
    		m_vec.push_back(0); // dummy fd_count
    	}
    
    	void clear()
    	{
    		m_vec.clear();
    		m_vec.push_back(0); // dummy fd_count
    	}
    
    	size_t count() const
    	{
    		assert(m_vec.size() >= 1);
    		return m_vec.size() - 1;
    	}
    
    	size_t index_of(SOCKET s) const
    	{
    		std::vector<SOCKET>::iterator const it = find_it(s);
    		if (it != m_vec.end())
    			return std::distance(m_vec.begin() + 1, it);
    		else
    			return npos;
    	}
    
    	bool contains(SOCKET s) const
    	{
    		return index_of(s) != npos;
    	}
    
    	bool add(SOCKET s)
    	{
    		if (!contains(s))
    		{
    			m_vec.push_back(s);
    			return true;
    		}
    		else
    			return false;
    	}
    
    	bool remove(SOCKET s)
    	{
    		std::vector<SOCKET>::iterator const it = find_it(s);
    		if (it != m_vec.end())
    		{
    			m_vec.erase(it);
    			return true;
    		}
    		else
    			return false;
    	}
    
    	fd_set* get_fd_set()
    	{
    		assert(m_vec.size() >= 1);
    		m_vec[0] = m_vec.size() - 1; // fix-up fd_count
    		return reinterpret_cast<fd_set*>(&(m_vec[0]));
    	}
    
    	operator fd_set* ()
    	{
    		return get_fd_set();
    	}
    
    private:
    	std::vector<SOCKET>::iterator find_it(SOCKET s) const
    	{
    		assert(m_vec.size() >= 1);
    		return std::find(m_vec.begin() + 1 /* skip fd_count */, m_vec.end(), s);
    	}
    
    	std::vector<SOCKET> mutable m_vec;
    };
    

    (ungetestet, aber müsste IMO funktionieren)



  • @volkard

    ich dachte mir, dass ich die winsock includiere und die FD_SETSIZE auf 64 setzt.
    dann wird meine funktion kompiliert, setz FD_SETSIZE auf numsockets, was zur
    folge hat, dass die makros FD_irgentwas mit "numsockets" arbeiten.

    @hustbaer

    vielen dank für diese klasse, ich denke das kann ich so übernehmen 🙂

    @allgemeinheit

    wenn ich aus code in eine 32-bit DLL erzeuge, wird die auf einem 64-bit OS
    gegen eine 32 oder 64 bit wsock.dll gelinkt? (zur laufzeit)

    da gibts doch bestimmt einen kompatiblitätsmodus für 32 bit?


Log in to reply