ツナドーナツ・技術メモ帳

色々なものを作る過程で分からなかったことなどを書いていきます

PHPファイルはあるのにjava.io.FileNotFoundExceptionというエラーがでる

PHPでsocket_readをfor文で囲むとバグりました。
例えば、ソケット通信でこんなコードを書いたとします。

for($i=0; i < 3; i++){
                $result = socket_read($socket, 512);
                echo $result;
        }

これをやると
W/System.err: java.io.FileNotFoundException: ”PHPのファイル名"
というエラーが出てきます。
サーバ側にPHPファイルがあるのに、無いぞと言われる。
Googleで検索かけてもそういったことは出てこない。
応急処置としてsocket_readをfor文の外に出したけど、原因がわからないので気持ち悪い。
原因がわかったらまた書きます。

error: duplicate attribute.とは?

error: duplicate attribute.というエラーが初めて出てきたので一応メモ。
これは、AndroidXMLで宣言が重複しているというエラーです。
例えばこのコードでは、Bootstrapとandroidのlayout_constraintHorizontal_biasが重複して定義されています。

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="36dp"
        android:scaleType="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.659"<!-- ここと -->
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView3"
        app:srcCompat="@android:drawable/ic_menu_report_image"
        bootstrap:layout_constraintHorizontal_bias="0.5"<!-- ここ -->
        bootstrap:layout_constraintStart_toEndOf="@+id/progressBar" />

このように宣言が重複してしまうとbuildの段階で止まりますので、どちらか一方の宣言だけを残してあげれば動くようになります。
bootstrapでデザインを整えたいのであれば、android側の宣言を取り消してしまえばOKです。

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="36dp"
        android:scaleType="center"
        app:layout_constraintEnd_toEndOf="parent"
        <!-- コメントアウト -->
        <!-- app:layout_constraintHorizontal_bias="0.659" -->
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView3"
        app:srcCompat="@android:drawable/ic_menu_report_image"
        bootstrap:layout_constraintHorizontal_bias="0.5"<!-- bootstrapを使う -->
        bootstrap:layout_constraintStart_toEndOf="@+id/progressBar" />

ajaxでサーバに画像送信

ブラウザ版きのこ診断サイトを作るにあたって必要になったので作ります。
といっても、ほぼすべてQiitaの記事に書いてあるもので何とかなりました。
qiita.com
こちらの記事を参考にしました。
ほとんど記事の通りなので、今回は僕が失敗したことを中心にメモしていきます。


  1. ファイルパスを間違えた

間違えて、「ローカルパス/var/www/html/ファイル名」みたいな感じで書いてしまった。
環境にもよるが、index.htmlと同じ場所にあるなら、ファイル名だけで大丈夫。

  1. JQueryのインポートミス

googleのサポートしているやつをそのまま使おうとして失敗。
間違えてもうリンクのない古いバージョンのものを使ってしまいました。

TkinterのCheckbuttonでチェックを変える方法

Tkinterチェックボックスにすべてチェックとかあったら便利なのでやってみる。

やり方は簡単で、Checkbuttonのハンドルを使ってselect()を実行するだけです。

#ハンドルを取ってくる
handle = []
for i in range(5):
    b = b = tkinter.Checkbutton(text=table[1][i], variable=bl, font=("", fontSize))
    handle.append(b) #これをfor文で回せばいい

#ボタンにチェックを入れる
def allCheck():
    for i in range(len(handle)):
        handle[i].select()

resizeの挙動を勘違いしていた

まずはソースコードを見てください

#include <iostream>
#include <vector>

int main() {
	std::vector<unsigned char> v;

	v.resize(100);
	v.push_back(39);//ミク

	std::cout << "v.size() = " << v.size() << std::endl;

	return 0;
}

これを実行するとこうなります。
f:id:iwanax:20180620093341j:plain


vector変数vをresizeして、変数のサイズを表示するプログラムです。

僕はresize関数は指定した大きさの容量を確保してくれる関数だと思っていました。なので、vのサイズが1になると思っていました。
しかし、実際は「容量」の確保ではなく「要素数」を確保してくれる関数です。

容量と要素数の説明を簡単にすると、
容量:std::vector v[100]; メモリだけ確保
素数:std::vector v[100] = {1, 2, 3, .... , 99, 100}; 配列に格納されている要素の数
みたいな感じです。

なので、容量を確保したいならreserve関数を使わないといけません。もしくは添え字を使って代入していくしかないです。
関数名で挙動を想像するとこういう痛い目をみるので、これからは調べてやるようにします。

Androidでソケット通信 Java(Android)からC++へ

Android端末でソケット通信ができるようになったのでメモ

