[C#] 콘솔 코드로 Java의 Servlet에 접속하는 방법(Post 데이터 값 전송과 파일 전송)


Development note/C#  2021. 2. 14. 21:33

안녕하세요. 명월입니다.

 

이 글은 C#의 콘솔 코드로 Java의 Servlet에 접속하는 방법(Post 데이터 값 전송과 파일 전송)에 대한 글입니다.

 

이전의 Java Servlet에서 C#의 RestAPI를 통해 접속하는 방법의 글에서 어떤 분이 Java Servlet에서 C# 콘솔로 접속하는 방법은 알겠는데 C# 콘솔에서 Java Servlet으로 접속하는 방법에 대해 물어보셔서 그것에 대한 답글입니다.

링크 - [C#] Java servlet에서 C#의 RestAPI를 통해 통신하는 방법

 

위 글에서는 사실 C#은 서버를 만들지 않는 이상 Java Servlet에서 C# 콘솔로 접속하는 게 어렵습니다. 왜냐면 콘솔 프로그램이 켜져 있는 것이 아니기 때문에...

그래서 제목은 Java Servlet에서 C#의 RestAPI를 통해 접속하는 방법이라고 한 것이지만, 사실 C# 콘솔 코드로 웹서버를 만드는 방법이 맞는 것이겠네요...

즉, 반대의 경우는 Java Servlet이 웹 서버 프로그래밍이기 때문에 접속하는 게 어렵지 않습니다.

 

그것에 관한 글을 찾아보니깐 이미 작성을 했더군요..

링크 - [C#] HttpConnection을 이용해서 웹 페이지 가져오기

링크 - [C#] 웹 서버로 HttpWebRequest를 이용하여 파일 업로드하는 방법

 

근데 위 글은 Java가 아닌 PHP로 예를 들었기 때문에.. 제가 Java로 다시 설명하겠습니다.

 

먼저 eclipse로 Java sevlet 프로젝트를 열고 index.jsp파일과 Test 서블릿 클래스 파일을 만들겠습니다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<!-- 해더 설정 -->
<head><title>Servlet Test</title></head>
<body>
  <!-- form 태그 submit 버튼을 누르면 Test servlet으로 POST형식을 요청한다. -->
  <form method="POST" action="Test?GetStringParam=helloworld">
    <!-- 파라미터는 PostParam의 이름으로 nowonbun의 값을 전송한다. -->
    <input type="text" name="PostParam" value="nowonbun">
    <!-- Submit 버튼 -->
    <input type="submit">
  </form>
</body>
</html>
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// Test 서블릿
@WebServlet("/Test")
public class Test extends HttpServlet {
  private static final long serialVersionUID = 1L;
  // 생성자
  public Test() {
    super();
  }
  // Method GET으로 요청이오면 호출되는 함수
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // HTTP 결과 값을 "Served at 호스트 페이지, 메소드 형식"으로  출력한다.
    response.getWriter().append("Served at : ").append(request.getContextPath()).append(" Method : GET");
    // 콘솔 출력
    System.out.println(request.getParameter("GetStringParam"));
  }
  // Method POST으로 요청이오면 호출되는 함수
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // HTTP 결과 값을 "Served at 호스트 페이지, 메소드 형식"으로  출력한다.
    response.getWriter().append("Served at : ").append(request.getContextPath()).append(" Method : POST");
    // 콘솔 출력
    System.out.println(request.getParameter("PostParam"));
  }
}

그리고 브라우저로 접속합니다.

그리고 브라우저 URL에 직접 /Test?GetStringParam=helloworld도 접속해 봅니다.

그리고 eclipse에서 GetString Parameter의 값이 제대로 콘솔에 출력이 되는지도 확인합니다.

Servlet 환경을 만들었습니다. 이제 C#에서 POST형식으로 접속을 해봅시다.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Http;

namespace Example
{
  class Program
  {
    // HttpRequest를 실행하는 함수
    public static string GetRequest(String url, HttpMethod method, object param = null)
    {
      // 파라미터가 있을 경우, 익명 클래스로 만들기 때문에 Reflection을 이용해서 데이터를 가져온다.
      if (param != null)
      {
        // 버퍼
        StringBuilder parameter = new StringBuilder();
        // 파라미터키=파라미터값&파라미터키=파라미터값&파라미터키=파라미터값 의 형태로 만든다.
        foreach (var p in param.GetType().GetProperties())
        {
          // 파라미터가 있으면 &표시로..
          if (parameter.Length > 0)
          {
            parameter.Append("&");
          }
          // 파라미터 추가한다.
          parameter.AppendFormat("{0}={1}", p.Name, p.GetValue(param));
        }
        // 버퍼를 String 형식으로 변환
        param = parameter.ToString();
      }
      else
      {
        param = "";
      }
      // Http method가 GET 방식의 경우 파라미터를 url 주소 뒤에 붙인다.
      if (HttpMethod.Get.Equals(method))
      {
        // URL에 이미 파라미터가 있으면
        if (url.Contains("?"))
        {
          // 기존 파라미터로 연결한다.
          url += "&" + param;
        }
        else
        {
          // 파라미터를 생성한다.
          url += "?" + param;
        }
      }
      // url를 통해 HttpWebRequest 클래스를 생성한다.
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
      // 해더의 메소드를 정의한다.
      request.Method = method.ToString();
      // 해더의 ContentType를 정의한다.
      request.ContentType = "application/x-www-form-urlencoded";
      // request에 프로퍼티로 정의되지 않는 해더의 경우는 Indexer의 형식으로 정의할 수 있다.
      // 프로퍼티로 정의된 해더의 경우, 아래와 같이 정의할 경우 에러가 발생한다.
      request.Headers["Upgrade-Insecure-Requests"] = "1";
      // Http method가 POST 방식의 경우, 해더 아래에
      if (HttpMethod.Post.Equals(method))
      {
        // 파라미터를 바이너리로 변환
        byte[] byteArray = Encoding.UTF8.GetBytes((string)param);
        // 바이너리 길이 설정
        request.ContentLength = byteArray.Length;
        // 스트림을 가져온다.
        using (Stream dataStream = request.GetRequestStream())
        {
          // 스트림에 바이너리를 쓴다.
          dataStream.Write(byteArray, 0, byteArray.Length);
        }
      }
      // Http 프로토콜을 접속해서 response 받기
      using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
      {
        // 프로토콜의 반환 코드를 받을 수 있다. (200이면 정상이다.)
        Console.WriteLine((int)response.StatusCode);
        // 스트림으로 반환 결과값을 받는다.
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
        {
          // 스트림을 String형식으로 읽어온다.
          return reader.ReadToEnd();
        }
      }
    }
    //실행 함수
    static void Main(string[] args)
    {
      // 서블릿에 접속한다. POST 형식으로 PostParam의 값은 test
      String html = GetRequest("http://localhost:8080/WebExample/Test", HttpMethod.Post, new { PostParam = "test" });
      // 결과를 콘솔에 출력
      Console.WriteLine(html);

      // 아무키 누르면 종료
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

실행을 하게 되면 서블릿으로 부터 받는 Response 값을 받게 됩니다.

 

eclipse를 확인해서 C#에서 보낸 Post 데이터가 제대로 전송되어 콘솔에 표시되는지 확인해 봅니다.

C# 콘솔 프로그램에서 보낸 데이터가 콘솔에 표시되었습니다.

 

여기까지 단순히 서블릿에 접속하여 데이터를 보내는 것만으로는 내용이 부족하니 이번에는 Servlet에 파일 업로드 소스를 만들고 C# 콘솔로 파일 업로드하는 것까지 만들겠습니다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<!-- 해더 설정 -->
<head><title>Servlet Test</title></head>
<body>
  <!-- 웹 폼 -->
  <form method="POST" action="Test" enctype="multipart/form-data">
    <!-- 파일 업로드 오브젝트 -->
    <input type="file" name="PostParam" value="nowonbun">
    <!-- Submit 버튼 -->
    <input type="submit">
  </form>
</body>
</html>

여기서 파일을 첨부하고 Submit 버튼을 누르면 파일 업로드가 되는 서블릿을 만들겠습니다.

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
// enctype="multipart/form-data"의 대응
@MultipartConfig
// 서블릿 설정
@WebServlet("/Test")
public class Test extends HttpServlet {
  private static final long serialVersionUID = 1L;
  // 생성자
  public Test() {
    super();
  }

  // 파일업로드는 post형식으로 바운더리로 요청이 오기 때문에 doGet은 필요가 없겠네요.
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 해더의 바운더리 영역을 취득한다.
    Collection<Part> parts = request.getParts();
    // 해더 영역을 전부 for문으로 탐색한다.
    for (Part part : parts) {
      // 파일 관계된 바운더리 데이터가 아니면 패스
      if (!part.getHeader("Content-Disposition").contains("filename=")) {
        continue;
      }
      // 해더 바운더리 영역에서 파일 이름을 추출한다.
      String fileName = extractFileName(part.getHeader("Content-Disposition"));
      // 파일 이름이 null이 아니거나 사이즈가 0보다 크면..
      if (fileName != null && part.getSize() > 0) {
        // 저는 임의의 d:\\work 폴더에 업로드 하겠습니다.
        part.write("d:\\work\\" + File.separator + fileName);
        part.delete();
      }
    }
  }

  // 헤더에서 파일 이름을 추출하는 함수
  private String extractFileName(String partHeader) {
    // 바운더리의 해더는 ;의 구분으로 데이터가 있다.
    for (String cd : partHeader.split(";")) {
      // 헤더 이름이 filename으로 시작하는 것이라면
      if (cd.trim().startsWith("filename")) {
        // '='부터 끝까지 따옴표는 제거
        String fileName = cd.substring(cd.indexOf("=") + 1).trim().replace("\"", "");
        // 그리고 디렉토리 표시 된것도 제거
        int index = fileName.lastIndexOf(File.separator);
        // 파일 이름 추출
        return fileName.substring(index + 1);
      }
    }
    // 위 조건에 맞지 않으면 null 리턴
    return null;
  }
}

여기까지 이전에 Servlet에서 파일 업로드하는 방법을 참고했습니다.

링크 - [Java] Servlet 환경 - 파일 업로드(프로그래스바로 파일 업로드 상태를 표시하는 방법)

 

이제 C# 콘솔에서 파일을 업로드합니다.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;

namespace Example
{
  class Program
  {
    // 파일을 바이너리 형식(byte[])으로 변환
    static byte[] ReadFileToBoundary(string filepath, string boundary)
    {
      // 바운더리 구분을 바이너리로 변환
      var boundarybytes = Encoding.ASCII.GetBytes($"\r\n--{boundary}\r\n");
      // 파일 정보 취득하기
      FileInfo fileInfo = new FileInfo(filepath);
      // 바운더리 해더 설정
      var header = Encoding.ASCII.GetBytes($"Content-Disposition: form-data; name=\"{fileInfo.Name}\"; filename=\"{fileInfo.Name}\"\r\nContent-Type: application/octet-stream\r\n\r\n");
      // 스트림으로 읽어오기
      using (Stream stream = new MemoryStream())
      {
        // 바운더리 구분
        stream.Write(boundarybytes, 0, boundarybytes.Length);
        // 해더 설정
        stream.Write(header, 0, header.Length);
        // 파일 바이너리 설정
        using (Stream fileStream = fileInfo.OpenRead())
        {
          fileStream.CopyTo(stream);
        }
        // 반환할 byte[] 설정
        var ret = new byte[stream.Length];
        // Stream seek를 맨앞으로.
        stream.Seek(0, SeekOrigin.Begin);
        // byte로 변환
        stream.Read(ret, 0, ret.Length);
        // 리턴
        return ret;
      }
    }
    // 실행 함수
    static void Main(string[] args)
    {
      // 바운더리 마크 설정
      string boundary = "**boundaryline**";
      // 서블릿에 접속
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:8080/WebExample/Test");
      // 해더 설정 (파일 전송)
      request.ContentType = "multipart/form-data; boundary=" + boundary;
      // 매서드 타입 설정
      request.Method = HttpMethod.Post.ToString();
      // request에 프로퍼티로 정의되지 않는 해더의 경우는 Indexer의 형식으로 정의할 수 있다.
      // 프로퍼티로 정의된 해더의 경우, 아래와 같이 정의할 경우 에러가 발생한다.
      request.Headers["Upgrade-Insecure-Requests"] = "1";
      // KeepAvlie 설정
      request.KeepAlive = true;
      // 파일 읽어와서 바운더리 설정
      var filelist = new List<byte[]>
      {
        ReadFileToBoundary(@"d:\\nowonbuntistory.png", boundary),
        ReadFileToBoundary(@"d:\\nowonbun.png", boundary),
      };
      // 바운더리를 ascii코드로 변환
      var endboundary = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--");
      // Header를 제외한 총 크기
      request.ContentLength = filelist.Sum(x => x.Length) + endboundary.Length;
      // 서버로 전송할 Stream 취득하기
      using (Stream stream = request.GetRequestStream())
      {
        // 바운더리 설정된 파일을 전송
        foreach (var file in filelist)
        {
          stream.Write(file, 0, file.Length);
        }
        // 바운더리 구분 설정
        stream.Write(endboundary, 0, endboundary.Length);
      }
      // 서버에 접속해서 Response 취득하기
      using (var response = request.GetResponse())
      {
        // 서버로 부터 받는 Stream 취득하기
        using (Stream stream2 = response.GetResponseStream())
        {
          // 스트림 Reader로 변환
          using (StreamReader reader2 = new StreamReader(stream2))
          {
            // 콘솔에 표시
            Console.WriteLine(reader2.ReadToEnd());
          }
        }
      }
      // 아무키가 누르세요...
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

저는 이미지 두개를 서버로 전송하겠습니다.

제대로 전송이 되었습니다.

 

여기까지 이전에 C# 콘솔에서 Web 페이지로 파일 업로드하는 방법을 참고했습니다.

링크 - [C#] 웹 서버로 HttpWebRequest를 이용하여 파일 업로드하는 방법

예전에는 PHP로 예제를 만들었는데 요번에는 Java Servlet을 대상으로 작성했습니다.

 

jsp의 경우는 웹 서버이기 때문에 소켓 프로그램밍 조금 안다면 접속 프로그램을 만드는 건 그렇게 어렵지 않습니다.

그리고 이 Tomcat이라는 어플리케이션 서버(WAS)는 예전보다 많은 부분의 성능이 좋아져서 웹 서버 프로그램 뿐만 스레드를 이용해서 여러가지 서버 프로그램으로도 사용이 가능합니다.(게임 서버도 작성 가능합니다.)

즉, 따로 멀티 스레드 서버 프로그램을 작성할 필요없이 그냥 Tomcat에다가 ServerSocket 인스턴스를 생성해서 소켓 서버로도 사용이 가능합니다.

링크 - [Java] Tomcat 서버에서 소켓 서버를 만드는 방법

링크 - [Java] Tomcat의 websocket을 이용해서 socket서버 만들기

 

여기까지 C#의 콘솔 코드로 Java의 Servlet에 접속하는 방법(Post 데이터 값 전송과 파일 전송)에 대한 글이었습니다.

 

궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.