News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Network programming

Started by Grincheux, March 17, 2011, 06:15:06 AM

Previous topic - Next topic

Grincheux

I have to write a server and a client programs. Not very difficult for a home only usage.
The goal is that every one form distant computers must be able to connect to the server.
Using DynDns.org I got an IP address and a domaind name.
When connecting to the server the client application must use the 23456 port number.
I modified my router to accept incoming connections on that port and redirect it to my IP address 192.168.0.24

What my program actually does :
It resolves the domain name "grincheux.dyndns.biz" and gets the corresponding IP adress.
When I try to bind the created socket to that IP adress on the 23456 port, I get and error.
Quote;   ********************************************
;   *** Obtention de l'adresse IP du serveur ***
;   ********************************************

                     INVOKE   getaddrinfo,__lpszServerName,NULL,NULL,ADDR _lpResult

                     test   eax,eax
                     jnz      @Error_2

                     push   edi
                     push   esi

                     mov      esi,_lpResult
                     mov      edi,OFFSET ServerAddress
                     mov      ecx,(SIZEOF ServerAddress) / (SIZEOF DWord)
                     rep      movsd

                     mov      eax,ServerAddress.ai_addr
                     mov      eax,(sockaddr_in Ptr [eax]).sin_addr
                     mov      _Service.sin_addr,eax

                     INVOKE   freeaddrinfo,_lpResult

                     mov      edi,OFFSET ServerAddress
                     mov      eax,(addrinfo Ptr [edi]).ai_addr
                     mov      lpSockaddr_Ip,eax
                     mov      _dwIpBufferLength,46

                     INVOKE   WSAAddressToString,lpSockaddr_Ip,(addrinfo Ptr [edi]).ai_addrlen,NULL,ADDR szServerIp,ADDR _dwIpBufferLength

                     INVOKE   wsprintf,ADDR _szTmp,ADDR szFmtResult_01,__lpszServerName,ADDR szServerIp

                     pop      esi
                     pop      edi

                     INVOKE   MessageBox,NULL,ADDR _szTmp,ADDR szInfo,MB_OK or MB_APPLMODAL or MB_ICONINFORMATION

;   ******************************************
;   *** Création du socket pour le serveur ***
;   ******************************************

                     INVOKE   socket,AF_INET,SOCK_STREAM,IPPROTO_TCP

                     test   eax,eax
                     jz      @Error_3

                     mov      hServer,eax

                     mov      _Service.sin_family,AF_INET
                     INVOKE   htons,23456
                     mov      _Service.sin_port,ax

                     INVOKE   bind,hServer,ADDR _Service,SIZEOF sockaddr_in

I created a domain name because it is more easier to connect and I expect my program to be installed on many computers as a server or as a station. Using this, everyone can have his own server name. The fix IP address is used because every day the router changes its IP too. Resolving the domain name always gives the same IP address which can be used to connect to my local computer.

Has anyone any idea to do this ?

Thanks for your help. :boohoo:

Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Grincheux

In the following code (first post) there is a bug. The line with "INVOKE   freeaddrinfo,_lpResult" must be removed or put later in the source code. But this is not the problem. When binding, I get the following error :

QuoteWSAEADDRNOTAVAIL   10049   Cannot assign requested address.
The requested address is not valid in its context. This normally results from an attempt to bind to an address that is not valid for the local computer.
This can also result from connect, sendto, WSAConnect, WSAJoinLeaf, or WSASendTo when the remote address or port is not valid for a remote computer
(for example, address or port 0).

Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

georgek01

My sever/client app works like a charm so here is my code. I hope it helps.

Server Socket Code

DoSocket proc

; initialize winsock

mov color,00000ffh

mov hSocket,NULL

invoke SetWindowText,statusWin,addr szError

invoke SetStatus,1,0
invoke WSAStartup, 202h,addr wsaData
.if eax != NULL
mov color,00h
invoke WSAGetLastError
invoke SetStatus,101,eax
xor eax, eax
ret
.endif

