Winsock versus Berkeley Sockets on Windows

External libraries (GTK, GSL, SDL, Allegro, OpenGL, etc) questions.
Post Reply
Imortis
Moderator
Posts: 1923
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

Winsock versus Berkeley Sockets on Windows

Post by Imortis »

I see that you can use Berkeley Sockets on Windows, instead of Winsock. I know that both provide a very similar interface to sockets. Why should I use Winsock when Berkeley is basiclly the same and can ALSO work on Linux machines? Is there something I am missing?
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Winsock versus Berkeley Sockets on Windows

Post by badidea »

https://en.wikipedia.org/wiki/Berkeley_sockets:
Berkeley sockets is an application programming interface (API) for Internet sockets and Unix domain sockets, used for inter-process communication (IPC). It is commonly implemented as a library of linkable modules. It originated with the 4.2BSD Unix released in 1983.
All modern operating systems implement a version of the Berkeley or POSIX socket interface. It became the standard interface for connecting to the Internet. Even the Winsock implementation for MS Windows, developed by unaffiliated developers, closely follows the standard.
Imortis
Moderator
Posts: 1923
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by Imortis »

@badidea:
I knew all that. I did the same google search you did. The thing is, you can use Berkeley sockets directly on Windows as well as Linux. But Winsock is Windows only. I have looked at a lot of code from people writing Libraries to be used on both Windows and Linux, and they always use Winsock AND Berkeley sockets, then use defines to determine which one is used for which OS. Is there a reason to NOT just use Berkeley sockets and only have to code for the one?
St_W
Posts: 1619
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by St_W »

Imortis wrote:I see that you can use Berkeley Sockets on Windows, instead of Winsock.
Could you provide a link to some documentation of that API? For my part I've only ever heard about Winsock. As the APIs are really very similar there's not a lot of effort necessary to port from one system to another anyway.
Imortis
Moderator
Posts: 1923
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by Imortis »

St_W wrote:Could you provide a link to some documentation of that API? For my part I've only ever heard about Winsock. As the APIs are really very similar there's not a lot of effort necessary to port from one system to another anyway.
http://www.thefullwiki.org/Berkeley_sockets

That seems to be a pretty solid overview.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by D.J.Peters »

@Importis you can take a look at my include file snc.bi imple [N]etwork [C]onnection (if you like)
the only difference between Linux socket lib and Win socket are you have to init and clean up the the lib on Windows.

Joshy

file: snc.bi

Code: Select all

#ifndef __SNC_BI__
#define __SNC_BI__

' file: snc.bi [S]imple [N]etwork [C]onnection
' needs uptodate include files.
' see at: http://www.freebasic.net/forum/viewtopic.php?f=7&t=23421

#include "crt/mem.bi" ' memcpy

#ifdef __FB_WIN32__
 #include "windows.bi"
 #include "win/windef.bi"
 #include "win/winsock2.bi"

 sub NetworkInit constructor
   dim as WSAData wd
   WSAStartup(WINSOCK_VERSION, @wd)
 end sub

 sub Network_Exit() destructor
   WSACleanup()
 end sub

#else
 #include "crt/unistd.bi"     ' close_ ...
 #include "crt/netinet/in.bi" ' socket.bi ...
 #include "crt/sys/select.bi" ' FD_SET ...
 #include "crt/netdb.bi"      ' hostent ...
 #include "crt/arpa/inet.bi"  ' inet_ntoa ...
#endif

' User-settable options (used with setsockopt).
#ifndef TCP_NODELAY ' "tcp.bi" does not exists
 #define TCP_NODELAY &H01 ' don't delay send to coalesce packets
#endif

#ifdef __FB_WIN32__
# ifdef __FB_64BIT__
   type SNC_SOCKET as ulongint
   const as SNC_SOCKET SNC_SOCK_ERROR  = &HFFFFFFFFFFFFFFFF
# else
   type SNC_SOCKET as ulong
   const as SNC_SOCKET SNC_SOCK_ERROR  = &HFFFFFFFF
# endif
#elseif defined(__FB_LINUX__)
  type SNC_SOCKET as long
  const as SNC_SOCKET SNC_SOCK_ERROR  = &HFFFFFFFF
#else
# error 666: Target must be Windows or Linux !
#endif

enum NETWORK_ERROR
  ERROR_NO_ERROR = 0
  ' connection
  ERROR_DISCONNECTED
  ERROR_PUT
  ERROR_GET
  ' socket
  ERROR_SOCKET
  ERROR_SETSOCKOPT
  ERROR_SELECT
  ' client
  ERROR_CONNECT
  ERROR_RESOLVE
  ' server
  ERROR_BIND
  ERROR_LISTEN
  ERROR_ACCEPT
end enum

