An Example of Simple Client and Server Applications

In this section, I provide an example that comprises two programs: client and server. Client and server parts of the application communicate using TCP/IP. Program code is presented in Listing 17.4 (server) and Listing 17.5 (client). Note that this is the simplest implementation of a client and server system, which, nevertheless, contains all the main mechanisms of application's communication using sockets. The server waits for the client call and, in response to a client request, sends to it the string that the client displays on the console. The client also sends a message in response to the server, which, in turn , displays it on the console. The server waits in a loop for requests and can reply to ten client requests that directly follow each other. To connect the server, the client must know the network name of the computer, in which the server component of the application runs. Before sending the request, the client determines the server's IP address by its name and displays it on the console.

Listing 17.4: The server component that receives requests from the clients
image from book
 ; The SERVER.ASM file .586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE equ -11 ; Prototypes of external procedures IFDEF MASM        EXTERN  shutdowns@8:NEAR        EXTERN  recv@16:NEAR        EXTERN  send@16:NEAR        EXTERN  accept@12:NEAR        EXTERN  listen@8:NEAR        EXTERN  bind@12:NEAR        EXTERN  closesocket@4:NEAR        EXTERN  socket@12:NEAR        EXTERN  CharToOemA@8:NEAR        EXTERN  WSAStartup@8:NEAR        EXTERN  wsprintfA:NEAR        EXTERN  GetLastError@0:NEAR        EXTERN  ExitProcess@4:NEAR        EXTERN  lstrlenA@4:NEAR        EXTERN  WriteConsoleA@20:NEAR        EXTERN  GetStdHandle@4:NEAR ELSE        EXTERN  shutdown:NEAR        EXTERN  recv:NEAR        EXTERN  send:NEAR        EXTERN  accept:NEAR        EXTERN  listen:NEAR        EXTERN  bind:NEAR        EXTERN  closesocket:NEAR        EXTERN  socket:NEAR        EXTERN  CharToOemA:NEAR        EXTERN  WSAStartup:NEAR        EXTERN  _wsprintfA:NEAR        EXTERN  GetLastError:NEAR        EXTERN  ExitProcess:NEAR        EXTERN  lstrlenA:NEAR        EXTERN  WriteConsoleA:NEAR        EXTERN  GetStdHandle:NEAR        shutdown@8 = shutdown        recv@16 = recv        send@16 = send        accept@12 = accept        listen@8 = listen        bind@12 = bind        closesocket@4 = closesocket        socket@12 = socket        CharToOemA@8 = CharToOemA        WSAStartup@8 = WSAStartup        GetStdHandle@4 = GetStdHandle        WriteConsoleA@20 = WriteConsoleA        lstrlenA@4 = lstrlenA        wsprintfA = _wsprintfA        GetLastError@0 = GetLastError        ExitProcess@4 = ExitProcess ENDIF ; INCLUDELIB directives for the linker IFDEF MASM        includelib d:\masm32\lib\user32.lib        includelib d:\masm32\lib\kernel32.lib        includelib d:\masm32\lib\ws2_32.lib ELSE        includelib c:\tasm32\lib\import32.lib ENDIF ;--------------------------------------------- WSADATA STRUCT        wVersion         WORD          ?        wHighVersion     WORD          ?        szDescription    BYTE 257 dup (?)        szSystemStatus   BYTE 129 dup (?)        iMaxSockets      WORD          ?        iMaxUdpDg        WORD          ?        lpVendorInfo     DWORD         ? WSADATA ENDS ;--------------------------------------------- S_UN_B STRUCT        s_b1 BYTE 0        s_b2 BYTE 0        s_b3 BYTE 0        s_b4 BYTE 0 S_UN_B ENDS S_UN_W STRUCT        S_w1 WORD 0        S_w2 WORD 0 S_UN_W ENDS ADDRESS_UNION UNION        s_u_b S_UN_b <>        s_u_w S_UN_w <>        s_addr DWORD 0 ADDRESS_UNION ENDS in_addr STRUCT        s_un ADDRESS_UNION <> in_addr ENDS sockaddr_in STRUCT        sin_family  WORD 0        sin_port    WORD 0        sin_addr    in_addr <>        sin_zero    BYTE 8 dup (0) sockaddr_in ENDS ;------------------------------------- ; Data segment _DATA SEGMENT HANDL  DD ? LENS   DD ? ERRS   DB "Error %u ",.0 ;------------------------------------- S1    DD ? S2    DD ? LEN   DD ? BUF   DB 100 DUP(O) BUF1  DB 100 DUP(0) txt   DB 'Call accepted. Please send acknowledgment.', 0 msg   DB 'The server is down', 0 sin1  sockaddr_in  <0> sin2  sockaddr_in  <0> wsd   WSADATA  <0> len1  DD ? _DATA ENDS ; ode segment _TEXT SEGMENT START: ; Get the descriptor of the output console        PUSH  STD_OUTPUT_HANDLE        CALL  GetStdHandle@4        MOV   HANDL, EAX ; Activate the sockets library        PUSH  OFFSET wsd        MOV   EAX, 0        MOV   AX, 0202H        PUSH  EAX        CALL  WSAStartup@8        CMP   EAX, 0        JZ    NO_ER1        CALL  ERRO        JMP   EXI NO_ER1: ; Create a socket        PUSH  0        PUSH  1 ; SOCK_STREAM        PUSH  2 ; AF_INET        CALL  socket@12        CMP   EAX, NOT 0        JNZ   N0_ER2        CALL  ERRO        JMP   EXI N0_ER2:        MOV   s1, EAX ; Connect a socket        MOV   sin1.sin_family, 2 ; AF_INET        MOV   sin1.sin_addr.s_un.s_addr, 0 ; INADDR_ANY        MOV   sin1.sin_port, 2000 ; Port number        PUSH  sizeof(sockaddr_in)        PUSH  OFFSET sin1        PUSH  s1        CALL  bind@12        CMP   EAX, 0        JZ    NO_ER3        CALL  ERRO        JMP   CLOS NO_ER3: ; Switch the socket to the listening state        MOV   ESI, 10        PUSH  5        PUSH  s1        CALL  listen@8        CMP   EAX, 0        JZ    N0_ER4        CALL  ERRO        JMP   CLOS NO_ER4:        MOV len1, sizeof(sockaddr_in) ; Wait for the client requests        PUSH  OFFSET len1        PUSH  OFFSET sin2        PUSH  s1        CALL  accept@12        MOV  s2, EAX ; The request has arrived, now sending the information        PUSH  0        PUSH  OFFSET txt        CALL  lstrlenA@4        PUSH  EAX        PUSH  OFFSET txt        PUSH  s2        CALL  send@16 ; Waiting for response        PUSH  0        PUSH  100        PUSH  OFFSET buf        PUSH  s2        CALL  recv@16 ; EAX contains the message length ; First, it is necessary to convert the string        PUSH  OFFSET buf1        PUSH  OFFSET buf        CALL  CharToOemA@8 ; Output        LEA  EAX, BUF1        MOV  EDI, 1        CALL WRITE ; Close the connection        PUSH 0        PUSH s2        CALL shutdown@8 ; Close the socket        PUSH S2        CALL closesocket@4        DEC  ESI        JNZ  N0_ER4 ; End of the loop        PUSH  OFFSET buf1        PUSH  OFFSET msg        CALL  CharToOemA@8 ; Now the conclusion        LEA   EAX, BUF1        MOV   EDI, 1        CALL  WRITE CLOSE:        PUSH  S1        CALL  closesocket@4 EXIT: ; Exiting when all services are terminated        PUSH  0        CALL  ExitProcess@4 ; Display the string terminated by the line feed ; EAX - To the start of the string ; EDI - With or without the line feed WRITE  PROC        PUSH  ESI ; Get the parameter length        PUSH  EAX        PUSH  EAX        CALL  lstrlenA@4        MOV   ESI, EAX        POP   EBX        CMP   EDI, 1        JNE   NO_ENT ; Line feed in the end        MOV   BYTE PTR [EBX+ESI], 13        MOV   BYTE PTR [EBX+ESI+1], 10        MOV   BYTE PTR [EBX+ESI+2], 0        ADD   EAX, 2 NO_ENT: ; String output        PUSH  0        PUSH  OFFSET LENS        PUSH  EAX        PUSH  EBX        PUSH  HANDL        CALL  WriteConsoleA@20        POP   ESI        RET WRITE ENDP ; The procedure for error code output ERRO   PROC        CALL  GetLastError@0        PUSH  EAX        PUSH  OFFSET ERRS        PUSH  OFFSET BUF1        CALL  wsprintfA        ADD   ESP, 12        LEA   EAX, BUF1        MOV   EDI, 1        CALL  WRITE        RET ERRO ENDP _TEXT ENDS END START 