; create socket

invoke SetStatus,2,0
invoke socket, AF_INET, SOCK_STREAM, 0
.IF eax == INVALID_SOCKET
mov color,00000ffh
invoke WSAGetLastError
invoke SetStatus,201,eax
invoke WSACleanup
xor eax, eax
ret
.else
mov hSocket, eax
.ENDIF

invoke SetStatus,3,0
invoke WSAAsyncSelect,hSocket,hWin,WM_SOCKET,FD_ACCEPT + FD_CONNECT + FD_READ + FD_WRITE + FD_CLOSE
.if eax == SOCKET_ERROR
mov color,00000ffh
invoke WSAGetLastError
invoke SetStatus,301,eax
invoke WSACleanup
xor eax, eax
ret
.endif

; bind and listen

invoke SetStatus,4,0
mov service.sin_family, AF_INET
mov service.sin_addr, INADDR_ANY

mov eax,dwPort
invoke htons, 1024
mov service.sin_port, ax

invoke bind, hSocket, addr service, SIZEOF service
.if eax == SOCKET_ERROR
mov color,00000ffh
invoke WSAGetLastError
invoke SetStatus,401,eax
invoke WSACleanup
xor eax, eax
ret
.endif

invoke SetStatus,5,0
invoke listen,hSocket,10
.if eax == SOCKET_ERROR
mov color,00000ffh
invoke WSAGetLastError
invoke SetStatus,501,eax
invoke WSACleanup
xor eax, eax
ret
.endif

mov color,033d9cdh

invoke SetWindowText,statusWin,addr szIdle

xor eax,eax
Ret
DoSocket EndP


Client Socket Code

DoSocket proc

; initialize winsock

mov hSocket,NULL

invoke SetStatus,1,0
invoke WSAStartup, 101h,addr wsaData
.if eax != NULL
invoke SetStatus,101,0
xor eax, eax
ret
.endif

; create socket

invoke SetStatus,2,0
invoke socket, AF_INET, SOCK_STREAM, 0
.IF eax == INVALID_SOCKET
invoke SetStatus,201,0
invoke WSACleanup
xor eax, eax
ret
.else
mov hSocket, eax
.ENDIF

invoke SetStatus,3,0
invoke WSAAsyncSelect,hSocket,hWin,WM_SOCKET,FD_ACCEPT + FD_CONNECT + FD_READ + FD_WRITE + FD_CLOSE
.if eax == SOCKET_ERROR
invoke SetStatus,301,0
invoke WSACleanup
xor eax, eax
ret
.endif

; connect

mov service.sin_family, AF_INET
invoke htons, 1024                   
mov service.sin_port,ax                   
invoke inet_addr, addr ipAddress    ; convert the IP address into network byte order
mov service.sin_addr,eax

invoke SetStatus,4,0
invoke connect,hSocket,addr service,sizeof service
.if eax == SOCKET_ERROR
invoke WSAGetLastError
.if eax != WSAEWOULDBLOCK       
invoke SetStatus,401,eax
invoke WSACleanup
xor eax, eax
ret
.else
; ready to go
.endif
.endif


xor eax,eax
Ret
DoSocket EndP

What we have to learn to do, we learn by doing.

- ARISTOTLE (384-322 BC)

drizz

You can't bind socket to router resource ( the address that grincheux.dyndns.biz resolves to ).
Use 0.0.0.0:port with port forwarded to 192.168.0.24 on router should work.
The truth cannot be learned ... it can only be recognized.

Grincheux

Thanks George, now it works, the only diffrence was
Quotemov service.sin_addr, INADDR_ANY
in the server.. I made this change and know it works... on my pc. I finish the server and the station (FD_CONNECT / FD_READ...) and send it to friends so far that I think that it they connect the problem will be solved. For testing, I will send them a file from my PC.

If I test at home, I think it will be ok, on my pc and on the children's computer. I don't like that kind of test.