function GetNetworkErrorText(byval errorcode as NETWORK_ERROR) as string
  select case as const errorcode
  case ERROR_NO_ERROR     : return "no error !"
  ' connection
  case ERROR_PUT          : return "error: put data !" 
  case ERROR_GET          : return "error: get data !"
  case ERROR_DISCONNECTED : return "error: disconnected !"
  ' socket
  case ERROR_SOCKET       : return "error: socket !"
  case ERROR_SETSOCKOPT   : return "error: setsockopt !"
  case ERROR_SELECT       : return "error: select !"
  ' client
  case ERROR_CONNECT      : return "error: client connect !"
  case ERROR_RESOLVE      : return "error: client resolve IP"
  ' server
  case ERROR_BIND         : return "error: server bind() !"
  case ERROR_LISTEN       : return "error: server listen() !"
  case ERROR_ACCEPT       : return "error: server accept() !"
  case else               : return "error: " & errorcode & " unknow !"
  end select
end function

' A NetworkConnection has to be constructed with a server or a client.
' You can send and receive any data to/from your peer with it.
type NetworkConnection
  public:
  declare constructor (byval aSocket as SNC_SOCKET)
  declare destructor

  ' returns: 1 = you can send data
  ' returns: 0 = not ATM. try again
  ' returns:-1 = error
  declare function CanPut() as integer

  ' returns: 1 = you can get data
  ' returns: 0 = no data
  ' returns:-1 = error
  declare function CanGet() as integer

  ' Sends any data
  ' NOTE: send a string like this: PutData(strptr(txt),len(txt)+1)
  ' returns number of bytes sended
  ' returns -1 as error signal
  declare function PutData(byval pData as any ptr,byval dataSize as integer) as integer

  ' Receives any data (pData will be reallocated)
  ' returns number of reseived bytes 
  ' returns  0 if other connection are closed
  ' returns -1 as error signal
  declare function GetData(byref pData as any ptr,byval maxSize as integer=0) as integer

  declare function GetLastError as NETWORK_ERROR
  protected:
  as SNC_SOCKET sock 
  as any ptr pInData
  as fd_set read_fds, write_fds
  as timeval timeout
  as NETWORK_ERROR lastError
end type

constructor NetworkConnection(byval aSocket as SNC_SOCKET)
  sock = aSocket
  if sock = SNC_SOCK_ERROR then
    lasterror=ERROR_SOCKET
  else
   ' dim as long tmp = 1
   ' if setsockopt(this.sock, IPPROTO_TCP, TCP_NODELAY,cptr(const zstring ptr,@tmp), sizeof(long))=-1 then
   '   lasterror=ERROR_SETSOCKOPT
   ' end if
  end if
end constructor

function NetworkConnection.GetLastError as NETWORK_ERROR
  return lasterror
end function

destructor NetworkConnection
  if sock<>SNC_SOCK_ERROR then closesocket(sock)
end destructor

function NetworkConnection.CanPut as integer
  if sock=SNC_SOCK_ERROR then lasterror=ERROR_SOCKET:return -1
  FD_ZERO(@write_fds)
  FD_SET_(sock, @write_fds)
  if select_(sock + 1, 0, @write_fds, 0, @timeout) = -1 then 
    lasterror=ERROR_SELECT
    return -1
  end if
  return iif(FD_ISSET(sock, @write_fds),1,0)
end function

function NetworkConnection.PutData(byval pData as any ptr,byval dataSize as integer) as integer
  if sock=SNC_SOCK_ERROR then lasterror=ERROR_SOCKET:return -1
  if pData=0    then lasterror=ERROR_PUT:return -1
  if dataSize<1 then lasterror=ERROR_PUT:return -1
  dim as integer size,dSize=dataSize
  ' as long as not all data has been send
  while (size<dataSize)
    dim as integer nBytes = send(sock,pData,dSize, 0)
    if nBytes<0 then lasterror=ERROR_PUT:return -1
    pData+=nBytes:dSize-=nBytes
    size+=nBytes
  wend
  return size
end function

function NetworkConnection.CanGet as integer
  if sock=SNC_SOCK_ERROR then lasterror=ERROR_SOCKET:return -1
  FD_ZERO(@read_fds)
  FD_SET_(sock, @read_fds)
  if select_(sock + 1, @read_fds, 0, 0, @timeout) = -1 then 
    lasterror=ERROR_SELECT
    return -1
  end if
  return iif(FD_ISSET(sock, @read_fds),1,0)
end function

