Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Sockets and TCP

Seto

Member

Hello,

I have been attempting to recreate the basic TCP/IP host functions that ruby has for rmxp (Just the very basic functions)
I am using the win 32 module
Code:
 

#===============================================================================

# ** Module Win32 - Handles numerical based data.

#-------------------------------------------------------------------------------

# Author    Ruby

# Version   1.8.1

#===============================================================================

 

 

module Win32

 

  #--------------------------------------------------------------------------

  # ● Retrieves data from a pointer.

  #--------------------------------------------------------------------------

  def copymem(len)

    buf = "\0" * len

    Win32API.new("kernel32", "RtlMoveMemory", "ppl", "").call(buf, self, len)

    buf

  end

 

end

 

# Extends the numeric class.

class Numeric

  include Win32

end

 

# Extends the string class.

class String

  include Win32

end

 

The winsock module
Code:
 

#===============================================================================

# ** Module Winsock - Maps out the functions held in the Winsock DLL.

#-------------------------------------------------------------------------------

# Author    Ruby

# Version   1.8.1

#===============================================================================

 

module Winsock

 

  DLL = "ws2_32"

 

  #--------------------------------------------------------------------------

  # * Accept Connection

  #--------------------------------------------------------------------------

  def self.accept(*args)

    Win32API.new(DLL, "accept", "ppl", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Bind

  #--------------------------------------------------------------------------

  def self.bind(*args)

    Win32API.new(DLL, "bind", "ppl", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Close Socket

  #--------------------------------------------------------------------------

  def self.closesocket(*args)

    Win32API.new(DLL, "closesocket", "p", "l").call(*args)

  end  

 

  #--------------------------------------------------------------------------

  # * Connect

  #--------------------------------------------------------------------------

  def self.connect(*args)

    Win32API.new(DLL, "connect", "ppl", "l").call(*args)

  end    

 

  #--------------------------------------------------------------------------

  # * Get host (Using Adress)

  #--------------------------------------------------------------------------

  def self.gethostbyaddr(*args)

    Win32API.new(DLL, "gethostbyaddr", "pll", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Get host (Using Name)

  #--------------------------------------------------------------------------

  def self.gethostbyname(*args)

    Win32API.new(DLL, "gethostbyname", "p", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Get host's Name

  #--------------------------------------------------------------------------

  def self.gethostname(*args)

    Win32API.new(DLL, "gethostname", "pl", "").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Get Server (Using Name)

  #--------------------------------------------------------------------------

  def self.getservbyname(*args)

    Win32API.new(DLL, "getservbyname", "pp", "p").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * HT OnL

  #--------------------------------------------------------------------------

  def self.htonl(*args)

    Win32API.new(DLL, "htonl", "l", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * HT OnS

  #--------------------------------------------------------------------------

  def self.htons(*args)

    Win32API.new(DLL, "htons", "l", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Inet Adress

  #--------------------------------------------------------------------------

  def self.inet_addr(*args)

    Win32API.new(DLL, "inet_addr", "p", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Inet NtOA

  #--------------------------------------------------------------------------

  def self.inet_ntoa(*args)

    Win32API.new(DLL, "inet_ntoa", "l", "p").call(*args)

  end  

 

  #--------------------------------------------------------------------------

  # * Listen

  #--------------------------------------------------------------------------

  def self.listen(*args)

    Win32API.new(DLL, "listen", "pl", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Recieve

  #--------------------------------------------------------------------------

  def self.recv(*args)

    Win32API.new(DLL, "recv", "ppll", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Select

  #--------------------------------------------------------------------------

  def self.select(*args)

    Win32API.new(DLL, "select", "lpppp", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Send

  #--------------------------------------------------------------------------

  def self.send(*args)

    Win32API.new(DLL, "send", "ppll", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Set Socket Options

  #--------------------------------------------------------------------------

  def self.setsockopt(*args)

    Win32API.new(DLL, "setsockopt", "pllpl", "l").call(*args)

  end  

 

  #--------------------------------------------------------------------------

  # * Shutdown

  #--------------------------------------------------------------------------

  def self.shutdown(*args)

    Win32API.new(DLL, "shutdown", "pl", "l").call(*args)

  end

 

  #--------------------------------------------------------------------------

  # * Socket

  #--------------------------------------------------------------------------

  def self.socket(*args)

    Win32API.new(DLL, "socket", "lll", "l").call(*args)  

  end

 

  #--------------------------------------------------------------------------

  # * Get Last Error

  #--------------------------------------------------------------------------

  def self.WSAGetLastError(*args)

    Win32API.new(DLL, "WSAGetLastError", "", "l").call(*args)

  end

 

end

 

The Socket class
Code:
 

#===============================================================================

# ** Socket - Creates and manages sockets.

#-------------------------------------------------------------------------------

# Author    Ruby

# Version   1.8.1

#===============================================================================

 

class Socket

 

  #--------------------------------------------------------------------------

  # ● Constants

  #--------------------------------------------------------------------------

  AF_UNSPEC                 = 0  

  AF_UNIX                   = 1

  AF_INET                   = 2

  AF_IPX                    = 6

  AF_APPLETALK              = 16

 

  PF_UNSPEC                 = 0  

  PF_UNIX                   = 1

  PF_INET                   = 2

  PF_IPX                    = 6

  PF_APPLETALK              = 16

 

  SOCK_STREAM               = 1

  SOCK_DGRAM                = 2

  SOCK_RAW                  = 3

  SOCK_RDM                  = 4

  SOCK_SEQPACKET            = 5

 

  IPPROTO_IP                = 0

  IPPROTO_ICMP              = 1

  IPPROTO_IGMP              = 2

  IPPROTO_GGP               = 3

  IPPROTO_TCP               = 6

  IPPROTO_PUP               = 12

  IPPROTO_UDP               = 17

  IPPROTO_IDP               = 22

  IPPROTO_ND                = 77

  IPPROTO_RAW               = 255

  IPPROTO_MAX               = 256

 

  SOL_SOCKET                = 65535

 

  SO_DEBUG                  = 1

  SO_REUSEADDR              = 4

  SO_KEEPALIVE              = 8

  SO_DONTROUTE              = 16

  SO_BROADCAST              = 32

  SO_LINGER                 = 128

  SO_OOBINLINE              = 256

  SO_RCVLOWAT               = 4100

  SO_SNDTIMEO               = 4101

  SO_RCVTIMEO               = 4102

  SO_ERROR                  = 4103

  SO_TYPE                   = 4104

  SO_SNDBUF                 = 4097

  SO_RCVBUF                 = 4098

  SO_SNDLOWAT               = 4099

 

  TCP_NODELAY               =   1

 

  MSG_OOB                   = 1

  MSG_PEEK                  = 2

  MSG_DONTROUTE             = 4

 

  IP_OPTIONS                =   1

  IP_DEFAULT_MULTICAST_LOOP =   1

  IP_DEFAULT_MULTICAST_TTL  =   1

  IP_MULTICAST_IF           =   2

  IP_MULTICAST_TTL          =   3

  IP_MULTICAST_LOOP         =   4

  IP_ADD_MEMBERSHIP         =   5

  IP_DROP_MEMBERSHIP        =   6

  IP_TTL                    =   7

  IP_TOS                    =   8

  IP_MAX_MEMBERSHIPS        =   20

 

  EAI_ADDRFAMILY            = 1

  EAI_AGAIN                 = 2

  EAI_BADFLAGS              = 3

  EAI_FAIL                  = 4

  EAI_FAMILY                = 5

  EAI_MEMORY                = 6

  EAI_NODATA                = 7

  EAI_NONAME                = 8

  EAI_SERVICE               = 9

  EAI_SOCKTYPE              = 10

  EAI_SYSTEM                = 11

  EAI_BADHINTS              = 12

  EAI_PROTOCOL              = 13

  EAI_MAX                   = 14

 

  AI_PASSIVE                = 1

  AI_CANONNAME              = 2

  AI_NUMERICHOST            = 4

  AI_MASK                   = 7

  AI_ALL                    = 256

  AI_V4MAPPED_CFG           = 512

  AI_ADDRCONFIG             = 1024

  AI_DEFAULT                = 1536

  AI_V4MAPPED               = 2048

 

  #--------------------------------------------------------------------------

  # ● Returns the associated IP address for the given hostname.

  #--------------------------------------------------------------------------  

  def self.getaddress(host)

    gethostbyname(host)[3].unpack("C4").join(".")

  end

 

  #--------------------------------------------------------------------------

  # ● Returns the associated IP address for the given hostname.

  #--------------------------------------------------------------------------  

  def self.getservice(serv)

    case serv

    when Numeric

      return serv

    when String

      return getservbyname(serv)

    else

      raise "Please us an interger or string for services."

    end

  end

 

  #--------------------------------------------------------------------------

  # ● Returns information about the given hostname.

  #--------------------------------------------------------------------------

  def self.gethostbyname(name)

    raise SocketError::ENOASSOCHOST if (ptr = Winsock.gethostbyname(name)) == 0

    host = ptr.copymem(16).unpack("iissi")

    [host[0].copymem(64).split("\0")[0], [], host[2], host[4].copymem(4).unpack("l")[0].copymem(4)]

  end

 

  #--------------------------------------------------------------------------

  # ● Returns the user's hostname.

  #--------------------------------------------------------------------------  

  def self.gethostname

    buf = "\0" * 256

    Winsock.gethostname(buf, 256)

    buf.strip

  end

 

  #--------------------------------------------------------------------------

  # ● Returns information about the given service.

  #--------------------------------------------------------------------------

  def self.getservbyname(name)

    case name

    when /echo/i

      return 7

    when /daytime/i

      return 13

    when /ftp/i

      return 21

    when /telnet/i

      return 23

    when /smtp/i

      return 25

    when /time/i

      return 37

    when /http/i

      return 80

    when /pop/i

      return 110

    else

      raise "Service not recognized."

    end

  end

 

  #--------------------------------------------------------------------------

  # ● Creates an INET-sockaddr struct.

  #--------------------------------------------------------------------------  

  def self.sockaddr_in(port, host)

    begin

      [AF_INET, getservice(port)].pack("sn") + gethostbyname(host)[3] + [].pack("x8")

    rescue

      nil

    end

  end

 

  #--------------------------------------------------------------------------

  # ● Creates a new socket and connects it to the given host and port.

  #--------------------------------------------------------------------------  

  def self.open(*args)

    socket = new(*args)

    if block_given?

      begin

        yield socket

      ensure

        socket.close

      end

    end

    nil

  end

 

  #--------------------------------------------------------------------------

  # ● Creates a new socket.

  #--------------------------------------------------------------------------  

  def self.initialize(domain, type, protocol)

    SocketError.check if (@fd = Winsock.socket(domain, type, protocol)) == -1

    @fd

  end

 

  #--------------------------------------------------------------------------

  # ● Accepts incoming connections.

  #--------------------------------------------------------------------------  

  def accept(flags = 0)

    buf = "\0" * 16

    SocketError.check if Winsock.accept(@fd, buf, flags) == -1

    buf

  end

 

  #--------------------------------------------------------------------------

  # ● Binds a socket to the given sockaddr.

  #--------------------------------------------------------------------------  

  def self.bind(sockaddr)

    SocketError.check if (ret = Winsock.bind(@fd, sockaddr, sockaddr.size)) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Closes a socket.

  #--------------------------------------------------------------------------  

  def close

    SocketError.check if (ret = Winsock.closesocket(@fd)) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Connects a socket to the given sockaddr.

  #--------------------------------------------------------------------------  

  def connect(sockaddr)

    ret = Winsock.connect(@fd, sockaddr, sockaddr.size)

    SocketError.check if ret == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Listens for incoming connections.

  #--------------------------------------------------------------------------  

  def listen(backlog)

    SocketError.check if (ret = Winsock.listen(@fd, backlog)) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Checks waiting data's status.

  #--------------------------------------------------------------------------  

  def select(timeout)

    SocketError.check if (ret = Winsock.select(1, [1, @fd].pack("ll"), 0, 0, [timeout, timeout * 1000000].pack("ll"))) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Checks if data is waiting.

  #--------------------------------------------------------------------------  

  def ready?

    not select(0) == 0

  end  

 

  #--------------------------------------------------------------------------

  # ● Reads data from socket.

  #--------------------------------------------------------------------------  

  def read(len)

    buf = "\0" * len

    Win32API.new("msvcrt", "_read", "lpl", "l").call(@fd, buf, len)

    buf

  end

 

  #--------------------------------------------------------------------------

  # ● Returns recieved data.

  #--------------------------------------------------------------------------  

  def recv(len, flags = 0)

    buf = "\0" * len

    SocketError.check if Winsock.recv(@fd, buf, buf.size, flags) == -1

    buf

  end

 

  #--------------------------------------------------------------------------

  # ● Sends data to a host.

  #--------------------------------------------------------------------------  

  def send(data, flags = 0)

    SocketError.check if (ret = Winsock.send(@fd, data, data.size, flags)) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # * Gets

  #--------------------------------------------------------------------------

  def gets

    # Create buffer

    buffer = ""

    # Loop Until "end of line"

    while (char = recv(1)) != "\n"

      buffer += char

    end

    # Return recieved data

    return buffer

  end

 

  #--------------------------------------------------------------------------

  # ● Writes data to socket.

  #--------------------------------------------------------------------------  

  def write(data)

    Win32API.new("msvcrt", "_write", "lpl", "l").call(@fd, data, 1)

  end

 

end

 

 

The TCP sub class for the Socket class
Code:
 

#===============================================================================

# ** TCPSocket - Creates and manages TCP sockets.

#-------------------------------------------------------------------------------

# Author    Ruby

# Version   1.8.1

#===============================================================================

 

class TCPSocket < Socket

 

  #--------------------------------------------------------------------------

  # ● Creates a new socket and connects it to the given host and port.

  #--------------------------------------------------------------------------  

  def self.open(*args)

    socket = new(*args)

    if block_given?

      begin

        yield socket

      ensure

        socket.close

      end

    end

    nil

  end

 

  #--------------------------------------------------------------------------

  # ● Creates a new socket and connects it to the given host and port.

  #--------------------------------------------------------------------------  

  def initialize(host, port)

    super(AF_INET, SOCK_STREAM, IPPROTO_TCP)

    connect([AF_INET, Socket.getservice(port), Socket.getaddress(host).split(".").collect { |b| b.to_i }].flatten.pack("snC4x8"))

  end

  

end

 

#==============================================================================

# ■ SocketError

#------------------------------------------------------------------------------

#  Default exception class for sockets.

#==============================================================================

class SocketError < StandardError

  ENOASSOCHOST = "getaddrinfo: no address associated with hostname."

  def self.check

    errno = Winsock.WSAGetLastError

    errno = Errno.constants.detect { |c| Errno.const_get(c).new.errno == errno }

    if errno != nil

      raise Errno.const_get(errno)

    end

  end

end

 

 

I created a class of my own called host and created the following method and attempted to create a socket that can be used for the host functions

The sockets family is PF_INET, Type is SOCK_STREAM and Protocol is IPPROTO_TCP
Then I attempted to create a sockaddr_in with the port 80 (Just to test) and the domain of AF_INET
Then I bind the socket to the sockaddr_in

However when I attempt to call this method the game crashes immediately and without any kind of error message.

Does any one have any experience with these scripts that could help me or tell me what I have done wrong?, I plan to continue working on the script after I have passed this bump in the road. If anyone would like to help me with the continued work or with this current problem please let me know, or if there is already a script out there that does what I am trying to do. Please don't post here saying things like "Why would you want your game to be able to host" because that is not what I am here for, I am here for help in writing a script. Also if this script is impossible to make and someone wants to post that kind of information that is fine ;).

Thank you to anyone who took the time to read this post, below is my attempt at creating the socket I need to begin the rest of the script.

Code:
 

class Host

  

  def self.create

   #creates a socket with the following family of PF_INET, Type of SOCK_STREAM and Protocol of IPPROTO_TCP

    Socket.initialize(2, 1, 6)

   # creates a sockaddr_in with the port 80 and the domain of AF_INET

    sockaddr_in = Socket.sockaddr_in(80, 2)

   # Binds to socket to the above sockaddr_in

    Socket.bind()#sockaddr_in)

  end

  

end

 

 

P.S feel free to PM me
 
Wouldn't it be more like:

[rgss] 
class Host
  def create
    hostname, port = '127.0.0.1', 80 # can also be 'localhost' or your IP.
    @socket = Socket.new(domain, type, protocol) # define the domain, type and protocol, but don't just use numbers, use Socket::SOCK_STREAM, and Socket::IPPROTO_TCP.
    address = @socket.sockaddr_in(port, hostname)
    @socket.connect(address)
  end
end
 
[/rgss]
 

Seto

Member

Thank you so much, that snippet pretty much did it, I had to edit it just a tiny bit though. I will post it in case anyone is following this, or looks at this and would like to help me continue. Your amazing Ulqiorra, I have see lots of your other work and I think your a really kind and helpful person. :D

Code:
 

class Host

   def self.create

     hostname, port = '127.0.0.1', 80 # can also be 'localhost' or your IP.

     domain = Socket::PF_INET

     type = Socket::SOCK_STREAM

     protocol = Socket::IPPROTO_TCP

     Socket.initialize(domain, type, protocol) # define the domain, type and protocol, but don't just use numbers, use Socket::SOCK_STREAM, and Socket::IPPROTO_TCP.

     address = Socket.sockaddr_in(port, hostname)

     Socket.bind(address)

   end

 end

 
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top