[C++] 161 SMTPプロトコル ソースファイルの再構成

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

ようやく『C言語によるTCP/IPネットワークプログラミング』の第2章 SMTPプロトコルに進みます。

まずは第1章と同様に2001年作成のソースコードがビルドできるようにしました。cppファイルが3つに増えたので、srcディレクトリなどを作成し、オブジェクトファイルやヘッダファイルを別々に管理するようにしています。

MacOSの場合はそのままではgetuid関数やmalloc関数がないというエラーが出ますが、それぞれunistd.h、stdlib.hをインクルードすれば解決します。

骨が折れる作業ですが、以前手がけたゲームプログラミングの本に比べたら大分楽です。汎用性が低かったゲームのコードに比べ、得られた知識を積み上げてメーラーやサーバーアプリ製作などの成果を上げられそうなのでモチベーションは高いです。

これから動作確認を行います。

getuidに関するMacOSマニュアルの説明
COMPILER = clang++
CPPFLAGS = -g -w -std=c++98

INCLUDE = -I./include
LDLIBS = 

# ソースファイル
SRCDIR = ./src
SRCS = $(shell find $(SRCDIR) -type f)

# オブジェクトファイル
OBJDIR = ./obj
OBJS = $(addprefix $(OBJDIR), $(patsubst ./src/%.cpp,/%.o,$(SRCS)))

# 実行ファイル
TARGETDIR = ./bin
TARGET = mailtest

# cppファイルからoファイル作成 $<:依存ファイル
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
	$(COMPILER) $(CPPFLAGS) $(INCLUDE) $(DEBUG) -o $@ -c $<

# oファイルから実行ファイル作成
$(TARGET):$(OBJS)
	$(COMPILER) -o $(TARGETDIR)/$@ $(OBJS) $(LDLIBS)

# 全ソース・コンパイル&ビルド
.PHONY:all
all: clean $(OBJS) $(TARGET)

# oファイル・実行ファイル削除
.PHONY:clean
clean:
	rm -rf $(OBJS) $(TARGETDIR)/$(TARGET)
Smtp $ make all
rm -rf ./obj/base64.o ./obj/mailtest.o ./obj/ujtoj.o ./bin/mailtest
clang++ -g -std=c++98 -w -I./include  -o obj/base64.o -c src/base64.cpp
clang++ -g -std=c++98 -w -I./include  -o obj/mailtest.o -c src/mailtest.cpp
clang++ -g -std=c++98 -w -I./include  -o obj/ujtoj.o -c src/ujtoj.cpp
clang++ -o ./bin/mailtest ./obj/base64.o ./obj/mailtest.o ./obj/ujtoj.o 

[C++] 160 HTTPプロトコル 受信データをファイルに保存

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

『C言語によるTCP/IPネットワークプログラミング』サンプルコード読解の続きです。ここら辺になってくると土台にある知見が膨大なので端折りながら理解に努めます。

受信データのヘッダ部は標準出力し、データ部は同名のファイルとして保存します。

それぞれのデータサイズ算出のため下図にポインタの関係をまとめました。

int SOCrecvDataToFile(int soc, const char *filename)
{
	int	width;
	struct timeval	timeout; // 時刻
	fd_set	readOK;  // FD(ファイルディスクリプタ)
	// fd_set	mask;
	char	tmpbuf[8193]; // 8192 = 2^13
	char	*ptr;
	int	size;
	int end;
	int head;
	FILE	*fp;

	if((fp = fopen(filename, "w")) == NULL){
		perror("fopen");
		return(-1);
	}

	FD_ZERO(&readOK); // FD初期化
	FD_SET(soc,&readOK); // FDセット
	width = soc + 1;

	head =0 ;
	end = 0;
	while(1){
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		// readOK = mask;
		// FDを監視
		switch(select(width, &readOK, NULL, NULL ,&timeout)){
			case	-1:
				if(errno!=EINTR){
					perror("select");
					end=1;
				}
				break;
			case	0:
				break;
			default:
				if(FD_ISSET(soc, &readOK)){ // FDを判別
					size = recv(soc, tmpbuf, 8192, 0); // データ受信
					if(size <= 0){
						end = 1;
					}
					else{
						if(head == 0){
							ptr = memstr(tmpbuf, size, "\r\n\r\n", 4); // バイナリ検索
							if(ptr != NULL){
								// ヘッダ部
								fwrite(tmpbuf, ptr-tmpbuf + 4, 1, stdout);
								head=1;
								// データ部 
								fwrite(ptr + 4,size-(ptr-tmpbuf)-4,1,fp);
							}
							else{
								// ヘッダ部
								fwrite(tmpbuf, size, 1, stdout);
							}
						}
						else{
							// データ部 
							fwrite(tmpbuf, size, 1, fp);
						}
					}
				}
				break;
		}
		if(end==1){
			break;
		}
	}

	fclose(fp);

	return(0);
}