Jump to content
xisto Community
Sign in to follow this  
einstein

Help Im Re-sync-ing

Recommended Posts

Intro-D

I've not written anything useful in a while and I've been thinking of stuff to write about. I did get to remembering my days of trying to learn sockets and how difficult it was to even find out about async sockets, let alone learn/use them. It seems to make sense, then, that it would be a good subject for a tutorial... and here it is.

 

About tute

This tute will cover basic sockets as well as the a-sync related functions. The language used will be assembly (MASM) but the layout is quite similar to C/++. You've got more of a chance of understanding this tute if you know some kinda programming language and are familiar with Win32 programming, as this won't be normal console stuff. If not, read it anyway as you still may learn something :)

 

Template

I won't be covering Win32 ASM here but I will provide you with a template for you to build the rest of the code into.

 

.686.model flat, stdcalloption casemap: noneinclude \masm32\include\windows.incinclude \masm32\include\kernel32.incinclude \masm32\include\user32.incinclude \masm32\include\ws2_32.incincludelib \masm32\lib\kernel32.libincludelib \masm32\lib\user32.libincludelib \masm32\lib\ws2_32.libWinMain   proto.data  WindowClass db 'std', 0.data?  hInst HINSTANCE ?.const  WM_SOCKET equ WM_USER+1.codestart:	  invoke GetModuleHandle, NULL	  mov hInst, eax	  invoke WinMain	  invoke ExitProcess, eaxWinMain proc  LOCAL wc:WNDCLASSEX  LOCAL msg:MSG  LOCAL hWnd:HWND; winsock struct  ; startup function	mov wc.cbSize, sizeof WNDCLASSEX	mov wc.style, CS_HREDRAW + CS_VREDRAW	mov wc.lpfnWndProc, offset WndProc	mov wc.cbClsExtra, NULL	mov wc.cbWndExtra, NULL	push hInst	pop wc.hInstance	mov wc.hbrBackground, COLOR_BTNFACE+1	mov wc.lpszMenuName, NULL	mov wc.lpszClassName, offset WindowClass	invoke LoadIcon, NULL, IDI_WINLOGO	mov wc.hIcon, eax	mov wc.hIconSm, eax	invoke LoadCursor, NULL, IDC_ARROW	mov wc.hCursor, eax	invoke RegisterClassEx, addr wc	invoke CreateWindowEx, NULL, addr WindowClass, NULL, \						   NULL, \						   0, 0, 0, 0, \						   NULL, NULL, hInst, NULL	mov hWnd, eax	invoke ShowWindow, hWnd, SW_HIDE	invoke UpdateWindow, hWnd	.while TRUE		invoke GetMessage, addr msg, NULL, 0, 0		.break .if (!eax)				invoke TranslateMessage, addr msg		invoke DispatchMessage, addr msg	.endw	mov eax, msg.wParam	retWinMain endpWndProc proc hWnd:HWND, msg:UINT, wParam:WPARAM, lParam:LPARAM; socket var 	.if msg == WM_CREATE	  ; socket functions	.elseif msg == WM_DESTROY		invoke PostQuitMessage, NULL  ; socket msg	.else		invoke DefWindowProc, hWnd, msg, wParam, lParam		ret	.endif	xor eax, eax	retWndProc endpend start

Libs

Before you can use anything winsock-related, you must include certain files. Unlike C and its variants, MASM requires two files. Firstly, there's the "include" file which contains the function prototypes. Secondly, there's the "lib" file, which contains the code. In the above template, you'll see that I've already included the files.

 

Startup

Before winsock can be used, it must be initialized. We do this by calling the function WSAStartup and passing two arguments. The first argument is the winsock version to request and the second argument is the address to a winsock struct. Firstly, let's create the struct. In the template there's the comment "winsock struct". Replace this with the following code.

 

LOCAL ws:WSADATA

WSADATA is the datatype and ws is the name which will be used to refer to it.

 

Now we have the struct, we can call the winsock init function. Below the previous comment is another. Replace that one with the following.

 

invoke WSAStartup, 0202h, addr ws

Here, we call the WSAStartup function and request winsock version 2.2, followed by the address of the struct we made earlier. You should note that the requested version isn't necessarily the version you'll get, although the function won't fail so you can still use it.

 

Socket creation

Now we're free to use winsock, we can create a socket. We're going to do this after the WM_CREATE message has been sent to the window. Just above here, you'll notice a 'socket var' comment. Replace this with...

 

LOCAL _ls:SOCKET

You should be able to work out by now that SOCKET is the datatype and _ls is the lable :) SOCKET isn't really a datatype - it's just the same as a DWORD, but which you refer to doesn't make any difference.

 

Now we have somewhere to store the socket info, we can call the socket function to create it. Under WM_CREATE is another comment.

 

invoke socket, 2, 1, 6mov [_ls], eax

