Simple Web Server
Пример простого веб сервера. Для использования компилируем, создаем папку wwwroot в папке, где создан EXE сервера. Далее в папку wwwroot кладем проект сайта или на худой конец один файл index.html. Забиваем в браузере ваш локальный адрес (http://127.0.0.1/) Должна высветиться корневая страница сайта. Желательно до компиляции создать файл ресурсов с иконкой, в нем прописать:
1 ICON "1.ico"
В этом случае в трее будет видна ваша иконка, щелкнув по которой , вывалится контекстное меню из трех пунктов: (Run, Pause, Exit).
Платформа: Windows
Автор: Anselme Dewavrin
'Simple Web Server, (c) Anselme Dewavrin 2006 - dewavrin@yahoo.com 'Feel free to use it, provided you mention my name. 'based on the example provided with freebasic. #INCLUDE Once "win/winsock2.bi" #INCLUDE Once "windows.bi" #INCLUDE Once "win/shellapi.bi" Const WM_SHELLNOTIFY = WM_USER + 5 Const ID_RUN = 1001 Const ID_PAUSE = 1002 Const ID_EXIT = 1003 Const SERVER_ADDR = "localhost" Const HOMEDIR = "wwwroot" Const DEFAULT_PORT = 80 Const SERVER_BUFFSIZE = 16 Dim Shared note As NOTIFYICONDATA Dim Shared TB_CREATED As Integer Dim Shared szAppName As String Dim Shared As Integer ServerPort = DEFAULT_PORT '--- Type CLIENT socket As socket ip As Integer port As Integer recvthread As Integer prev As CLIENT Ptr Next As CLIENT Ptr End Type Type SERVERCTX socket As socket acceptthread As Integer isrunning As Integer globmutex As Integer filemutex As Integer clientlisthead As CLIENT Ptr End Type Dim Shared ctx As SERVERCTX 'multithreaded socket handling ''::::: Sub serverDel( Byval client As CLIENT Ptr ) Dim s As socket '' not already removed? If( client->socket <> NULL ) Then s = NULL Swap s, client->socket ' this should be atomic.. '' close connection shutdown( s, 2 ) closesocket( s ) '' recv thread stills running? If( client->recvthread <> NULL ) Then Threadwait( client->recvthread ) End If '' remove from list If( client->Next ) Then client->Next->prev = client->prev End If If( client->prev ) Then client->prev->Next = client->Next Else ctx.clientlisthead = client->Next End If End If End Sub ''::::: Function serverEnd( ) As Integer Dim client As CLIENT Ptr ctx.isrunning = FALSE '' close the listening socket If( ctx.socket <> 0 ) Then shutdown(ctx.socket, 2 ) closesocket( ctx.socket ) ctx.socket = 0 End If '' remove all clients yet running Dim i As Integer Do client = ctx.clientlisthead If( client = NULL ) Then Exit Do End If serverDel( client ) Loop '' shutdown winsock Function = WSACleanup( ) End Function 'thread waiting for data to arrive, parsing HTTP GET requests and sending responses Sub serverReceive( Byval client As CLIENT Ptr ) Dim PacketBuffer(SERVER_BUFFSIZE) As Byte Dim As Integer ReceivedLen = 0 Dim As Byte Ptr ReceivedBuffer = 0 Dim As String stNL = Chr(13) & Chr(10) Dim As String stNLNL = stNL & stNL Dim FileBuffer() As Byte 'fix for fb0.16beta, thx v1ctor Dim SendBuffer() As Byte 'fix for fb0.16beta, thx v1ctor Dim FileHandle As Ubyte 'receive loop Do While( ctx.isrunning And (client->socket <> NULL) ) ' block until some data Dim bytes As Integer bytes = recv( client->socket, @PacketBuffer(0), SERVER_BUFFSIZE, 0 ) ' connection closed? If( bytes <= 0 ) Then Exit Do End If ' accumulate received data ReceivedBuffer = Reallocate(ReceivedBuffer, bytes + ReceivedLen) Dim i As Integer For i=0 To bytes-1 ReceivedBuffer[ReceivedLen+i] = PacketBuffer(i) Next i ReceivedLen += bytes 'CRLF found twice ? If (ReceivedLen >= 4) And _ (ReceivedBuffer[ReceivedLen-4] = 13) And _ (ReceivedBuffer[ReceivedLen-3] = 10) And _ (ReceivedBuffer[ReceivedLen-2] = 13) And _ (ReceivedBuffer[ReceivedLen-1] = 10) Then 'extract get path + url decoding (special chars are coded %XY) Dim As String stPath = HOMEDIR Dim As Integer iAcc = 0, iHex = 0 For i = 4 To ReceivedLen-1 Dim c As Byte c = ReceivedBuffer[i] If c = Asc(" ") Then Exit For If iHex <> 0 Then iHex += 1 'decode hex code iAcc *= 16 iAcc += (c-48) If iHex = 3 Then c = iAcc iAcc = 0 iHex = 0 Endif Endif If c=Asc("%") Then 'hex code coming ? iHex = 1 iAcc = 0 Endif If iHex = 0 Then stPath += Chr(c) Next i If (stPath = HOMEDIR + "/") Or _ 'default page and (Instr(stPath,"..") <> 0) Then 'restrict to wwwroot stPath = HOMEDIR + "/index.html" End If 'get rid of received data ReceivedLen = 0 Deallocate(ReceivedBuffer) 'prepare response Dim As String stReponseHeader Dim As Integer FileLength = 0 'read requested file from disk (no mutex, thanx to Zerospeed) If Dir(stPath) = "" Then stReponseHeader = "HTTP/1.1 404 Not Found" & stNL & stNL Else FileHandle = Freefile If Open( stPath For Binary Access Read Shared As #FileHandle ) <> 0 Then stReponseHeader = "HTTP/1.1 403 Forbbiden" & stNL & stNL Else FileLength = Lof(FileHandle) 'file len If FileLength <> 0 Then Redim FileBuffer(FileLength+1) As Byte Get #FileHandle, , FileBuffer(0), FileLength End If Close #FileHandle stReponseHeader = "HTTP/1.1 200 OK" & stNL stReponseHeader += "Cache-Control: private" & stNL stReponseHeader += "content-length : " & Str(FileLength) & stNL & stNL End If End If 'copy response header to sendbuffer Dim HeaderLength As Integer HeaderLength = Len(stReponseHeader) Dim As Integer SendBufferLen = HeaderLength + FileLength Redim SendBuffer(SendBufferLen) As Byte 'copy loop (thx v1ctor for this simplified version) For i = 0 To HeaderLength-1 SendBuffer(i) = stReponseHeader[i] Next i 'copy response data to sendbuffer If FileLength <> 0 Then For i = 0 To FileLength-1 SendBuffer(i+HeaderLength) = FileBuffer(i) Next i End If 'send response Dim As Byte Ptr sendptr sendptr = @sendBuffer(0) Do While (ctx.isrunning And (client->socket <> NULL) And (SendBufferLen > 0)) ' loop until the whole buffer is sent bytes = send( client->socket, sendptr, SendBufferLen, 0 ) '' connection closed? If( bytes <= 0 ) Then Exit Do End If sendptr += bytes SendBufferLen -= bytes Loop 'send loop ' remove client client->recvthread = NULL serverDel( client ) End If 'bFound Loop 'receive loop ' remove client client->recvthread = NULL serverDel( client ) End Sub Sub serverAccept( Byval unused As Integer ) Dim sa As sockaddr_in Dim s As socket Do While( ctx.isrunning ) Dim salen As Integer salen = Len( sockaddr_in ) s = accept( ctx.socket, Cptr( PSOCKADDR, @sa ), @salen ) If( s = INVALID_SOCKET ) Then Exit Do End If Dim client As CLIENT Ptr '' access global data, lock it Mutexlock( ctx.globmutex ) '' allocate node client = Allocate( Len( CLIENT ) ) '' add to head of list client->Next = ctx.clientlisthead ctx.clientlisthead = client If client->Next Then client->Next->prev = client client->prev = NULL Mutexunlock( ctx.globmutex ) '' setup the client client->socket = s client->ip = (@sa)->sin_addr.S_addr client->port = (@sa)->sin_port '' start new recv and send threads client->recvthread = Threadcreate( @serverReceive, Cint( client ) ) Loop ctx.isrunning = FALSE End Sub Function serverRun( ) As Integer ' start winsock Dim wsaData As WSAData If( WSAStartup( MAKEWORD( 2, 0 ), @wsaData ) <> 0 ) Then 'print "error calling WSAStartup: "; WSAGetLastError( ) Return FALSE End If If( wsaData.wVersion <> MAKEWORD( 2, 0 ) ) Then WSACleanup( ) Return FALSE End If ' create a socket for listening ctx.socket = OpenSocket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) If( ctx.socket = NULL ) Then 'print "error calling opensocket: "; WSAGetLastError( ) Return FALSE End If ' bind it to the server port Dim sa As sockaddr_in sa.sin_port = htons( ServerPort ) sa.sin_family = AF_INET sa.sin_addr.S_addr = INADDR_ANY If(bind( ctx.socket, Cptr( PSOCKADDR, @sa ), Len( sa ) ) = SOCKET_ERROR ) Then 'print "error calling bind: "; WSAGetLastError( ) Return FALSE End If If( listen( ctx.socket, SOMAXCONN ) = SOCKET_ERROR ) Then Return FALSE End If ctx.clientlisthead = NULL ctx.isrunning = TRUE ctx.globmutex = Mutexcreate( ) ctx.filemutex = Mutexcreate( ) ctx.acceptthread = Threadcreate( @serverAccept ) 'launch accept thread Function = TRUE End Function '----------- TRAY ICON HANDLING -------------- Function WndProc ( Byval hWnd As HWND, Byval message As UINT, Byval wParam As WPARAM, Byval lParam As LPARAM ) As LRESULT Static pt As Point Function = 0 Select Case (message) Case WM_CREATE TB_CREATED = RegisterWindowMessage ("TaskbarCreated") Exit Function Case WM_DESTROY UnregisterClass (szAppName, GetModuleHandle( null )) Shell_NotifyIcon (NIM_DELETE, @note) PostQuitMessage( 0 ) Exit Function Case WM_COMMAND If Loword (wParam)= ID_RUN Then If ctx.isrunning = FALSE Then serverRun( ) 'Listenning on 80 End If If Loword (wParam)= ID_PAUSE Then If ctx.isrunning = TRUE Then serverEnd( ) 'pause End If If Loword (wParam) = ID_EXIT Then DestroyWindow (hWnd) End If Case WM_SHELLNOTIFY If (lParam = WM_RBUTTONDOWN) Or (lParam = WM_LBUTTONDOWN) Then GetCursorPos (@pt) SetForegroundWindow (hWnd) Dim MainMenu As HANDLE Dim FileMenu As HANDLE MainMenu = CreateMenu () FileMenu = CreateMenu () If ctx.isrunning = TRUE Then AppendMenu (FileMenu, MF_STRING Or MF_CHECKED Or MF_GRAYED, ID_RUN, "&Run") AppendMenu (FileMenu, MF_STRING, ID_PAUSE, "&Pause") Else AppendMenu (FileMenu, MF_STRING, ID_RUN, "&Run") AppendMenu (FileMenu, MF_STRING Or MF_CHECKED Or MF_GRAYED, ID_PAUSE, "&Pause") End If AppendMenu (FileMenu, MF_STRING, ID_EXIT, "E&xit") InsertMenu (MainMenu, 0, MF_POPUP, Cuint(FileMenu), "invisible menu") TrackPopupMenuEx (FileMenu, TPM_LEFTALIGN Or TPM_RIGHTBUTTON, pt.x, pt.y, hWnd, NULL) PostMessage (hWnd, WM_NULL, 0, 0) End If Case TB_CREATED Shell_NotifyIcon (NIM_ADD, @note) End Select Function = DefWindowProc( hWnd, message, wParam, lParam ) End Function '---------------- SIMILI-WINMAIN ---------------- Dim hInstance As HINSTANCE hInstance = GetModuleHandle( null ) If Command$ <> "" Then ServerPort = Val(Command$) Endif If( FALSE = serverRun( ) ) Then End Dim wMsg As MSG Dim wcls As WNDCLASS Dim hWnd As HWND szAppName = "SWS" 'already running ? hWnd=FindWindow(szAppName,NULL) If hWnd <> 0 Then End End If With wcls .style = CS_HREDRAW Or CS_VREDRAW .lpfnWndProc = @WndProc .cbClsExtra = 0 .cbWndExtra = 0 .hInstance = hInstance .hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (1)) .hCursor = LoadCursor( NULL, IDC_ARROW ) .hbrBackground = GetStockObject( WHITE_BRUSH ) .lpszMenuName = NULL .lpszClassName = Strptr( szAppName ) End With If( RegisterClass( @wcls ) = FALSE ) Then End End If '' Create the window and _BUT DONT_ show it hWnd = CreateWindowEx( 0, szAppName, "", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL ) note.cbSize = Sizeof (NOTIFYICONDATA) note.hWnd = hWnd note.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (1)) note.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE note.uCallbackMessage = WM_SHELLNOTIFY note.szTip= szAppName Shell_NotifyIcon (NIM_ADD, @note) 'wait for quit message While GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE TranslateMessage( @wMsg ) DispatchMessage( @wMsg ) Wend 'eventually stop server If ctx.isrunning = TRUE Then serverEnd( ) End If