Bye & Thanks
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Grincheux

QuoteUse 0.0.0.0:port with port forwarded to 192.168.0.24 on router should work

That means that everyone that tries to connect at 0.0.0.0, from everywhere in the world, using port 23456 could connect to my server at 192.168.x.x ?
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Grincheux

I think that using MSMQ with such MQCreateQueue functions could help me. I never used it. Does anyone has any sample ont Message Queuing .
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Tedd

You need to understand the separation caused by your router.

192.168.x.x is the ip-address INSIDE the safe domain of your network. No-one from the scary outside world (the internet) sees that. Your dyndns ip-address is the only place others can connect to 'you.'

When you open a socket, you make a connection to your router - YOUR ip-address is 192.168.x.x. When the router receives your connection, it forwards that (depending on settings) to the internet (ITS ip-address is the external dyndns one.) When it receives any data for that connection, that is forwarded to you. You are never really connected to the internet.

Your computers only ever have 192.168.x.x ip-addresses. For someone to connect to your computer, they must connect to the router. The router must then forward that connection to you.
To run a server that others can connect to, bind your socket to "0.0.0.0" and then set it to 'listen.' You will also need to set your router to forward connections from the internet on port 23456 to your computer.
Then, when someone connects to "grincheux.dyndns.biz" on port 23456, the router will receive the connection. It will check the rules, and then forward the connection to your computer. Then you can use the received connection as normal (as if it were a direct one.)
No snowflake in an avalanche feels responsible.

Grincheux

Thanks, it seems clearer now.
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Grincheux

For Tedd
QuoteSERVER CODE

InitLan                  PROC   __hWnd:HWND,__lpszServerName:LPSTR
                     LOCAL   _WsaData:WSADATA
                     LOCAL   _Service:sockaddr_in
                     LOCAL   _lpHostEnt:Ptr hostent

;   *********************************
;   *** Initialisation de WinSock ***
;   *********************************

                     INVOKE   WSAStartup,202h,ADDR _WsaData

                     test   eax,eax
                     jnz      @Error_1

                     INVOKE   gethostname,ADDR szServerName,SIZEOF szServerName

                     INVOKE   htons,F32_PORT

                     mov      _Service.sin_port,ax
                     mov      _Service.sin_family,AF_INET

;   ********************************************
;   *** Obtention de l'adresse IP du serveur ***
;   ********************************************

                     INVOKE   gethostbyname,ADDR szServerName

                     test   eax,eax
                     jz      @Error_2

                     mov      eax,(hostent Ptr [eax]).h_list
                     mov      eax,DWord Ptr [eax]
                     mov      eax,DWord Ptr [eax]
                     mov      dwServerIpAddress,eax
                     mov      _Service.sin_addr,eax

;   ******************************************
;   *** Création du socket pour le serveur ***
;   ******************************************

                     INVOKE   WSASocket,AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,NULL,WSA_FLAG_OVERLAPPED

                     test   eax,eax
                     jz      @Error_3

                     mov      hServer,eax

                     INVOKE   WSAAsyncSelect,eax,__hWnd,WM_SOCKET,FD_CONNECT or FD_ACCEPT or FD_READ or FD_WRITE or FD_CLOSE or FD_CONNECT

                     INVOKE   bind,hServer,ADDR _Service,SIZEOF sockaddr_in

                     cmp      eax,SOCKET_ERROR
                     je      @Error_5

                     INVOKE   listen,hServer,SOMAXCONN

                     cmp      eax,SOCKET_ERROR
                     je      @Error_6

                     INVOKE   Log,ADDR szLog_0001

@Exit :

                     mov      eax,TRUE
                     ret

;   ****************************************
;   *** Impossible d'initialiser WinSock ***
;   ****************************************

                     ALIGN   16

@Error_1 :

                     mov      edx,OFFSET szError_1
                     jmp      @Error

