なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

いろいろな言語でHttpClient

全てのコードが最適な形で書かれている訳ではないことをご了承ください。あくまで自分の実験的なコードです。参考にしたサイトは一番下にまとめて記述しています。

Java

package foo.bar;

import java.io.*;
import java.net.*;

public class HttpClient {
	public static void main(String args[]){

		String host = "hatsukari.2ch.net";
		String path = "/news/subject.txt";
		int port = 80;


		String line;
		Socket socket;
		BufferedReader reader;
		BufferedWriter writer;


		try {
			socket = new Socket(host, port);
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			writer.write("GET " + path + " HTTP/1.1\r\n");
			writer.write("Accept-Encoding: gzip \r\n");
			writer.write("Host: " + host + ":" + port + "\r\n");
			writer.write("Accept: */*\r\n");
			writer.write("Referer: http://hatsukari.2ch.net/news/ \r\n");
			writer.write("Accept-Language: ja \r\n");
			writer.write("User-Agent: Mozilla/5.0 \r\n");
			writer.write("Connection: close \r\n\r\n");
			writer.flush();

			
			
			while ((line = reader.readLine()) != null) {
			    System.out.println(line);
			}


			reader.close();
			writer.close();
			socket.close();

		} catch (UnknownHostException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (IOException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		}
	}
}

C#

同じくgzipの解凍ができてない上にかなりいい加減

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HttpClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //GETリクエストを送信するサーバー名とパス
            string server = "hatsukari.2ch.net";
            //リクエストメッセージを作成する
            string reqMsg = "GET /news/subject.txt HTTP/1.1 \r\n" +
                "Accept-Encoding: gzip \r\n" +
                "Host: hatsukari.2ch.net \r\n" +
                "Accept: */*\r\n" +
                "Referer: http://hatsukari.2ch.net/news/ \r\n" +
                "Accept-Language: ja \r\n" +
                "User-Agent: Mozilla/5.0 \r\n" +
                "Connection: close \r\n\r\n";

            //文字列をbyte配列に変換
            System.Text.Encoding enc =
                System.Text.Encoding.GetEncoding("shift_jis");
            byte[] reqBytes = enc.GetBytes(reqMsg);

            //ホスト名からIPアドレスを取得
            System.Net.IPAddress hostadd =
                System.Net.Dns.Resolve(server).AddressList[0];

            System.Net.IPEndPoint ephost =
                new System.Net.IPEndPoint(hostadd, 80);

            //Socketの作成
            System.Net.Sockets.Socket sock =
                new System.Net.Sockets.Socket(
                System.Net.Sockets.AddressFamily.InterNetwork,
                System.Net.Sockets.SocketType.Stream,
                System.Net.Sockets.ProtocolType.Tcp);

            //接続
            sock.Connect(ephost);

            //リクエストメッセージを送信
            sock.Send(reqBytes, reqBytes.Length,
                System.Net.Sockets.SocketFlags.None);

            //受信する
            byte[] resBytes = new byte[1024];
            System.IO.MemoryStream mem = new System.IO.MemoryStream();
            while (true)
            {
                int resSize =
                    sock.Receive(resBytes, resBytes.Length,
                    System.Net.Sockets.SocketFlags.None);
                if (resSize == 0)
                    break;
                mem.Write(resBytes, 0, resSize);
            }
            string resMsg = enc.GetString(mem.GetBuffer(), 0, (int)mem.Length);
            mem.Close();

            //閉じる
            sock.Shutdown(System.Net.Sockets.SocketShutdown.Both);
            sock.Close();

            //受信したメッセージを表示する
            Console.WriteLine(resMsg);
        }
    }
}

Boostを使ったC++

2011/11/06 gzipの解凍処理を追加…なんかもっとスマートに書く方法あるんじゃないかな?

#include <fstream>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>

using namespace std;
using namespace boost::asio;
namespace fs = boost::filesystem;

