Flask를 사용하여 로컬 서버를 만들어, 이를 공용 IP로 접속하여 데이터를 받으려고 하는데,
HttpException: Connection closed while receiving data 라는 에러가 발생하였습니다.
이걸 해결하기 위해 했던 여러가지 방법을 소개해드리겠습니다! 제가 시도했던 방법들을 순서대로 나열할 것이기 때문에, 어느 순서에서 되게 될지는 모르겠습니다
1. 안드로이드 스튜디오 에뮬레이터 proxy 설정
이걸로 해결했다는 분들 많으시던데, 저는 이걸로 안되더라구요. 실패!
2. 안드로이드 스튜디오 에뮬레이터(플러터) 보안 설정
// xml/network-security-config.xml
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">example.com</domain>
</domain-config>
</network-security-config>
// AndroidManifest.xml
<application
android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
android/app/src/main/res에 xml 폴더를 추가하시고, network-security-config.xml 파일을 만들어주세요.
그리고 첫번째 부분의 코드를 적고 저장한 뒤, AndroidManifest.xml에 두번째 부분의 코드를 적고 저장해주세요.
(개발중에만 사용하고, 실제 배포시에는 빼주시는게 좋아요)
3. Dio 사용 함수 변경
Future<void> play(String url) async {
try {
Map<String, String> header = {'Content-Type': "audio/wav'", "Connection": "keep-alive"};
Directory tempDir = await getTemporaryDirectory();
String tempPath = '${tempDir.path}/temp_audio.wav';
Response response = await Dio(
BaseOptions(
connectTimeout: Duration(seconds: 60),
receiveTimeout: Duration(seconds: 60),
sendTimeout: Duration(seconds: 60),
headers: header,
persistentConnection: true,
method: 'GET',
responseType: ResponseType.bytes
),
).download(url, tempPath,
options: Options(
headers: header,
persistentConnection: false,
), onReceiveProgress: (received, total) {
if (total != -1) {
print('Downloading: ${(received / total * 100).toStringAsFixed(0)}%');
}
});
print('Download Completed: ${response.statusCode}');
} else {
print('File does not exist at path: $tempPath');
}
} on DioError catch (e) {
print('Dio error: $e');
if (e.response != null) {
print('Dio error response data: ${e.response?.data}');
print('Dio error response headers: ${e.response?.headers}');
print('Dio error response statusCode: ${e.response?.statusCode}');
} else {
print('Dio error request: ${e.requestOptions}');
print('Dio error message: ${e.message}');
}
} catch (e) {
print('Error starting player: $e');
}
}
connectionTimeout, receiveTimeout, sendTimeout 등을 바꿨는데, 사실 이거는 굳이 안건드리셔도 될것 같습니다! 이거 바꿨어도 어차피 안됬었거든요… 그래도 적어봤습니다.
여기까지가 클라이언트 측에서 수정한 내용들이고, 이후 수정은 서버에서 하였습니다!
4. Flask 서버 환경 개선
pip install gunicorn
기존의 flask 실행할 때 python app.py 라면, gunicorn을 사용하여 실행할 때는 아래와 같이 실행하면 됩니다.
gunicorn app.app
하지만 우리는 추가 설정을 하기 위해 gunicorn을 설치한 것이니, 추가 설정을 해보도록 하겠습니다.
실행 파일과 같은 위치에 ‘service_config.py’를 만들어주세요.
# service_config.py
# 서버 바인딩 설정
bind = '0.0.0.0:8000'
# 워커 설정
workers = 4 # 워커 프로세스 수
worker_class = 'gevent' # 워커 클래스 (gevent 사용 예시)
worker_connections = 1000 # gevent 워커당 최대 연결 수
# 타임아웃 설정
timeout = 30 # 타임아웃 설정 (초)
keepalive = 2 # 유지 시간 설정 (초)
# 로그 설정
accesslog = '/path/to/access.log' # 접근 로그 파일 경로
errorlog = '/path/to/error.log' # 에러 로그 파일 경로
loglevel = 'info' # 로그 레벨 (debug, info, warning, error, critical)
# 보안 설정
limit_request_line = 4094 # 요청 라인 제한 (바이트)
limit_request_fields = 100 # 요청 헤더 필드 수 제한
limit_request_field_size = 8190 # 요청 헤더 필드 크기 제한 (바이트)
# 기타 설정
preload_app = False # 앱 미리 로드 여부
max_requests = 0 # 워커가 재시작되기 전에 처리할 최대 요청 수
max_requests_jitter = 0 # max_requests에 대한 지터 값 (랜덤하게 추가되는 최대 값)
graceful_timeout = 30 # 우아한 종료를 위한 타임아웃 (초)
본인이 원하는 설정대로 설정하시면 됩니다! 저는 아래와 같이 설정했어요.
# service_config.py
bind = '0.0.0.0:8000'
workers = 4
keepalive = 30
timeout = 30
workers_class = 'gthread'
thread = 10
worker_connections = 10000
timeout = 30
그리고 아래와 같이 실행하시면 됩니다.
gunicorn -c service_config.py app:app
이렇게 4가지 방법을 HttpException: Connection closed while receiving data 문제를 해결했습니다.
아마도 서버의 대역폭 또는 설정이 미흡하여 저런 문제가 생겼던것 같아요.
저와 비슷한 문제를 겪으신 분에게 도움이 되셨으면 좋겠습니다.
읽어주셔서 감사합니다!