質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.61%

C言語を使ってAPIを叩くプログラムの作成に関して(Windows)

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,329

score 8

C言語(コンパイラ型言語)の初心者です。
普段はPythonを使用しているため、わからない点が多く、躓いています。

前提・実現したいこと

現在、C言語でAPIを叩くプログラムを作成しています。
上記のプログラムはWindows上で動作する必要があるので、VisualStudioを使って開発を行い、exeを作成しています。

使用イメージとしては、トークンを取得する際に上記のプログラムを使用します。
特定のURLに対してユーザー名とパスワードを含んだJSONデータをPOST送信し、トークンが含まれたレスポンス(json)を受けるといった形です。

動作環境

Windows 10
Visual Studio 2017

エラーに関して

以下のエラー(レスポンス)が返ってきます。*一部内容を抜いています

HTTP/1.1 400 Bad Request
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, key, secret, Usertoken, Appkey, Appsecret, 
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 1728000
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Fri, 19 Apr 2019 08:08:11 GMT
Content-Length: 265

{"error":{"id":"xxxxxx","code":1100,"level":1,"category":"Username","message":"Bad request","detail":"Invalid json in call to authenticate device","lowLevelError":{},"line":"/go/src/api.go:66"},"statusCode":400}
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close


sendしている内容は以下の通りです。

POST /{path}/auth HTTP/1.1
Host: 192.168.00.10:8080
Content-Type: application/json

{"userName":"hoge,"password":"foo"}

現状のコード

現状、以下の様なコードを使用しています。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#include "cJSON.h"
#include <winsock2.h>

#define BUF_LEN 4096

int main(int argc, char* argv[])
{
    // REST API叩く
    char destination[] = "192.168.00.10"; // 例
    unsigned short port = 8080; // 例
    char httppath[] = "/{path}/auth";// 例
    char httphost[] = "192.168.00.10";// 例
    int dstSocket;
    int result;

    /* sockaddr_in 構造体 */
    struct sockaddr_in dstAddr;

    /* パラメータ */
    char toSendText[BUF_LEN];
    char buf[BUF_LEN];
    int read_size;

    WSADATA data;
    WSAStartup(MAKEWORD(2, 0), &data);

    /* sockaddr_in 構造体のセット */
    memset(&dstAddr, 0, sizeof(dstAddr));
    dstAddr.sin_port = htons(port);
    dstAddr.sin_family = AF_INET;
    dstAddr.sin_addr.s_addr = inet_addr(destination);

    /* ソケット生成 */
    dstSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (dstSocket < 0) {
        printf("%d\n", GetLastError());
        printf("ソケット生成エラー\n");
    }

    /* 接続 */
    result = connect(dstSocket, (struct sockaddr *) &dstAddr, sizeof(dstAddr));
    if (result < 0) {
        printf("%d\n", GetLastError());
        printf("バインドエラー\n");
    }


    /* HTTP プロトコル生成 & サーバに送信 */
    sprintf(toSendText, "POST %s HTTP/1.1\r\n", httppath);
    send(dstSocket, toSendText, strlen(toSendText), 0);
    printf("%s", toSendText);

    sprintf(toSendText, "Host: %s:%d\r\n", httphost, port);
    send(dstSocket, toSendText, strlen(toSendText), 0);
    printf("%s", toSendText);

    sprintf(toSendText, "Content-Type: application/json\r\n", httphost, port);
    send(dstSocket, toSendText, strlen(toSendText), 0);
    printf("%s", toSendText);

    sprintf(toSendText, "\r\n");
    send(dstSocket, toSendText, strlen(toSendText), 0);
    printf("%s", toSendText);

    // HTTP Body部の作成
    // json作成
    cJSON *auth = cJSON_CreateObject();

    cJSON_AddStringToObject(auth, "userName", "hoge");
    cJSON_AddStringToObject(auth, "password", "foo");
    sprintf(toSendText, "%s\r\n", cJSON_PrintUnformatted(auth), "str");
    send(dstSocket, toSendText, strlen(toSendText), 0);
    printf("%s", toSendText);

    //response
    puts("result");
    while (1) {
        memset(buf, 0, sizeof(buf));
        read_size = recv(dstSocket, buf, BUF_LEN, 0);
        if (read_size > 0) {
            printf("%s", buf);
        }
        else {
            break;
        }
    }

    closesocket(dstSocket);
    WSACleanup();

    return 0;
}

試したこと

1.APIの動作確認(問題なし)
プログラムが間違えているのか、APIサーバーに問題があるのか確かめるためにPOSTMANを使用してプログラムと同じアドレス、jsonでAPIを叩いたところ問題なくトークンが取得できました。

2.通信内容の比較(問題あり)
POSTMANを使用した際と、プログラムを使用した際に通信の内容がどのように異なっているのかをWiresharkを使用して確かめてみました。
POSTMANを使用した場合、WiresharkにJavaScript Object Notation: application/jsonという項目があり、展開すると送信したJSONデータが表示されます。

それに対して、作成したプログラムでは、JavaScript Object Notationの項目が表示されません。
というより、POSTMANで成功した場合には、ProtocolがHTTPのもので、Info部分にPOST /{path}/auth HTTP/1.1 (application/json)と記載されているパケット(?行の事)がありましたが、プログラムで作成したものに関しては全てTCPでした。
そもそも、リクエストが送信されていないのでしょうか?
個人的には、リクエストヘッダに"Content-Type: application/json"を入れて、bodyにjsonデータを入れればうまくいくといった認識でしたが、違うのでしょうか?

最後に

何か必要な情報等ありましたら、回答をお願いします。
質問を書きながらHTTPプロトコルの生成はしていますが、送信ができていないのでは?と思うところがありますが、具体的にどうすればよいかわからない状況です。

お力をお貸しください。
よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • dodox86

    2019/04/19 17:48

    POSTMANで送ったときのHTTPリクエストの内容と、完全に同じですか? "Content-Length:" フィールドが無いとか違いはありませんか?
    > そもそも、リクエストが送信されていないのでしょうか?
    sendの返り値を逐一、チェックしてみてください。また、Windowsの場合、connectやsend等のソケットAPIはGetLastError ではなく、WSAGetLastErrorを使います。
    https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-send

    キャンセル

  • asm

    2019/04/19 19:14

    > sendしている内容は以下の通りです。
    > {"userName":"hoge,"password":"foo"}

    "hoge"の後ろのダブルクオーテーションが無いけどコピペミスでしょうか?

    キャンセル

回答 1

+1

こんにちは。

折角WireSharkでみているのでしたら、バイナリの部分を見比べたら相違がある筈です。その相違をなくせば通る筈ですよ。

httpのこのレベルのプロトコルには詳しくないのですが、リクエストをパケット分割する際には分割されていることがサーバ側で分からないと解析のしようがない筈です。ですが、それっぽいデータは付いていないように見えます。
このリクエストは1パケットで十分に送れそうですから、1回のsendで全て送ってみてはどうでしょう?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る