;   *****************************************************
;   *** Impossible d'obtenir des infos sur le serveur ***
;   *****************************************************

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_2 :

                     mov      edx,OFFSET szError_2
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_3 :

                     mov      edx,OFFSET szError_3
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_4 :

                     mov      edx,OFFSET szError_4
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_5 :

                     call   WSAGetLastError

                     cmp      eax,WSAEWOULDBLOCK
                     je      @Exit

                     mov      edx,OFFSET szError_5
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_6 :

                     mov      edx,OFFSET szError_6
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error :

                     push   MB_OK or MB_APPLMODAL or MB_ICONINFORMATION
                     push   OFFSET szError
                     push   edx
                     push   __hWnd

                     INVOKE   MessageBeep,MB_ICONEXCLAMATION

                     call   MessageBox
                     call   WSACleanup

                     xor      eax,eax
                     ret
InitLan                  ENDP


I don't bind on "0.0.0.0" and it works fine. I can connect from a customer's pc at his home to my pc. The most difficult part is too understand that there is a loopback problem so I can't test the server and the station onto the same computer as I would do for testing a home network.
QuoteSTATION CODE

                     jnz      @Error_1

;   ********************************************
;   *** Obtention de l'adresse IP du station ***
;   ********************************************

                     INVOKE   htons,F32_PORT

                     mov      _Service.sin_port,ax

                     INVOKE   gethostbyname,__lpszServerName

                     test   eax,eax
                     jz      @Error_2

                     mov      eax,(hostent Ptr [eax]).h_list
                     mov      eax,DWord Ptr [eax]
                     mov      eax,DWord Ptr [eax]
                     mov      dwStationIpAddress,eax
                     mov      _Service.sin_addr,eax
                     mov      _Service.sin_family,AF_INET

;   ******************************************
;   *** Création du socket pour la station ***
;   ******************************************

                     INVOKE   WSASocket,AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED

                     test   eax,eax
                     jz      @Error_3

                     mov      hStation,eax

                     INVOKE   wsprintf,_lpszTmp,ADDR szLog_0000,eax
                     INVOKE   Log,_lpszTmp

                     INVOKE   WSAAsyncSelect,hStation,__hWnd,WM_SOCKET,FD_ACCEPT or FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE
                     INVOKE   WSAConnect,hStation,ADDR _Service,SIZEOF sockaddr_in,ADDR WsaBuffer_Caller,ADDR WsaBuffer_Callee,NULL,NULL

                     cmp      eax,SOCKET_ERROR
                     je      @Error_5

@Exit :

                     INVOKE   wsprintf,_lpszTmp,ADDR szLog_0001,hStation
                     INVOKE   Log,_lpszTmp

                     mov      eax,TRUE
                     ret

;   ****************************************
;   *** Impossible d'initialiser WinSock ***
;   ****************************************

                     ALIGN   16

@Error_1 :

                     mov      edx,OFFSET szError_1
                     jmp      @Error

;   *****************************************************
;   *** Impossible d'obtenir des infos sur le serveur ***
;   *****************************************************

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_2 :

                     mov      edx,OFFSET szError_2
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_3 :

                     mov      edx,OFFSET szError_3
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_4 :

                     INVOKE   freeaddrinfo,_lpResult

                     mov      edx,OFFSET szError_4
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error_5 :

                     call   WSAGetLastError

                     cmp      eax,WSAEWOULDBLOCK
                     je      @Exit

                     mov      edx,OFFSET szError_5
                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

;@Error_6 :

;                     mov      edx,OFFSET szError_6
;                     jmp      @Error

;-----------------------------------------------------------------
                     ALIGN   16
;-----------------------------------------------------------------

@Error :

                     push   MB_OK or MB_APPLMODAL or MB_ICONINFORMATION
                     push   OFFSET szError
                     push   edx
                     push   __hWnd

                     INVOKE   Log_Errors,edx

                     INVOKE   MessageBeep,MB_ICONEXCLAMATION

                     call   MessageBox
                     call   WSACleanup

                     xor      eax,eax
                     ret