function NetworkConnection.GetData(byref pData as any ptr,byval maxSize as integer) as integer
  const CHUNK_SIZE=1024*8
  static as ubyte chunk(CHUNK_SIZE-1)
  if sock=SNC_SOCK_ERROR then 
    lasterror=ERROR_SOCKET
    return -1
  end if

  dim as integer dSize=0
  if pData then deallocate pData:pData=0
  do 
    dim as integer nBytes = recv(sock,@chunk(0),CHUNK_SIZE, 0)
    if nBytes=0 then 
      if dSize<maxSize then 
        lasterror=ERROR_DISCONNECTED
        return -1
      end if
      exit do
    end if 
    if nBytes<0 then 
      lasterror=ERROR_GET
      return -1
    end if
    pData=reallocate(pData,dSize+nBytes)
    dim as ubyte ptr pWrite=pData+dSize
    memcpy pWrite,@chunk(0),nBytes
    dSize+=nBytes
    if maxSize>0 andalso dSize>=maxSize then 
      lasterror=ERROR_NO_ERROR
      exit do
    end if
    dim as integer _timeout = 2000
    sleep 20
    while CanGet()<>1 andalso _timeout>0
      sleep 10:_timeout-=10
    wend
    if _timeout<=0 then
      lasterror=ERROR_DISCONNECTED
      exit do
    end if
  loop
  return dSize
end function

' constructing NetworkConnection pointers
type ConnectionFactoy extends object
  public:
  declare constructor
  declare virtual destructor
  ' return a connection If it returns NULL, there is no connection available.
  declare abstract function GetConnection() as NetworkConnection ptr
  declare function GetLastError as NETWORK_ERROR

  protected:
  as SNC_SOCKET sock
  as fd_set read_fd
  as sockaddr_in addr
  as NETWORK_ERROR lastError
end type

constructor ConnectionFactoy
  sock = opensocket(AF_INET, SOCK_STREAM, 0)
  if sock=SNC_SOCK_ERROR then lasterror=ERROR_SOCKET
end constructor

destructor ConnectionFactoy
  if sock<>SNC_SOCK_ERROR then
    closesocket(sock)
    sock=SNC_SOCK_ERROR
  end if
end destructor

function ConnectionFactoy.GetLastError as NETWORK_ERROR
  return lasterror
end function

' Constructs connection to server
type NetworkClient extends ConnectionFactoy
  public:
  ' Initialized with the server address and the destination port
  declare constructor (address as string,byval port as ushort)
  ' Returns a connection to the server
  declare function GetConnection() as NetworkConnection ptr
end type

'  connection_maker(port)
constructor NetworkClient(address as string,byval port as ushort)
  base()
  if port=SNC_SOCK_ERROR then 
    LastError = ERROR_SOCKET
    return
  end if
  ' server address
  dim as hostent ptr he = gethostbyname(strptr(address))
  if (he=0) then 
    LastError = ERROR_RESOLVE
    return
  end if
  addr.sin_family = AF_INET
  addr.sin_port = htons(port)
  addr.sin_addr = *cptr(in_addr ptr,he->h_addr_list[0])
  if connect(sock,cptr(sockaddr ptr,@addr), sizeof(sockaddr))=SNC_SOCK_ERROR then
    LastError = ERROR_CONNECT
  end if
end constructor

function NetworkClient.GetConnection() as NetworkConnection ptr
  return new NetworkConnection(sock)
end function

' Constructs connections to clients
type NetworkServer extends ConnectionFactoy
  public:
  ' Opens connection possibility on port for maxConnections clients
  declare constructor (byval port as ushort,byval maxConnections as long=64)
  ' Returns a connection to a connecting client
  declare function GetConnection() as NetworkConnection ptr
  dim as string ClientIP
  dim as ushort ClientPort
  private:
  as timeval timeout
end type

constructor NetworkServer(byval port as ushort,byval maxConnections as long)
  base()
  if sock=SNC_SOCK_ERROR then 
    LastError = ERROR_SOCKET
    return
  end if
  addr.sin_family = AF_INET
  addr.sin_port = htons(port)
  addr.sin_addr.s_addr = INADDR_ANY
  if bind(sock, cptr(sockaddr ptr,@addr), sizeof(sockaddr)) = SNC_SOCK_ERROR then
    lasterror=ERROR_BIND
  elseif listen(sock, maxConnections) = SNC_SOCK_ERROR then
    lasterror=ERROR_LISTEN
  end if
end constructor

function NetworkServer.GetConnection() as NetworkConnection ptr
  FD_ZERO(@read_fd)
  FD_SET_(sock, @read_fd)
  if select_(sock + 1, @read_fd, 0, 0, @timeout) = SNC_SOCK_ERROR then
    lasterror=ERROR_SELECT
    return 0
  end if

  if FD_ISSET(sock, @read_fd) = 0 then return 0

  dim as sockaddr_in ClientAddress
  dim as long size = sizeof(sockaddr_in)
  var clientsock = accept(sock, cptr(sockaddr ptr,@ClientAddress), @size)
  if clientsock=SNC_SOCK_ERROR then
    lasterror = ERROR_ACCEPT
    return 0
  end if
  dim as zstring ptr pIP = inet_ntoa(ClientAddress.sin_addr)
  if pIP then 
    ClientIP   = *pIP
    ClientPort = ntohs(ClientAddress.sin_port)
  else
    ClientIP   = ""
    ClientPort = 0
  end if
  return new NetworkConnection(clientsock)