I've used numbers here because it makes me look k00l. Actually, I just found them easier to remember. Most programmers would be more used to AF_INET, SOCK_STREAM and IPPROTO_TCP. Really, they're just the same. Anyway, the first argument (2) is the family to use. In this case, we're going for network/internet. The second argument (1) is the type of socket. There's DGRAM and RAW, but we just want a STREAM for simplicity. The final argument (6) is the protocol number. As we're working with connection-oriented sockets, we'll be using TCP.

 

When socket is called, it returns a value to be used as an identifier, if you will. We'll refer to this value when operating on this socket. As such, we'll need to store it somewhere. Where better than our SOCKET var from earlier?

 

Socket info

A function we're gonna call in the future is gonna need some information as to what we need from our socket. This information is stored in another struct, which we'll need to create. Under our SOCKET creation...

 

LOCAL _li:sockaddr_in

With this done, we can start filling. Below are several lines of code to put under the socket call.

 

mov _li.sin_family, 2

Firstly, we need to specify the family. Like before, we're using the i-net.

 

mov _li.sin_addr.S_un.S_addr, 0

Secondly, we must specify an incoming IP address. As we're making a server, all IP addresses must be welcome so we use '0'.

 

invoke htons, 80mov _li.sin_port, ax

Lastly, we specify a port to listen on. Before we can assign the value to the struct, we need to convert the numeric port number into something more suitable. For this, we pass it to htons which returns the proper minerals in the accumulator.

 

Bind

Before we listen, we bind the socket. This function is where our sockaddr_in struct is needed. We pass 3 arguments to bind. By now, they should make sense.

 

invoke bind, [_ls], addr _li, sizeof _li

Listen

Now the socket is bound, we can begin to listen. Listen is quite easy to understand and only has 2 arguments. There's the socket to listen on and the maximum connection que. Place the following under bind.

 

invoke listen, [_ls], 10

Sink?

With the socket created, bound and set to listen, we can now turn this socket into an a-sync socket. To do so, we call WSAAsyncSelect.

 

invoke WSAAsyncSelect, [_ls], [hWnd], WM_SOCKET, FD_ACCEPT + FD_READ

The first argument is obviously the socket to be a-sync'd. The second argument is a handle to a window to send messages to. In this case, we'll be sending socket messages to the main window. The third argument is the message to send to the window. Here, a constant is used which has been defined under '.const'. The fourth argument is a set of 'flags' to show which events should be reported. As we're only creating a simple server, we'll only be using FD_ACCEPT and FD_READ.

 

WM_SOCKET

Under the WM_DESTROY message is another comment. Replace this with the following code.

 

.elseif msg == WM_SOCKET  ; events

This adds another message handler for the WM_SOCKET message, which is needed for handling the FD_ messages.

 

FD_ACCEPT

Under the WM_SOCKET message-conditional-thingy, we'll be adding more code. In higher languages, you'll need to call functions to get different words from lParam. As we're using ASM, we can just shift lParam into EAX and refer to AX for our event code. Code to be added?

 

mov eax, [lParam].if ax == FD_ACCEPT

Below here, we'll be handling the FD_ACCEPT event. This event occurs when a connection is made on the port we bound our socket to. The only thing we really have to do is... accept the connection.

 

invoke accept, [wParam], NULL, NULL

There's a bit more to be said here. For starters, it should be noted that when a socket event occurs, the socket 'ID' will be stored in wParam. Secondly, the 2nd and 3rd arguments don't need to be NULL. Here you can specify struct info, similar to our _li. This can be used if you want to see the client's IP, etc. For the sake of our example, I'm leaving this out. Now we've accepted the connection, we just have to wait...

 

FD_READ

We've waited long enough and our client has decided to send us something. So, the FD_READ event is gonna kick off. That's no use if we don't know how to handle it, though. So, under the accept call, add...

 

.elseif ax == FD_READ

Now something has entered our domain, we can play with it...

 

Recv

To pull data from a socket so we can toy with it, we must call recv. This function requires an address for a buffer to store the data. We don't have one, so we must make one. Put the following under our sockaddr_in creation.

 

LOCAL recvb[384]:BYTE

Simply put, this just provides us with 384 bytes to shove data into. Now we have it, we can call the function. Below the last FD_READ entry...

 

invoke recv, [wParam], addr recvb, 383, 0mov [recvb][eax], 0

The first argument is obviously the socket ID, stored in wParam as I said before. The second argument is the address to the buffer we made earlier, followed by the third argument which is the number of bytes to move into the buffer. Our buffer is only 384 bytes and we need room for a NULL terminator, so we're only going to allow 383. The final argument is a set of flags, but they're rarely necessary so we can just set this to 0. The line below just addresses a byte in recvb and sets the value to 0. EAX will contain the number of bytes received, so it can be used as a pointer to the byte just after the final received byte.

 