int main()
{
	// ディレクトリ作成
	fs::path dir( "./dat" );
	fs::create_directory( dir );

	// menu.2ch.net の http サービスに接続
	ip::tcp::iostream s( "menu.2ch.net", "http" );

	// 送信
	s << "GET /bbsmenu.html HTTP/1.0 \r\n";
	s << "Accept-Encoding: gzip \r\n";
	s << "Host: menu.2ch.net \r\n";
	s << "Accept:  \r\n";
	s << "Referer: http://menu.2ch.net/ \r\n";
	s << "Accept-Language: ja \r\n";
	s << "User-Agent: Mozilla/5.0 \r\n";
	s << "Connection: close \r\n";
	s << "\r\n";
	s << flush;

	// 受信
	string line;
	bool flag = false;
	// gzipのヘッダ作成
	char HEX[] = {0x1f,0x8b,0x08,0x00};
	//  ios::binary はバイナリ形式で出力
	fs::ofstream outputfilegzip( dir/"BoardList.gzip", std::ios::binary );

	while( getline(s, line) ){
		if ( flag ) {
			// 真の場合
			outputfilegzip << line << endl;
		} else {
			// 偽の場合
			unsigned int loc = line.find( *HEX , 0 );
    		if( loc != string::npos ) {
      			outputfilegzip << line << endl;
      			flag = true;
    		}
		}
	}
	outputfilegzip.close();
	line.clear();
}

win32api風C++

(あとでgzipの変換やらを付け足すつもり…)

// HttpClientWin32.cpp

#include <stdio.h>
#include <WinSock2.h>
#include <string.h>

#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\Lib\\WinInet.Lib")
#pragma comment(lib, "C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\Lib\\WSock32.Lib")

int main(){

	WSADATA wsaData;
	LPHOSTENT lpHost;
	SOCKET s;
	int nRtn;
	SOCKADDR_IN sockadd;

	//やっぱりサーバーは決め打ち
	char* szServer = "hatsukari.2ch.net";
	u_short port = atoi("80");
	//受け取ったHTTP通信のボディだけ受け取るための変数
	boolean record = false;

	char szStrRcv[1024]; 
	char *szStr;

	unsigned int addr;

	//データ保存先のファイルポインタ
	FILE *fp;

	//WinSockの初期化
	if (WSAStartup(MAKEWORD(1, 1), &wsaData ) != 0 ){
		perror("WSAStartupエラーです\n");
		return -1;
	}
	//ソケットオープン
	s = socket(PF_INET, SOCK_STREAM, 0);
	if (s == INVALID_SOCKET) {
		perror("ソケットをオープンできません\n");
		WSACleanup();
		return -2;
	}
	//名前解決
	lpHost = gethostbyname(szServer);
	if (lpHost == NULL) {
		addr = inet_addr(szServer);
		lpHost = gethostbyaddr((char *)&addr, 4, AF_INET);
		sprintf(szStr, "%sが見つかりません\n", szServer);
		perror(szStr);
		WSACleanup();
		return -3;
	}
	//構造体を用意してやっと接続
	memset(&sockadd, 0, sizeof(sockadd));
	sockadd.sin_family = AF_INET;
	sockadd.sin_port = htons(port);
	sockadd.sin_addr = *((LPIN_ADDR)*lpHost->h_addr_list);
	if (connect(s, (PSOCKADDR)&sockadd, sizeof(sockadd)) != 0){
		perror("サーバーソケットに接続失敗\n");
		closesocket(s);
		WSACleanup();
		return -4;
	}
	//送りつけるリクエスト文をsprintfで生成
	char* szStrBuf = "GET /news/subject.txt HTTP/1.1 \r\nHost: hatsukari.2ch.net \r\nAccept: */*\r\nReferer: http://hatsukari.2ch.net/news/ \r\nAccept-Language: ja \r\nUser-Agent: Mozilla/5.0 \r\nConnection: close \r\n\r\n";		 

	nRtn = send(s, szStrBuf, (int)strlen(szStrBuf), 0);

	//ダウンロードした文章読み出しループ・gzip形式のファイルに追記する
	if ((fp = fopen("data.txt", "a+")) == NULL) {
		printf("ファイルがオープンできんぞコラ\n");
		return -5;
	}
	while(1) {
		//szStrRcvが読み出された文字列の本体
		memset(szStrRcv, '\0', sizeof(szStrRcv));
		nRtn = recv (
			s, szStrRcv, (int)sizeof(szStrRcv) - 1, 0);

		//読みだすものがなくなったらブレーク
		if ( nRtn == 0 ) 
			break;
		//エラーが出たらブレーク
		if ( nRtn == SOCKET_ERROR ) {
			perror("recvエラーです\n");
			break;
		}
		//ファイルに追記
		if ( record == true ){
			fputs(szStrRcv , fp);
		}
		//Content-Type: text/plainが文字列として読み出されたらスイッチをオン
		//if ( strstr( szStrRcv, "Content-Type: text/plain" ) != NULL || record == true ) {
		//	record = true;
		//}
	}

	if ( shutdown(s, SD_BOTH) != 0) {
		perror("シャットダウンに失敗しました\n");
	}

	closesocket(s);
	WSACleanup();
	fclose(fp);

	return 0;
}