image from book
 

To translate the SERVER.ASM (Listing 17.4), issue the following commands for MASM32:

 ml /c /coff /DMASM server.asm     link /subsystem:console server.obj 

Issue the following commands for TASM32:

 tasm32 /ml server.asm     tlink32 -ap server.obj 

The program presented in Listing 17.4 requires some comments.

  • The SERVER.ASM program waits for the client programs to contact it. It listens to port 2000. The waiting is implemented by the accept function. This function returns control when a client attempts to connect this server. If the connection was successfully established, the function returns the newly-created socket, through which the server will communicate to the client program.

  • The accept function has one specific feature. It returns control immediately after receiving a message from the client. This doesn't guarantee yet that the connection has been established; this is especially true for WANs. Consequently, if you build the communications design according to this approach, it is necessary to provide additional features for acknowledging the connection.

  • This program implements the simplest method of communication, in which the server can communicate only with one client at a time. To ensure the possibility of simultaneous operation with multiple clients, it is necessary to use another approach: When receiving a message from the client, the server must create a new thread and pass the newly-created socket to it. This thread will further server this client. The main thread will again switch to waiting for new client requests.

Listing 17.5: The client program that calls the server component
image from book
 ; The CLIENT.ASM program .586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE equ -11 ; Prototypes of external procedures IFDEF MASM        EXTERN connect@12:NEAR        EXTERN gethostbyname@4:NEAR        EXTERN shutdown@8:NEAR        EXTERN recv@16:NEAR        EXTERN send@16:NEAR        EXTERN accept@12:NEAR        EXTERN listen@8:NEAR        EXTERN bind@12:NEAR        EXTERN closesocket@4:NEAR        EXTERN socket@12:NEAR        EXTERN CharToOemA@8:NEAR        EXTERN WSAStartup@8:NEAR        EXTERN wsprintfA:NEAR        EXTERN GetLastError@0:NEAR        EXTERN ExitProcess@4:NEAR        EXTERN lstrlenA@4:NEAR        EXTERN WriteConsoleA@20:NEAR        EXTERN GetStdHandle@4:NEAR ELSE        EXTERN connect:NEAR        EXTERN gethostbyname:NEAR        EXTERN shutdown:NEAR        EXTERN recv:NEAR        EXTERN send:NEAR        EXTERN accept:NEAR        EXTERN listen :NEAR        EXTERN bind:NEAR        EXTERN closesocket:NEAR        EXTERN socket:NEAR        EXTERN CharToOemA:NEAR        EXTERN WSAStartup:NEAR        EXTERN _wsprintfA:NEAR        EXTERN GetLastError:NEAR        EXTERN ExitProcess:NEAR        EXTERN lstrlenA:NEAR        EXTERN WriteConsoleA:NEAR        EXTERN GetStdHandle:NEAR        connect@12 = connect        gethostbyname@4 = gethostbyname        shutdown@8 = shutdown        recv@16 = recv        send@16 = send        accept@12 = accept        listen@8 = listen        bind@12 = bind        closesocket@4 = closesocket        socket@12 = socket        CharToOemA@8 = CharToOemA        WSAStartup@8 = WSAStartup        GetStdHandle@4 = GetStdHandle        WriteConsoleA@20 = WriteConsoleA        lstrlenA@4 = lstrlenA        wsprintfA = _wsprintfA        GetLastError@0 = GetLastError        ExitProcess@4 = ExitProcess ENDIF ; INCLUDELIB directives for the linker IFDEF MASM        includelib d:\masm32\lib\user32.lib        includelib d:\masm32\lib\kernel32.lib        includelib d:\masm32\lib\ws2_32.lib ELSE        includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ WSADATA STRUCT        wVersion           WORD            ?        wHighVersion       WORD            ?        szDescription     BYTE 257 dup   (?)        szSystemStatus     BYTE 129 dup   (?)        iMaxSockets        WORD            ?        iMaxUdpDg          WORD            ?        lpVendorInfo       DWORD           ? WSADATA ENDS ;-------------------------------------------- S_UN_B STRUCT        s_b1 BYTE 0        s_b2 BYTE 0        s_b3 BYTE 0        s_b4 BYTE 0 S_UN_B ENDS S_UN_W STRUCT        S_w1 WORD 0        s_w2 WORD 0 S_UN_W ENDS ADDRESS_UNION UNION        s_u_b S_UN_b <>        s_u_w S_UN_w <>        s_addr DWORD 0 ADDRESS_UNION ENDS in_addr STRUCT        s_un ADDRESS_UNION <> in_addr ENDS sockaddr_in STRUCT        sin_family   WORD      0        sin_port     WORD      0        sin_addr     in_addr <>        sin_zero     BYTE 8 dup (0) sockaddr_in ENDS hostent STRUCT        h_name     DWORD      ?        h_alias    DWORD      ?        h_addr     WORD       ?        h_len      WORD       ?        h_list     DWORD      ? hostent ENDS ;-------------------------------------------- ; Data segment _DATA SEGMENT HANDL   DD ? LENS    DD ? ERRS    DB "Error %u ", 0 IP      DB "IP address %hu.%hu.%hu.%hu", 0 IPA     DD ? S1      DD ? comp    DB "pvju1", 0 txt     DB "Call acknowledged", 0 txt1    DB "Host address", 0 LEN     DD ? sin2    sockaddr_in  <0> hp      hostent  <0> BUF     DB 100 DUP(0) BUF1    DB 100 DUP(0) wsd     WSADATA  <0> _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Define the output console handle        PUSH  STD_OUTPUT_HANDLE        CALL  GetStdHandle@4        MOV   HANDL, EAX ; Activate the sockets library        PUSH  OFFSET wsd        MOV   EAX, 0        MOV   AX, 0202H        PUSH  EAX        CALL  WSAStartup@8        CMP   EAX, 0        JZ    NO_ER1        CALL  ERRO        JMP   EXI NO_ER1: ; Determine the server address using the host name        PUSH  OFFSET comp        CALL  gethostbyname@4        CMP   EAX, 0        JNZ   NO_ER2        CALL  ERRO        JMP   EXI NO_ER2: ; Display the address        MOV   EBX, [EAX+12] ; h_list in the hostent structure        MOV   EDX, [EBX]        MOV   EDX, [EDX]        MOV   IPA, EDX        SHR   EDX, 24        AND   EDX, 000000FFH        PUSH  EDX        MOV   EDX, IPA        SHR   EDX, 16        AND   EDX, 000000FFH        PUSH  EDX        MOV   EDX, IPA        SHR   EDX, 8        AND   EDX, 000000FFH        PUSH  EDX        MOV   EDX, IPA        AND   EDX, 000000FFH        PUSH  EDX        PUSH  OFFSET IP        PUSH  OFFSET BUF1        CALL  wsptrintfA        ADD   ESP, 24        LEA   EAX, BUF1        MOV   EDI, 1        CALL  WRITE        MOV   EDX, IPA        MOV   sin2.sin_addr.s_un.s_addr, EDX        MOV   sin2.sin_port, 2000        MOV   sin2.sin_family, 2 ; AF_INET ; Create a socket        PUSH 0        PUSH 1   ; SOCK_STREAM        PUSH 2   ; AF_INET        CALL  socket@12        CMP   EAX, NOT 0        JNZ   N0_ER3        CALL  ERRO        JMP   EXI N0_ER3:        MOV   s1, EAX ; Try to connect the server        PUSH  sizeof(sockaddr_in)        PUSH  OFFSET sin2        PUSH  s1        CALL  connect@12        CMP   EAX, 0        JZ    NO_ER4        CALL  ERRO        JMP   CLOS NO_ER4: ; Wait for information        PUSH  0        PUSH  100        PUSH  OFFSET buf        PUSH  s1        CALL  recv@16 ; Message length in EAX ; First, it is necessary to convert the string        PUSH  OFFSET buf1        PUSH  OFFSET buf        CALL  CharToOemA@8 ; Output        LEA   EAX, BUF1        MOV   EDI, 1        CALL  WRITE ; Send information        PUSH  0        PUSH  OFFSET txt        CALL  lstrlenA@4        PUSH  EAX        PUSH  OFFSET txt        PUSH  s1        CALL  send@16 CLOSE:        PUSH  S1        CALL  closesocket@4 EXIT: ; Exiting after all services have terminated        PUSH  0        CALL  ExitProcess@4 ; Display the string (line feed in the end) ; EAX - To the start of the string ; EDI - With or without the line feed WRITE   PROC ; Get the parameter length        PUSH  EAX        PUSH  EAX        CALL  lstrlenA@4        MOV   ESI, EAX        POP   EBX        CMP   EDI, 1        JNE   NO_ENT ; Line feed in the end of the string        MOV   BYTE PTR [EBX+ESI], 13        MOV   BYTE PTR [EBX+ESI+1], 10        MOV   BYTE PTR [EBX+ESI+2], 0        ADD   EAX, 2 NO_ENT: ; String output        PUSH  0        PUSH  OFFSET LENS        PUSH  EAX        PUSH  EBX        PUSH  HANDL        CALL  WriteConsoleA@20        RET WRITE ENDP ; Display error code ERRO   PROC        CALL  GetLastError@0        PUSH  EAX        PUSH  OFFSET ERRS        PUSH  OFFSET BUF1        CALL  wsprintfA        ADD   ESP, 12        LEA   EAX, BUF1        MOV   EDI, 1        CALL  WRITE        RET ERRO ENDP _TEXT ENDS END START 