Now we have the data in a buffer and we've made sure it's NULL terminated, we're going to display it on the screen. For this, we'll call the MessageBox function. The following goes under our recv.

 

invoke MessageBox, NULL, addr recvb, addr WindowClass, MB_OK

The arguments here aren't all that important, so I won't explain them. Anyway, this will display a message box with the content in recvb displayed in the main window and the window class as the caption.

 

Close

Now we've accepted, received and printed, it's time to say night-night to our client. For this, we call closesocket with the socket ID as the only argument.

 

invoke closesocket, [wParam]

With this done, it's also wise to close off the conditional.

 

.endif

End product

Just so you know you've got it right, here's the template with the functions and such added.

 

.686.model flat, stdcalloption casemap: noneinclude \masm32\include\windows.incinclude \masm32\include\kernel32.incinclude \masm32\include\user32.incinclude \masm32\include\ws2_32.incincludelib \masm32\lib\kernel32.libincludelib \masm32\lib\user32.libincludelib \masm32\lib\ws2_32.libWinMain   proto.data  WindowClass db 'std', 0.data?  hInst HINSTANCE ?.const  WM_SOCKET equ WM_USER+1.codestart:	  invoke GetModuleHandle, NULL	  mov hInst, eax	  invoke WinMain	  invoke ExitProcess, eaxWinMain proc  LOCAL wc:WNDCLASSEX  LOCAL msg:MSG  LOCAL hWnd:HWND  LOCAL ws:WSADATA	invoke WSAStartup, 0202h, addr ws	mov wc.cbSize, sizeof WNDCLASSEX	mov wc.style, CS_HREDRAW + CS_VREDRAW	mov wc.lpfnWndProc, offset WndProc	mov wc.cbClsExtra, NULL	mov wc.cbWndExtra, NULL	push hInst	pop wc.hInstance	mov wc.hbrBackground, COLOR_BTNFACE+1	mov wc.lpszMenuName, NULL	mov wc.lpszClassName, offset WindowClass	invoke LoadIcon, NULL, IDI_WINLOGO	mov wc.hIcon, eax	mov wc.hIconSm, eax	invoke LoadCursor, NULL, IDC_ARROW	mov wc.hCursor, eax	invoke RegisterClassEx, addr wc	invoke CreateWindowEx, NULL, addr WindowClass, NULL, \						   NULL, \						   0, 0, 0, 0, \						   NULL, NULL, hInst, NULL	mov hWnd, eax	invoke ShowWindow, hWnd, SW_HIDE	invoke UpdateWindow, hWnd	.while TRUE		invoke GetMessage, addr msg, NULL, 0, 0		.break .if (!eax)				invoke TranslateMessage, addr msg		invoke DispatchMessage, addr msg	.endw	mov eax, msg.wParam	retWinMain endpWndProc proc hWnd:HWND, msg:UINT, wParam:WPARAM, lParam:LPARAM  LOCAL _ls:SOCKET  LOCAL _li:sockaddr_in  LOCAL recvb[384]:BYTE 	.if msg == WM_CREATE		invoke socket, 2, 1, 6		mov [_ls], eax		mov _li.sin_family, 2		mov _li.sin_addr.S_un.S_addr, 0		invoke htons, 80		mov _li.sin_port, ax		invoke bind, [_ls], addr _li, sizeof _li		invoke listen, [_ls], 10		invoke WSAAsyncSelect, [_ls], [hWnd], WM_SOCKET, FD_ACCEPT + FD_READ	.elseif msg == WM_DESTROY		invoke PostQuitMessage, NULL	.elseif msg == WM_SOCKET		mov eax, [lParam]		.if ax == FD_ACCEPT			invoke accept, [wParam], NULL, NULL		.elseif ax == FD_READ			invoke recv, [wParam], addr recvb, 383, 0			mov [recvb][eax], 0			invoke MessageBox, NULL, addr recvb, addr WindowClass, MB_OK			invoke closesocket, [wParam]		.endif	.else		invoke DefWindowProc, hWnd, msg, wParam, lParam		ret	.endif	xor eax, eax	retWndProc endpend start

Now, if you assemble this, run it and visit http://forums.xisto.com/no_longer_exists/ in your browser, you'll see a GET request in a message box. As I've not created a visual window, to close the app you'll have to use the ol' CTRL + DEL.

 

Quick note...

In this example, we've only used a few winsock functions and a-sync events. For more information on the others, you can search the MSDN library or locate some other tutorials. Maybe I'll write another socket tutorial covering client creation.

 

Closing

That concludes my introduction to a-sync sockets. Hopefully with this information, you'll be more prepared for creating applications that require multiple connections on a single port.

 

Got anything to say? Say it ;p

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×
×
  • Create New...

Important Information

Terms of Use | Privacy Policy | Guidelines | We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.