end function

#endif '__SNC_BI__
St_W
Posts: 1619
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by St_W »

Imortis wrote:http://www.thefullwiki.org/Berkeley_sockets
That seems to be a pretty solid overview.
Sorry, I wasn't clear enough. I meant a documentation of Microsoft's implementation of that API. Because I only know the Winsock documentation on MSDN.
Imortis
Moderator
Posts: 1923
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by Imortis »

St_W wrote: Sorry, I wasn't clear enough. I meant a documentation of Microsoft's implementation of that API. Because I only know the Winsock documentation on MSDN.
As far as I know there in no Microsoft specific documentation. The headers are the only thing I know of:

Code: Select all

#Include "crt/sys/types.bi"
#Include "crt/sys/socket.bi"
#Include "crt/netinet/in.bi"
#Include "crt/unistd.bi"
#Include "crt/netdb.bi"
#Include "crt/arpa/inet.bi"
St_W
Posts: 1619
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by St_W »

Imortis wrote:As far as I know there in no Microsoft specific documentation. The headers are the only thing I know of:

Code: Select all

#Include "crt/sys/types.bi"
#Include "crt/sys/socket.bi"
#Include "crt/netinet/in.bi"
#Include "crt/unistd.bi"
#Include "crt/netdb.bi"
#Include "crt/arpa/inet.bi"
But these are just the Linux headers. They probably won't work on Windows - and if they do it's just coincidentally because so many methods do match between the two APIs. You won't find such headers e.g. in the Windows SDK.

In general, the FreeBasic headers for the CRT are a bit of a mess and some things are broken. They are basically the result of merging Windows and Linux and maybe some other platform's CRT headers converted to BASIC.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by D.J.Peters »

I use the Berkeley socket interface in snc.bi socket(), bind(), listen(), accept(), connect(), gethostbyname(), gethostbyaddr() ...
and it works for Windows/Linux 32/64-bit so what are the problem ?

Joshy
St_W
Posts: 1619
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by St_W »

D.J.Peters wrote:I use the Berkeley socket interface in snc.bi socket(), bind(), listen(), accept(), connect(), gethostbyname(), gethostbyaddr() ...
and it works for Windows/Linux 32/64-bit so what are the problem ?
No, you are using the Winsock API on Windows, which is in large parts very similar or identical to the Berkeley socket interface. But it's still Winsock. The relevant part in the code you posted above is the following:

Code: Select all

#ifdef __FB_WIN32__
 #include "windows.bi"
 #include "win/windef.bi"
 #include "win/winsock2.bi"
#else
 #include "crt/unistd.bi"     ' close_ ...
 #include "crt/netinet/in.bi" ' socket.bi ...
 #include "crt/sys/select.bi" ' FD_SET ...
 #include "crt/netdb.bi"      ' hostent ...
 #include "crt/arpa/inet.bi"  ' inet_ntoa ...
#endif
There's no problem, your code is totally fine. The issue seems to be rather a wrong use of certain terms IMHO. That's probably also the reason for the question in the first post.
Imortis
Moderator
Posts: 1923
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

Re: Winsock versus Berkeley Sockets on Windows

Post by Imortis »

St_W wrote:
D.J.Peters wrote:I use the Berkeley socket interface in snc.bi socket(), bind(), listen(), accept(), connect(), gethostbyname(), gethostbyaddr() ...
and it works for Windows/Linux 32/64-bit so what are the problem ?
No, you are using the Winsock API on Windows, which is in large parts very similar or identical to the Berkeley socket interface. But it's still Winsock. The relevant part in the code you posted above is the following:

Code: Select all

#ifdef __FB_WIN32__
 #include "windows.bi"
 #include "win/windef.bi"
 #include "win/winsock2.bi"
#else
 #include "crt/unistd.bi"     ' close_ ...
 #include "crt/netinet/in.bi" ' socket.bi ...
 #include "crt/sys/select.bi" ' FD_SET ...
 #include "crt/netdb.bi"      ' hostent ...
 #include "crt/arpa/inet.bi"  ' inet_ntoa ...
#endif
There's no problem, your code is totally fine. The issue seems to be rather a wrong use of certain terms IMHO. That's probably also the reason for the question in the first post.
Looks like you are correct. It is strange that there are "win32" specific headers from some of those files, but the actual socket.bi file says everything but Linux is unsupported.

I now understand my mistake.
Post Reply