image from book
 

To translate the program presented in Listing 17.5, issue the following commands for MASM32:

 ml /c /coff /DMASM CLIENT.asm     link /subsystem:console CLIENT.obj 

Issue the following commands for TASM32:

 tasm32 /ml CLIENT.asm     tlink32 -ap CLIENT.obj 

The program presented in Listing 17.5 requires some comments.

  • Before trying to access the server, the client must get the IP address of the host that runs the server component of the application. For this purpose, the gethostbyname function is used. If this function completes successfully, it returns the pointer to the hostent structure that I described earlier. The definition of this structure is placed into the program code, although it doesn't directly use a variable of this type. To make it easier to understand how the IP address is then retrieved, consider the appropriate lines from the program code:

     MOV EBX, [EAX+12] ; h_list is in the hostent structure     MOV EDX, [EBX]     MOV EDX, [EDX]     MOV IPA, EDX 

Note that the h_list field resides exactly at 12-byte offset from the starting point of the structure. This field contains the pointer to the string of bytes. This string contains several 4-byte chains, each of which represents an IP address of the host (recall that the host can have several IP addresses). A zero code serves as an indication of the end of this sequence. At the same time, you need to be interested only in the first 4-byte chain; therefore, you read it using the MOV EDX, [EDX] command. The least significant byte is the leftmost byte in the IP address.

  • You can modify this program by taking the IP address instead of the host name as a basis. To achieve this, it is necessary to form a double word so that the least significant byte will be the leftmost component of the address and the most significant byte will be the rightmost component. Then, it will be necessary to load this double word into a register (e.g., EDX ) and execute the following command:

     MOV sin2.sin_addr.S_un.s_addr, EDX 

To conclude the examples of working with sockets, consider Fig. 17.4, which shows a schematic diagram of communications between the SERVER.ASM and CLIENT.ASM programs.

image from book
Figure 17.4: Method of client and server communication (see Listings 17.4 and 17.5)


The Assembly Programming Master Book
The Assembly Programming Master Book
ISBN: 8170088178
EAN: 2147483647
Year: 2004
Pages: 140
Authors: Vlad Pirogov

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net