diff --git a/client.go b/client.go index b0e59ca..c849008 100644 --- a/client.go +++ b/client.go @@ -4,19 +4,36 @@ import ( "bufio" "flag" "fmt" + "io" "net" + "net/http" "os" "strings" ) var ( - host = flag.String("h", "localhost", "Server host") - port = flag.String("p", "26666", "Server port") + host = flag.String("h", "localhost", "Server host") + port = flag.String("p", "26666", "Server port") + proto = flag.String("proto", "tcp", "Protocol to use (tcp, udp, http)") + method = flag.String("method", "get", "HTTP method to use (get, post) - only for http protocol") ) func main() { flag.Parse() + switch strings.ToLower(*proto) { + case "tcp": + runTCPClient() + case "udp": + runUDPClient() + case "http": + runHTTPClient() + default: + fmt.Printf("Unsupported protocol: %s. Use tcp, udp, or http.\n", *proto) + } +} + +func runTCPClient() { // Connect to the server conn, err := net.Dial("tcp", *host+":"+*port) if err != nil { @@ -25,7 +42,7 @@ func main() { } defer conn.Close() - fmt.Printf("Connected to server %s:%s\n", *host, *port) + fmt.Printf("Connected to TCP server %s:%s\n", *host, *port) fmt.Println("Type messages to send to the server (type 'exit' to quit):") // Create a scanner to read from standard input @@ -63,3 +80,134 @@ func main() { fmt.Printf("Error reading input: %v\n", err) } } + +func runUDPClient() { + // Resolve UDP address + addr, err := net.ResolveUDPAddr("udp", *host+":"+*port) + if err != nil { + fmt.Printf("Failed to resolve UDP address: %v\n", err) + return + } + + // Connect to the server + conn, err := net.DialUDP("udp", nil, addr) + if err != nil { + fmt.Printf("Failed to connect to UDP server: %v\n", err) + return + } + defer conn.Close() + + fmt.Printf("Connected to UDP server %s:%s\n", *host, *port) + fmt.Println("Type messages to send to the server (type 'exit' to quit):") + + // Create a scanner to read from standard input + scanner := bufio.NewScanner(os.Stdin) + + // Create a goroutine to read responses from the server + go func() { + buf := make([]byte, 1024) + for { + n, _, err := conn.ReadFromUDP(buf) + if err != nil { + fmt.Printf("Error reading from UDP server: %v\n", err) + return + } + fmt.Printf("Server: %s\n", string(buf[:n])) + } + }() + + // Read from standard input and send to server + for { + if !scanner.Scan() { + break + } + + message := scanner.Text() + if strings.TrimSpace(message) == "exit" { + fmt.Println("Exiting...") + break + } + + // Send message to server + _, err := conn.Write([]byte(message)) + if err != nil { + fmt.Printf("Failed to send message: %v\n", err) + break + } + } + + if err := scanner.Err(); err != nil { + fmt.Printf("Error reading input: %v\n", err) + } +} + +func runHTTPClient() { + // HTTP server runs on port+1 + portInt := parseInt(*port) + httpPort := fmt.Sprintf("%d", portInt+1) + url := fmt.Sprintf("http://%s:%s/ping", *host, httpPort) + + fmt.Printf("Connecting to HTTP server %s:%s\n", *host, httpPort) + fmt.Println("Type messages to send to the server (type 'exit' to quit):") + + // Create a scanner to read from standard input + scanner := bufio.NewScanner(os.Stdin) + + // Read from standard input and send to server + for { + if !scanner.Scan() { + break + } + + message := scanner.Text() + if strings.TrimSpace(message) == "exit" { + fmt.Println("Exiting...") + break + } + + // Send message to server + var resp *http.Response + var err error + + switch strings.ToLower(*method) { + case "post": + resp, err = http.Post(url, "text/plain", strings.NewReader(message)) + case "get": + // For GET requests, we'll add the message as a query parameter + getURL := url + if message != "" { + getURL += "?data=" + message + } + resp, err = http.Get(getURL) + default: + fmt.Printf("Unsupported HTTP method: %s. Use get or post.\n", *method) + return + } + + if err != nil { + fmt.Printf("Failed to send message: %v\n", err) + break + } + + // Read response + body, err := io.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + fmt.Printf("Failed to read response: %v\n", err) + break + } + + fmt.Printf("Server: %s\n", string(body)) + } + + if err := scanner.Err(); err != nil { + fmt.Printf("Error reading input: %v\n", err) + } +} + +// Helper function to convert string to int +func parseInt(s string) int { + var result int + fmt.Sscanf(s, "%d", &result) + return result +} diff --git a/server.go b/server.go index e0a4622..cc27e88 100644 --- a/server.go +++ b/server.go @@ -4,17 +4,29 @@ import ( "bufio" "flag" "fmt" + "io" "log" "net" + "net/http" "os" "os/exec" + "strings" "time" ) var ( - port = flag.String("p", "26666", "Port to listen on") - logFile = flag.String("log", "server.log", "Log file path") - daemon = flag.Bool("d", false, "Run as daemon") + port = flag.String("p", "26666", "Port to listen on") + tcpLog = flag.String("tcp-log", "tcp.log", "TCP log file path") + udpLog = flag.String("udp-log", "udp.log", "UDP log file path") + httpLog = flag.String("http-log", "http.log", "HTTP log file path") + daemon = flag.Bool("d", false, "Run as daemon") +) + +// Logger for each service +var ( + tcpLogger *log.Logger + udpLogger *log.Logger + httpLogger *log.Logger ) func main() { @@ -34,8 +46,14 @@ func main() { if *port != "26666" { args = append(args, "-p", *port) } - if *logFile != "server.log" { - args = append(args, "-log", *logFile) + if *tcpLog != "tcp.log" { + args = append(args, "-tcp-log", *tcpLog) + } + if *udpLog != "udp.log" { + args = append(args, "-udp-log", *udpLog) + } + if *httpLog != "http.log" { + args = append(args, "-http-log", *httpLog) } cmd := exec.Command(executable, args...) @@ -53,51 +71,79 @@ func main() { return } - // Open log file - file, err := os.OpenFile(*logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + // Open log files + tcpFile, err := os.OpenFile(*tcpLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { - log.Fatal("Failed to open log file:", err) + log.Fatal("Failed to open TCP log file:", err) } - defer file.Close() + defer tcpFile.Close() - // Set log output to both file and console - log.SetOutput(file) + udpFile, err := os.OpenFile(*udpLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + log.Fatal("Failed to open UDP log file:", err) + } + defer udpFile.Close() - // Start listening on specified port + httpFile, err := os.OpenFile(*httpLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + log.Fatal("Failed to open HTTP log file:", err) + } + defer httpFile.Close() + + // Create loggers for each service + tcpLogger = log.New(tcpFile, "", 0) + udpLogger = log.New(udpFile, "", 0) + httpLogger = log.New(httpFile, "", 0) + + // Start TCP server + go startTCPServer() + + // Start UDP server + go startUDPServer() + + // Start HTTP server + go startHTTPServer() + + // Wait forever + select {} +} + +func startTCPServer() { + // Start listening on specified port for TCP listener, err := net.Listen("tcp", ":"+*port) if err != nil { - log.Fatal("Failed to start server:", err) + log.Fatal("Failed to start TCP server:", err) } defer listener.Close() - fmt.Printf("Server listening on port %s\n", *port) - log.Printf("Server listening on port %s\n", *port) + fmt.Printf("TCP Server listening on port %s\n", *port) + tcpLogger.Printf("[%s] TCP Server listening on port %s", time.Now().Format("2006-01-02 15:04:05"), *port) for { // Accept incoming connections conn, err := listener.Accept() if err != nil { - log.Printf("Failed to accept connection: %v", err) + tcpLogger.Printf("[%s] Failed to accept connection: %v", time.Now().Format("2006-01-02 15:04:05"), err) continue } // Handle each connection in a separate goroutine - go handleConnection(conn) + go handleTCPConnection(conn) } } -func handleConnection(conn net.Conn) { +func handleTCPConnection(conn net.Conn) { defer conn.Close() // Get connection info remoteAddr := conn.RemoteAddr().(*net.TCPAddr) - timestamp := time.Now().Format("2006-01-02 15:04:05") - - // Print and log connection info - connInfo := fmt.Sprintf("New connection from IP: %s, Port: %d, Timestamp: %s", - remoteAddr.IP, remoteAddr.Port, timestamp) - fmt.Println(connInfo) - log.Println(connInfo) + + // Log when client connects with consistent format + connectTimestamp := time.Now().Format("2006-01-02 15:04:05") + connectMsg := fmt.Sprintf("[%s] [TCP] New connection from IP: %s, Port: %d", + connectTimestamp, remoteAddr.IP, remoteAddr.Port) + fmt.Println(connectMsg) + tcpLogger.Println(connectMsg) // Create a scanner to read messages from the client scanner := bufio.NewScanner(conn) @@ -106,19 +152,154 @@ func handleConnection(conn net.Conn) { msgTimestamp := time.Now().Format("2006-01-02 15:04:05") // Print and log the received message with timestamp - logEntry := fmt.Sprintf("[%s] Received: %s", msgTimestamp, message) + logEntry := fmt.Sprintf("[%s] [TCP] Received: %s", msgTimestamp, message) fmt.Println(logEntry) - log.Println(logEntry) + tcpLogger.Println(logEntry) } if err := scanner.Err(); err != nil { - errorMsg := fmt.Sprintf("Error reading from connection: %v", err) + errorTimestamp := time.Now().Format("2006-01-02 15:04:05") + errorMsg := fmt.Sprintf("[%s] [TCP] Error reading from connection: %v", errorTimestamp, err) fmt.Println(errorMsg) - log.Println(errorMsg) + tcpLogger.Println(errorMsg) } - // Log when client disconnects - disconnectMsg := fmt.Sprintf("Client %s:%d disconnected", remoteAddr.IP, remoteAddr.Port) + // Log when client disconnects with consistent format + disconnectTimestamp := time.Now().Format("2006-01-02 15:04:05") + disconnectMsg := fmt.Sprintf("[%s] [TCP] Client %s:%d disconnected", + disconnectTimestamp, remoteAddr.IP, remoteAddr.Port) fmt.Println(disconnectMsg) - log.Println(disconnectMsg) + tcpLogger.Println(disconnectMsg) +} + +func startUDPServer() { + // Start listening on specified port for UDP + addr, err := net.ResolveUDPAddr("udp", ":"+*port) + if err != nil { + log.Fatal("Failed to resolve UDP address:", err) + } + + conn, err := net.ListenUDP("udp", addr) + if err != nil { + log.Fatal("Failed to start UDP server:", err) + } + defer conn.Close() + + fmt.Printf("UDP Server listening on port %s\n", *port) + udpLogger.Printf("[%s] UDP Server listening on port %s", time.Now().Format("2006-01-02 15:04:05"), *port) + + // Buffer for reading data + buf := make([]byte, 1024) + + for { + // Read from UDP connection + n, remoteAddr, err := conn.ReadFromUDP(buf) + if err != nil { + udpLogger.Printf("[%s] Failed to read UDP message: %v", time.Now().Format("2006-01-02 15:04:05"), err) + continue + } + + // Handle the UDP message + go handleUDPMessage(remoteAddr, buf[:n]) + } +} + +func handleUDPMessage(remoteAddr *net.UDPAddr, data []byte) { + // Log when client sends data with consistent format + timestamp := time.Now().Format("2006-01-02 15:04:05") + connectMsg := fmt.Sprintf("[%s] [UDP] New connection from IP: %s, Port: %d", + timestamp, remoteAddr.IP, remoteAddr.Port) + fmt.Println(connectMsg) + udpLogger.Println(connectMsg) + + // Log the received message + message := string(data) + msgTimestamp := time.Now().Format("2006-01-02 15:04:05") + logEntry := fmt.Sprintf("[%s] [UDP] Received: %s", msgTimestamp, message) + fmt.Println(logEntry) + udpLogger.Println(logEntry) +} + +func startHTTPServer() { + // HTTP server will run on port+1 + httpPort := fmt.Sprintf("%d", parseInt(*port)+1) + + // Register ping handler for both GET and POST + http.HandleFunc("/ping", pingHandler) + + fmt.Printf("HTTP Server listening on port %s\n", httpPort) + httpLogger.Printf("[%s] HTTP Server listening on port %s", time.Now().Format("2006-01-02 15:04:05"), httpPort) + + // Start HTTP server + log.Fatal(http.ListenAndServe(":"+httpPort, nil)) +} + +func pingHandler(w http.ResponseWriter, r *http.Request) { + // Log the request + timestamp := time.Now().Format("2006-01-02 15:04:05") + method := r.Method + remoteAddr := r.RemoteAddr + + // Parse remote address + var ip string + var port string + if strings.Contains(remoteAddr, ":") { + parts := strings.Split(remoteAddr, ":") + ip = strings.Join(parts[:len(parts)-1], ":") + port = parts[len(parts)-1] + } else { + ip = remoteAddr + port = "unknown" + } + + // Log connection + connectMsg := fmt.Sprintf("[%s] [HTTP] New %s request from IP: %s, Port: %s", + timestamp, method, ip, port) + fmt.Println(connectMsg) + httpLogger.Println(connectMsg) + + // Read request body for POST requests + var body string + if r.Method == "POST" { + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + errorMsg := fmt.Sprintf("[%s] [HTTP] Error reading request body: %v", + time.Now().Format("2006-01-02 15:04:05"), err) + fmt.Println(errorMsg) + httpLogger.Println(errorMsg) + } else { + body = string(bodyBytes) + } + } else if r.Method == "GET" { + // For GET requests, collect query parameters + query := r.URL.Query() + if len(query) > 0 { + body = fmt.Sprintf("Query params: %v", query) + } + } + + // Log the received data + if body != "" { + msgTimestamp := time.Now().Format("2006-01-02 15:04:05") + logEntry := fmt.Sprintf("[%s] [HTTP] Received: %s", msgTimestamp, body) + fmt.Println(logEntry) + httpLogger.Println(logEntry) + } + + // Send response + response := "pong" + w.WriteHeader(http.StatusOK) + w.Write([]byte(response)) + + // Log response + responseMsg := fmt.Sprintf("[%s] [HTTP] Sent: %s", time.Now().Format("2006-01-02 15:04:05"), response) + fmt.Println(responseMsg) + httpLogger.Println(responseMsg) +} + +// Helper function to convert string to int +func parseInt(s string) int { + var result int + fmt.Sscanf(s, "%d", &result) + return result }