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:
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).
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
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.
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
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 ?
I think that using MSMQ with such MQCreateQueue functions could help me. I never used it. Does anyone has any sample ont Message Queuing .
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.)
Thanks, it seems clearer now.
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.
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.
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.
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....
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.
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.
Quote from: Grincheux on March 26, 2011, 05:06:02 PM
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 ?
You will receive FD_READ each time a user sends something, with wParam indicating the particular socket to read this data from (and thus who sent it.) You can then deal with that and return. And then you'll receive more notifications for other incoming data on other sockets. There isn't really a problem with multiple incoming connections, unless you're streaming lots of data and need low latency.
Quote
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 ?
You receive FD_CLOSE when a client closes the connection or the connection is lost. You don't need to do anything special (except add it to the mask in WSAAsyncSelect.) When the connection is closed, there is no remaining incoming data to receive.
Quote from: Tedd on March 26, 2011, 10:48:38 PM
oex, I'm afraid you're only confusing things further ::) You should read this too.
Ah sorry, it's a very long time since I wrote any server code :lol
Thank you Tedd, a very good course.
FOr incoming connections now that's ok.
I want to test if the station already is connected, for the following reason. My job, is to repair phone lines. I repair when a tree is falling to the line cable, or when there are too many errors on the customer's line. When there is a lot of wind, the datas can be lost and the link between the router and the head of the line can have a poor quality. There are many technical reasons than make me think that the connection can be lost without receiving a FD_CLOSE event.
Some lines I have to repair have a lot of CRC errors (I use a MC2+ for testing lines) and other erros such as FEC, HEC...
I can also find capacitors that will drop the frequency of ADSL and lead a power connection.
Here are the reasons for which I wanted to send dummy datas between the server and the station.
What do you think about
Here is (http://www.jwb-environmental.com/web2/res.nsf/vlFileURLLookup/MC2Plus+Main+User+Guide+ENG+/$FILE/MC2Plus+Main+User+Guide.pdf) a pdf file giving informations about the MC2+ used for my job. The problems I meet during repairing ADSL user's problems, I want to include them into my programs and it seems to be a good idea for me.
Quote from: Grincheux on March 27, 2011, 06:48:57 AM
I want to test if the station already is connected, for the following reason. My job, is to repair phone lines. I repair when a tree is falling to the line cable, or when there are too many errors on the customer's line. When there is a lot of wind, the datas can be lost and the link between the router and the head of the line can have a poor quality. There are many technical reasons than make me think that the connection can be lost without receiving a FD_CLOSE event.
Some lines I have to repair have a lot of CRC errors (I use a MC2+ for testing lines) and other erros such as FEC, HEC...
I can also find capacitors that will drop the frequency of ADSL and lead a power connection.
Here are the reasons for which I wanted to send dummy datas between the server and the station.
What do you think about
For stream-based sockets (TCP), this isn't an issue. The connection is frequently checked for connectivity by sending small packets of data - this is all done automatically, you don't need to do anything. If the connection is lost, this will be detected and you will still receive FD_CLOSE after some time-out period with no reply. Transmission errors are also checked - any packets with an incorrect checksum are re-sent (automatically.)
For datagram sockets (UDP), you don't have this support, so you must do almost everything yourself.
I am happy you are there. All my questions have found a very good answer. Thank you. I did not think that FD_CLOSE was running like this. Now the post can be closed.