2008년 10월 24일 금요일

common lisp 에서 \r\n, crlf 의 표현 방법 ( usocket 을 이용한 간단한 http call 예제 )

;;; \r\n 이 자주 쓰이는 표현인데 common lisp 에서는 \r\n 식으로
;;; 표현하면 그냥 이스케이프 되서 못알아먹고 #\Return #\Newline 등으로
;;; 나타내야 되는데 lisp 을 자주 쓰는것도 아니고 어쩌다 한번 쓸려고
;;; 하면 매번 까먹길래 적어둔다.
;;;
;;; 참고로 sbcl 에선 아래결과가 나오는데
;;; (char-code #\Return) -> 13
;;; (char-code #\Linefeed) -> 10
;;; (char-code #\Newline) -> 10
;;; 이게 표준인지는 잘 모르겠다 특히 라인피드와 뉴라인 캐릭터가 같이
;;; 있는건 좀 애매하군. 설마 플래폼마다 Newline 값이 달라지는건가?
;;;
;;; 아래 예제는 usocket 을 이용해서 간단히 http request 를 날려보는
;;; 예제. \r\n 을 스트림으로 쏘는 함수를 만들어서 이용을 했는데 굳이
;;; 이렇게 안하고 format 에서 ~C~C 로 하고 #\Return #\Linefeed 을 대줘도
;;; 된다.
;;;

(require 'usocket)
(use-package :usocket)

(declaim (inline crlf))
(defun crlf (s)
(check-type s stream)
(write-char #\Return s)
(write-char #\Linefeed s))

;;; crlf 함수를 통해서
(with-client-socket (sock stream "www.live.com" 80)
(format stream "GET / HTTP/1.0")
(crlf stream)
(crlf stream)
(force-output stream)
(loop
for buf = (read-line stream nil)
while buf
do (print buf)))


;;; format 에서 바로 써봤다. \r\n 처럼 format 안쪽에서 바로 표현하는
;;; 방법은 모르겠네
(with-client-socket (sock stream "www.live.com" 80)
(format stream "GET / HTTP/1.0~C~C~C~C" #\Return #\Linefeed #\Return #\Linefeed)
(force-output stream)
(loop
for buf = (read-line stream nil)
while buf
do (print buf)))

2008년 10월 21일 화요일

cmake 로 빌드후 떨어진 바이너리를 ftp/http 등으로 업로드 하는 방법

일단 간단히 ftp 로 파일 하나 올리는것만 적어두고 나중에 더 쓰게되면 업데이트해야지.
중요한것은 add_custom_target 으로 curl 을 실행했다는것. 뭐 curl 이 잘되어있으니 앵간한건 죄다 이걸로 처리가 가능하겠지.

curl 사용법은 http://www.cs.sunysb.edu/documentation/curl/index.html 를 참고.

add_custom_target(upload
COMMAND curl -T 올릴파일이름 -u 계정:암호 ftp://localhost/tmp/
WORKING_DIRECTORY ${proto_BINARY_DIR}
COMMENT "ftp uploading main.loud")
물론 이건 최소한의 사용법이고 프로젝트에 추가하려면 주소나 계정은 변수로 뽑아내고 여러 타겟등을 올려야 할테니 macro 를 만들어 써야할거다. 의존성거는것도 잊지말고...

2008년 10월 20일 월요일

cmake 에서 LDFLAGS 를 타겟별로가 아닌 전체값을 바꾸기

CMAKE_EXE_LINKER_FLAGS
CMAKE_SHARED_LINKER_FLAGS
CMAKE_MODULE_LINKER_FLAGS

변수 참고.
전에도 한번 쓴적이 있는데 까먹고 있었네


지금 cmake 로 크로스컴파일 세팅중인데
cmake 가 제안하는 방법대론 죄다 오류가 나길래
그냥 환경변수쪽에서 CC=arm-elf-gcc 잡아주고
CMakeLists.txt 에서 아래 코드 추가해서 대강 돌리고 있다.

# 크로스컴파일 관련 기능들이 이상하게 돌길래 일단은 크로스컴파일을
# 할때마다 CC 환경변수를 잡아주는 식으로 개발중이다. CMAKE_C_COMPILER
# 를 확인해봐서 arm 이란 말이 보이면 -elf2flt 를 옵션에 추가하자.
if(CMAKE_C_COMPILER MATCHES "arm")
# 바이너리 만들때만 넣어주면 되겠지
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -elf2flt)
# -rdynamic 옵션을 꺼버렸다. 끄기전에 message 로 찍어보니 이 옵션
# -하나길래 걍 "" 로 바꿔버렸지.
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
endif(CMAKE_C_COMPILER MATCHES "arm")

회식시간이군.
나중에 재작성.
...
...

증상에 대해서 좀더 자세히 적어둘려고 했는데 귀찮네
버그같은데 어차피 패치될테고.
혹시나 해서 대강 적어두자면

cmake 2.4 시절에는 크로스컴파일을 공식적으로 지원을 안해서 그냥 컴파일러를 나타내는 변수 CMAKE_C_COMPILER 등을 강제로 다른걸로 바꾸고 돌리면 문제가 없었는데 2.6 때 크로스컴파일을 지원한다고 하더니 위 값을 바꾸면 캐시가 어쩌구 하면서 값을 되돌리고 다시 설정을 돌리더라. 내가 코드상에 값을 다시 바꿔놨으니 이게 계속 반복되면서 무한루프를 타더군.. 위 링크 문서에 보면 강제로 컴파일러를 세팅하는 force 뭐시기도 있는데 이것도 마찬가지로 무한루프질을 하고.. 쉘상에서 CC 를 아예 바꾸고 cmake 를 돌릴때만 문제가 없었다. 이게 버그 맞겠지 아니면 내가 잘못 쓴건가.. 그리고 ccache 랑도 같이 쓰고 싶은데 이것도 좀 후지게 도는 문제가 있더라.








2008년 10월 14일 화요일

haskell 로 간단히 echo 짜봤는디..

쓰레드 안쓴놈(동시접속불가) 코드 보기




쓰레드 쓴놈 코드 보기



2008년 10월 8일 수요일

cmake 에서 한벌의 소스로 디파인만 달리해가면서 여러개의 바이너리를 뽑는 방법. COMPILE_DEFINITIONS 속성!

  • 참고링크는 생략 이넘들 버전업이 빠르니.. 그냥 http://www.cmake.org 에서 문서 링크 찾아보자.

제목대로 한벌의 소스로 디파인만 바꿔가면서 바이너리를 뽑아보려고 했는데
add_definitions 가 타겟별로 cflags 를 맥여주는놈이 아니라 좀 난감했다.

적당히 검색해보니 COMPILE_DEFINITIONS 라는게 있네!! 전에는 못봤던거 같은데.. 버전올라가면서 생긴건가.. 어쨌건 이놈을 쓰면 타겟별로 디파인을 달리 주는게 가능해진다.

문서에서 저놈의 존재를 보고 막연히 set_target_properties 로 세팅하면 되겠구나 했더니 안되더라.. 구글링 해보니 set_property 를 쓰더만.. 아 씨바.. 존나 땜질식의 문법.. 나중에 이런기능이 필요하면 다시 삽질할게 뻔하니 적어둔다.

아 추가로 문서보는게 더 빠르지만.. COMPILE_DEFINITIONS_<CONFIG> 식으로 빌드타입(디버그냐 릴리즈냐..)에 따라 알아서 물게 뜨도록 할수도 있다.

cmake_minimum_required(VERSION 2.6)

# 지금 이 디렉토리내의 코드들은 는 project(brpc) 라고 선언된 다른
#
디렉토리의 라이브러리를 쓰고있다.
include_directories(${brpc_SOURCE_DIR})
link_directories(${brpc_BINARY_DIR})

# 컴파일할 소스들
# 내 원래는 그냥 add_executable 안에 줄줄이 나열하는걸 더 좋아하지만
# 소스 한벌을 여러번 써야 하니 변수에 일단 넣어둬야겠지.
set(srcs
main.c
dispatch.c
session.c
methodtable.c
bpacket.c
mac.c
handshake.c
brpc.c
ping.c
env.c
proc.c)

# 로그켠놈을 빌드해보자.
# 그냥 지금까지 쓰던 그대로지. 코드안에서 기본적으로
# ENABLE_LOG_MACROS 을 1 로 정의하는 코드가 있기 때문.
# 사실 명시적으로 여기에도 추가하는게 좋겠지.
add_executable(main ${srcs})
target_link_libraries(main tcp util benc)

# 로그끈놈을 빌드해보자.
# 달리 설명이 필요없지. 저 더러운 set_property 신택스를 보자. 글고
# COMPILE_DEFINITIONS 에 "/DFOO" 식으로 주면 에러난다. "FOO" 로 줘야만
# 한다. 처음에 이렇게 줬다가 오묘한 에러를 내주길래 시간만 버렸다.
add_executable(main.silence ${srcs})
set_property(TARGET main.silence APPEND PROPERTY COMPILE_DEFINITIONS "ENABLE_LOG_MACROS=0")
target_link_libraries(main.silence tcp util benc)




2008년 10월 7일 화요일

variadic macro __VA_ARGS__ 를 이용한 간단한 로그 매크로


개발도중 주석을 쓰느니 로그를 많이 남기는 편인데 항상 비슷한 코드들을 쓰게 되길래 걍 적어둔다. 나중에 필요하면 바로 베낄수 있도록.
#if 1
# define LOG(...) do { fprintf(stdout, "[LOG] " __VA_ARGS__); } while(0) // 일반적인 로그를 남길때
# define ERR(...) do { fprintf(stdout, "[ERR] " __VA_ARGS__); } while(0) // 에러를 남길때
# define WRN(...) do { fprintf(stdout, "[WRN] " __VA_ARGS__); } while(0) // 에러는 아니고 그냥 넘기기는 그런 것들
# define XXX(...) do { fprintf(stdout, "[XXX] " __VA_ARGS__); } while(0) // 이건 한창 디버깅중일때 일시적으로만
#else
# define LOG(...) do {} while(0)
# define ERR(...) do {} while(0)
# define WRN(...) do {} while(0)
# define XXX(...) do {} while(0)
#endif



로그를 너무 많이 찍어대서 눈에 잘 안보이면 안시코드를 쓰던가 하자.
fprintf(stdout, "\e[1;7m[ERR]\e[0m " __VA_ARGS__);
요딴식으로..








SO_KEEPALIVE tcp 연결유지

http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/

tcp 연결의 경우 접속이 끊어진걸 알지 못하는 경우가 있다. 특히나 트래픽이 적은 경우 문제가 된다. 이런 문제때문에 접속유지용으로 별 의미없는 내용을 주고 받는 식으로 구현을 해왔는데 (heartbeat/ping) 마침 시간이 나서 SO_KEEPALIVE 문서를 읽어봤다....

...읽어보고 그냥 지금까지 하던대로 핑퐁 주고받는식으로 짜기로 했지만 읽어둔것 대강 정리만 해둔다.

  • SO_KEEPALIVE 옵션을 켜두면 적절한 시간마다 0 length data 에 ACK 를 켜서 쏜다. 상대방은 굳이 KEEPALIVE 지원이 필요 없다. 길이가 0 라서 스트림식으로 도는 tcp 코드는 수정이 필요없다. 완존 투명.
  • 이때 사용되는 커널변수들은 세가지로 모두 /proc/sys/net/ipv4/ 에서 확인가능
    • tcp_keepalive_time
    • tcp_keepalive_intvl
    • tcp_keepalive_probes
  • 시스템레벨에서 위값을 바꾸는 방법도 소개되어있는데 이건 내 관심밖
  • 프로그램레벨에서 바꿔주려면 setsockopt 로 SOL_TCP 레벨에서 아래 세값이 각각 위의 값에 대응한다.(참고로 SO_KEEPALIVE 는 SOL_SOCKET 레벨이지. 사실 SOL_TCP 레벨값을 수정해본적은 지금껏 없었다..)
    • TCP_KEEPIDLE
    • TCP_KEEPINTVL
    • TCP_KEEPCNT
  • 문서상에서는 위 세가지 값이 시스템전체를 바꾸는게 아니고 해당 소켓에만 적용된다고 한다. 좋군. 그런데 이게 리눅스 외의 다른 OS 들에서도 사용할수 있는 방법인지 확인해보지는 못했다. 만약 이방법을 쓰고자 한다면 반드시 확인해봐야겠지.
  • 코드수정이 어려운경우엔 libkeepalive 라는걸 사용하는 방법도 있다.

SO_KEEPALIVE 를 쓰지 않기로 한 이유는.. ping/pong 구조 구현이 그다지 어렵지 않고 후에 필요하다면 서버쪽에서만 SO_KEEPALIVE 구현을 추가할수 있기 때문에 ping 방식이 문제가 생기더라도 투명하게 수정이 가능하다는점때문.


2009/02/13 추가.
결국 오늘 KEEPALIVE 구현을 추가했다.
SOL_TCP 값이 inet/tcp.h 에 들어있더라.