Androidでソケット通信を行う際は、AndroidManifest.xmlパーミッションを通す必要があります。
packageとapplicationの間に
<uses-permission android:name="android.permission.INTERNET" />
を追記すればOKです。

あと、通信などの重たい処理をする際はUIスレッドとは別のスレッドで処理を行わなければなりません。AsyncTaskで非同期処理させないと止められてしまうので注意です。

Android側のソースコード(MainActivity.java)

package .....

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button sendImageButton = findViewById(R.id.sendImageButton);
        sendImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                byte src_img[] = {10, 20, 30};
                SocketThread sockTask = new SocketThread(src_img);

                sockTask.execute();
            }
        });
    }
}

Android側のソースコード(SocketThread.java)

package ......

import android.os.AsyncTask;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;

public class SocketThread extends AsyncTask<Integer, Integer, Integer> {
    byte src_img[];

    SocketThread(byte src_img[]){
        this.src_img = new byte[src_img.length];
        this.src_img = src_img;
    }

    @Override
    protected Integer doInBackground(Integer... src_img){
        String server = "";//自分の環境のアドレス
        int port = 12345;

        try{
             //ソケット通信
            Socket sock = new Socket(server, port);

            OutputStream os = sock.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            
            //送信部分
            //送信するデータがbyte型なのでwrite一回でも送信できます
            for(int i = 0; i < this.src_img.length; i++) {
                System.out.println(i + "回目  " + this.src_img[i]);
                dos.write(this.src_img[i]);
                dos.flush();
            }

            dos.close();
            sock.close();

        }catch(Exception e){
            e.printStackTrace();
        }
        return 0;
    }
}


ジェネリクス(<Integer, Integer, Integer>のこと)は配列を扱えないらしく、execute(10, 20, 30);という風にいれないといけないらしいです。
なので、インスタンス生成する際にSocketThreadのsrc_imgに値を代入しておくことにしました。画像データの時もこれでいけると思います。


サーバ側のソースコード(SocketThread.cpp)

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>

void print_Array(unsigned char buf[]){
        printf("受信内容 : ");
        for(int i = 0; i < sizeof(buf); i++){
                printf("%d, ", buf[i]);
        }
        printf("\n\n");
}

int main(){
        int sock0;
        int sock;
        struct sockaddr_in addr;
        struct sockaddr_in client;
        socklen_t len;

        sock0 = socket(AF_INET, SOCK_STREAM, 0);

        addr.sin_family = AF_INET;
        addr.sin_port = htons(12345);
        addr.sin_addr.s_addr = INADDR_ANY;

        bind(sock0, (struct sockaddr *)&addr, sizeof(addr));

        listen(sock0, 5);

        len = sizeof(client);
        sock = accept(sock0, (struct sockaddr *)&client, &len);

        unsigned char src_img[24];

        sleep(1);
        int result = recv(sock, &src_img, sizeof(src_img), 0);

        if(result != -1){
                printf("正常に受信できました。\n");
                printf("受信したバイト数 = %d\n", result);
                print_Array(src_img);
        }else{
                printf("受信できませんでした。\n");
                printf("受信した文字数 = %d\n", result);
                print_Array(src_img);
        }

        close(sock0);
        close(sock);

        return 0;
}

C++のほうはいたって普通のもので大丈夫です。

画像をJava→C言語のソケット通信で送りたかった

JavaからCに画像をソケット通信で送ろうとしたが失敗したのでメモ。

原因:画像のヘッダが紛れ込んでしまうため。

JavaではImageIO.read()で画像を読み込むことができます。読み込んだ画像はBufferedImage型の変数に格納します。

BufferedImage readImage = ImageIO.read(new ByteArrayInputStream("画像"));

↑ここで読み込んだものを扱いやすくするためにbyte型の配列に直します。

try{
byte byte_image = new byte;
ByteArrayInputStream bos = new ByteArrayInputStream();
BufferedOutputStream os = new BufferedOutputStream(bos);
imageIO.write(readImage, "png", os);
byte_image = bos.toByteArray();
}catch(Exception e){
}

これでbyte_imageにバイト型でreadImageが入ります。
ただこれ、どうやら画像ヘッダも一緒にバイト型に変換されて代入されているようです。
imageIO.writeは画像を書き込む関数なので、OpenCVのdataのように生データを取得するわけではないんです。そこを勘違いしていました。

Javaをしっかり理解していなかったがためのミスでした。
もはやソケットの問題ですらないです。

解決策としては、
画像ヘッダのチャンクとシグネチャを読むコードを組む
Java用のOpenCVを使ってdataを丸投げする
とかでしょうか。

めんどくさいので、これからはOpenCVで画像を丸投げすることにします(笑)