InitLan                  ENDP

"grincheux.dyndns.biz" is the "__lpszServerName" parameter.
Now, I have all the necessary code for building my PC to PC forum.

The only thing I have a problem is for listenning on many ports at the same time. WinSocks permits it when using at least Vista, but generally my members use XP. SO for the moment I don't know how to do.

Thanks for your help.


Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Tedd

#10
Is there a reason you need to listen on multiple ports? The point of a port is to have a well-defined 'location' to connect to for a given service. Different ports should provide different services - so they should, naturally, have different sockets associated with them.

For multiple users connecting to your service, they all connect to the same port. When a connection is accepted, another NEW socket is created for communication between your server and the connecting client. The original listening socket stays as it was and goes back to listening for future incoming connections. So there is no problem with multiple clients connecting to the 'same' port, because they are still separate connections originating from different ip-addresses (and ports.)

Alternatively, if you really do wish to provide multiple services, via multiple ports, there shouldn't be much issue to continue as you are with WSAAsyncSelect - since you only receive notification upon an event. However, if you're really concerned about efficiency, you can monitor multiple sockets using a call to select. Though this will increase the complexity of the code you have to write, and you will probably have to avoid using WSAAsyncSelect on the same sockets.
No snowflake in an avalanche feels responsible.

Grincheux

I was afraid that my server could not accept all the incoming connections, so I thought that listening on multiple ports could be an answer. In that case I would have to create many listening sockets. But is it the real answer. I will study your las post, I learnt many things with it.

Thanks.
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

oex

These ports arent 'physical ports' so you can listen to all traffic on one port.... You might have multiple connections with multiple *sockets*.... You would retrieve a header on each initial connection maybe to identify which connection is which and then all data on that socket would be the same user....

ie as an example when you access the web traffic to http:// is generally all port 80 even if you are accessing 2 webpages at the same time in different browser windows....
We are all of us insane, just to varying degrees and intelligently balanced through networking

http://www.hereford.tv

Grincheux

Listenning on multiple ports could be a solution if I had a large number of users, but I don't think my program will be a facebook like !
I made many tests, the connexion is ok from every place I tried.
Now, i am thinking about sending and receiving datas. I would like to use a thread for sending and an other for receiving. I want to do like this because, I could get two users that transmit nearly at the same time. If the receiving function is not stored into a thread I can't get the second ?
One thing, I am thinking about is how to check that a connection is lost. Do I have to create a timer for sending dummy datas to each connected user ? Do I have to wait that I receive from or I have to send to ?
After I resolve these points there is no problem for the rest of the program.
Thanks for your help.
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Tedd

#14
oex, I'm afraid you're only confusing things further ::) You should read this too.


I did explain this point, because I know it's something that's often missed or skipped over. But let's try again..

So, let's assume you have 3 people connecting to your server all at the exact same second. What happens?
All 3 incoming connections are on the same port, and are queued by winsock.
Since you're using WSAAsyncSelect, you receive a notification for the 'first' connection (whichever happens to be randomly queued first - obviously one of them must be.)
Your handler responds to that notification by calling accept for the connection. At this point something magical happens - A NEW SOCKET is created. This is a completely separate and new socket - not the same one used for listening on. Any data you send or receive on that socket is purely between this server and that particular client.
Once you've finished dealing with that, you will inevitably receive yet another incoming connection notification, since there are 2 more connections waiting in the queue. So the same thing happens again - new sockets are created - until the queue is empty.

Now, by the end of this, you have the following: 1 original socket listening on your service port. 1 socket connected to client 1 from some free user port. 1 socket connected to client 2 from another free user port. And yet another socket connected to client 3 from a completely different free user port.
Your listening socket is still listening and ready for any incoming connections. And your other 3 sockets are all connected to their individual clients, separately, without interfering with each other or the listening port. And they all use different outgoing ports for communication, even though they connected to the same incoming port.
No snowflake in an avalanche feels responsible.