Perl

#!C:\strawberry\perl\bin\perl.exe -w

# $Id: http-client.pl,v 1.3 2003/03/23 11:28:03 68user Exp $
# 右のサイトを参考に作成しました http://x68000.q-e-d.net/~68user/net/http-2.html

use strict;
use Encode qw/ decode /;
use Compress::Zlib;

# Socket モジュールを使う
use Socket;

# 接続先ホスト名
my $host = 'hatsukari.2ch.net';

# HTTP プロトコルを使う
my $port = getservbyname('http', 'tcp');

# ホスト名を、IP アドレスの構造体に変換
my $iaddr = inet_aton($host)
        or die "$host は存在しないホストです。\n";

# ポート番号と IP アドレスを構造体に変換
my $sock_addr = pack_sockaddr_in($port, $iaddr);

# ソケット生成
socket(SOCKET, PF_INET, SOCK_STREAM, 0)
	or die "ソケットを生成できません。\n";

# 指定のホストの指定のポートに接続
connect(SOCKET, $sock_addr)
        or die "$host のポート $portに接続できません。\n";

# ファイルハンドル SOCKET をバッファリングしない
select(SOCKET); $|=1; select(STDOUT);

# WWW サーバに HTTP リクエストを送る
print SOCKET "GET /news/subject.txt HTTP/1.1 \r\n";
print SOCKET "Accept-Encoding: gzip \r\n";
print SOCKET "Host: hatsukari.2ch.net \r\n";
print SOCKET "Accept: */*\r\n";
print SOCKET "Referer: http://hatsukari.2ch.net/news/ \r\n";
print SOCKET "Accept-Language: ja \r\n";
print SOCKET "User-Agent: Mozilla/5.0 \r\n";
print SOCKET "Connection: close \r\n";
print SOCKET "\r\n";

# ヘッダ部分を受け取る
while (<SOCKET>){
# 改行のみの行ならループを抜ける
    m/^\r\n$/ and last;
}

# 圧縮されたファイルの入れ物
my $buffer;
while (my $line = <SOCKET>){
	$buffer .= $line
}
# 表示させる文字列
my $showstr = Compress::Zlib::memGunzip($buffer);
my $decode_data = decode ( 'shiftjis' , $showstr );
print $decode_data;

ここで正規表現使ってみたりも面白いよ

# URL 例 http://hatsukari.2ch.net/test/read.cgi/news/1320145754/
my $twochURL = "<a href=\"http://hatsukari.2ch.net/test/read.cgi/news/";
my $endtag = "</a><br>";
$decode_data =~ s/([\d]{10}).dat<>([^\n]+)\n/$twochURL$1\"\/>$2$endtag/gm;

#改行コードを <BR>タグに置換える
$decode_data =~ s/\r\n/<BR>/g;
$decode_data =~ s/\n/<BR>/g;
$decode_data =~ s/\r/<BR>/g;