C#やJavaでは簡単にhttp Clientプログラムができる。C言語ではソケットを作成するところから必要です。またWindowsとLinux両方対応するように書いたのでプログラムが長くなる。
(bcc32で動作確認済み)
プログラムは、最初にsocketを生成してWebサーバに接続し、テキスト形式でHTTPリクエストメッセージを作成してWebサーバに送信する。
HTTPリクエストメッセージは、複数行から成り立つ一連のデータ列。ここでいう1行とは、終端にCR(キャリッジリターン、16進の0x0d)とLF(ラインフィード、16進の0x0a)を持つデータの単位を示す。ほぼ、通常のテキスト・データの1行と等しくなる。メッセージ・ヘッダとメッセージボディ部に分かれ、両者は空行(単独のCR+LF)で分割される。
httpを理解するには、telnetで手入力でHTTPをしゃべってみるがいい方法。
https://www.softel.co.jp/blogs/tech/archives/263
まずプログラムをリストする:
#include <stdio.h> /* printf, sprintf */
#include <stdlib.h> /* exit, atoi, malloc, free */
// #include <unistd.h> /* read, write, close */
#include <string.h> /* memcpy, memset */
#ifdef __linux__
#include <sys/socket.h> /* socket, connect */
#include <netdb.h> /* struct hostent, gethostbyname */
#include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */
#elif _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib") //Winsock Library
#else
#endif
struct hostent *server;
struct sockaddr_in serv_addr;
int bytes, sent, received;
char response[4096];
int portno;
char *host;
char *path;
void error(const char *msg) { perror(msg); exit(0); }
#ifdef _WIN32
win_send_http(char *message){
WSADATA wsa;
SOCKET s;
printf("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("Failed. Error Code : %d",WSAGetLastError());
return 1;
}
printf("Initialised.\n");
//Create a socket
if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)
{
printf("Could not create socket : %d" , WSAGetLastError());
}
printf("Socket created.\n");
server = gethostbyname(host);
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
//Connect to remote server
if (connect(s , (struct sockaddr *)&serv_addr , sizeof(serv_addr)) < 0)
{
printf("connect failed with error code : %d" , WSAGetLastError());
return 1;
}
puts("Connected");
if( send(s , message , strlen(message) , 0) < 0)
{
printf("Send failed with error code : %d" , WSAGetLastError());
return 1;
}
puts("Data Send\n");
//Receive a reply from the server
if((received = recv(s , response , 2000 , 0)) == SOCKET_ERROR)
{
printf("recv failed with error code : %d" , WSAGetLastError());
}
puts("Reply received\n");
//Add a NULL terminating character to make it a proper string before printing
response[received] = '\0';
puts(response);
closesocket(s);
WSACleanup();
return 0; // everything OK
}
#endif
#ifdef __linux__
linx_send_http(message) {
int sockfd;
int total;
total = strlen(message);
/* lookup the ip address */
server = gethostbyname(host);
if (server == NULL) error("ERROR, no such host");
/* create the socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket");
/* fill in the structure */
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
/* connect the socket */
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
/* send the request */
sent = 0;
do {
bytes = write(sockfd,message+sent,total-sent);
if (bytes < 0)
error("ERROR writing message to socket");
if (bytes == 0)
break;
sent+=bytes;
} while (sent < total);
/* receive the response */
memset(response, 0, sizeof(response));
total = sizeof(response)-1;
received = 0;
printf("Response: \n");
do {
printf("%s", response);
memset(response, 0, sizeof(response));
bytes = recv(sockfd, response, 1024, 0);
if (bytes < 0)
printf("ERROR reading response from socket");
if (bytes == 0)
break;
received+=bytes;
} while (1);
if (received == total)
error("ERROR storing complete response from socket");
/* close the socket */
close(sockfd);
return 0; // everything OK
}
#endif
int main(int argc,char *argv[])
{
int i;
char message[4096];
portno = atoi(argv[2])>0?atoi(argv[2]):80;
host = strlen(argv[1])>0?argv[1]:"localhost";
path = strlen(argv[4])>0?argv[4]:"/";
if (argc < 5) { puts("Parameters: <host> <port> <method> <path> [<data> [<headers>]]"); exit(0); }
/* How big is the message? */
if(!strcmp(argv[3],"GET"))
{
if(argc>5)
sprintf(message,"%s %s%s%s HTTP/1.0\r\nHost: %s\r\n",
strlen(argv[3])>0?argv[3]:"GET", /* method */
path, /* path */
strlen(argv[5])>0?"?":"", /* ? */
strlen(argv[5])>0?argv[5]:"",host); /* query string */
else
sprintf(message,"%s %s HTTP/1.0\r\nHost: %s\r\n",
strlen(argv[3])>0?argv[3]:"GET", /* method */
path,host); /* path */
for(i=6;i<argc;i++) /* headers */
{strcat(message,argv[i]);strcat(message,"\r\n");}
strcat(message,"\r\n"); /* blank line */
}
else
{
sprintf(message,"%s %s HTTP/1.0\r\nHost: %s\r\n",
strlen(argv[3])>0?argv[3]:"POST", /* method */
path,host); /* path */
for(i=6;i<argc;i++) /* headers */
{strcat(message,argv[i]);strcat(message,"\r\n");}
if(argc>5) {
sprintf(message+strlen(message),"Content-Length: %d\r\n",(int)strlen(argv[5]));
sprintf(message+strlen(message),"Content-Type: application/x-www-form-urlencoded\r\n");
}
strcat(message,"\r\n"); /* blank line */
if(argc>5)
strcat(message,argv[5]); /* body */
sprintf(message,"%s %s HTTP/1.0\r\nHost: %s\r\n",
strlen(argv[3])>0?argv[3]:"POST", /* method */
path,host); /* path */
for(i=6;i<argc;i++) /* headers */
{strcat(message,argv[i]);strcat(message,"\r\n");}
if(argc>5) {
sprintf(message+strlen(message),"Content-Length: %d\r\n",(int)strlen(argv[5]));
sprintf(message+strlen(message),"Content-Type: application/x-www-form-urlencoded\r\n");
}
strcat(message,"\r\n"); /* blank line */
if(argc>5)
strcat(message,argv[5]); /* body */
}
printf("Processed\n");
/* What are we going to send? */
printf("Request:\n%s\n",message);
#ifdef _WIN32
win_send_http(message);
#endif
#ifdef __linux__
linx_send_http(message);
#endif
return 0;
}
このプログラムを利用して、例のTinyWebDBを読み書きしてみる。
まずPostで、データを書き込む
C:\Users\chen\Documents\C\http-client>http-client-3 tinydb.work 80 POST /api/storeavalue/ “tag=presentationtimer&value=Just a TEST from C3”
Process 2
Allocating...
Processed
Request:
POST /api/storeavalue/ HTTP/1.0
Host: tinydb.work
Content-Length: 47
Content-Type: application/x-www-form-urlencoded
tag=presentationtimer&value=Just a TEST from C3
Initialising Winsock...Initialised.
Socket created.
Connected
Data Send
Reply received
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 12 Dec 2017 06:56:12 GMT
Content-Type: application/json
Content-Length: 52
Connection: close
X-Powered-By: PHP/5.6.31
Vary: Cookie
Set-Cookie: wfvt_2029330401=5a2f7d8c36e15; expires=Tue, 12-Dec-2017 07:26:12 GMT; Max-Age=1800; path=/; httponly
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-cache, must-revalidate
["STORED","presentationtimer","Just a TEST from C3"]
次は、Getで、書き込んだデータを取得
C:\Users\chen\Documents\C\http-client>http-client-3 tinydb.work 80 POST /api/getvalue/ “tag=presentationtimer”
Process 2
Allocating...
Processed
Request:
POST /api/getvalue/ HTTP/1.0
Host: tinydb.work
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
tag=presentationtimer
Initialising Winsock...Initialised.
Socket created.
Connected
Data Send
Reply received
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 12 Dec 2017 06:57:10 GMT
Content-Type: application/json
Content-Length: 51
Connection: close
X-Powered-By: PHP/5.6.31
Vary: Cookie
Set-Cookie: wfvt_2029330401=5a2f7dc61d61c; expires=Tue, 12-Dec-2017 07:27:10 GMT; Max-Age=1800; path=/; httponly
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-cache, must-revalidate
["VALUE","presentationtimer","Just a TEST from C3"]