次のコード行では、リクエスト行 (GET
またはHEAD <filename> HTTP/1.0
) を取得して、ファイルに関する情報を取得しています。
これは、html/text ファイルと image/gif ファイルに対して正しく機能しています。
しかし、ディレクトリを に入れると、プログラムはそれがソケットまたは不明であると通知しますが、ディレクトリであると通知するはずです。
UNIXman
の例からコードをコピーしたため、奇妙に思えます。コードの例でman
は、ディレクトリを引数として指定すると、ディレクトリが認識されます。
唯一の違いは、プログラムが実行中にユーザー入力 (クライアントサーバー) からファイル (またはディレクトリ) 名を取得するのに対し、UNIX プログラムはコマンドライン引数から名前を取得することです。
何が問題ですか?
class request {
vector<string> requests;
string message;
bool valid, isGET, isHEAD;
public:
explicit request(char line[]): requests(split_string(line)), valid(true) {
struct stat sb;
if(stat(requests[1].c_str(), &sb) == -1) {
cerr << "Usage: GET or HEAD (filename) HTTP/1.0" << endl;
valid = false;
exit(EXIT_FAILURE);
}
string cont_type = "Content-Type: ";
switch (sb.st_mode & S_IFMT) {
case S_IFBLK: printf("block device\n"); break;
case S_IFCHR: printf("character device\n"); break;
case S_IFDIR: cont_type += "directory\n"; cout << "DIRECTORY" <<endl; break;
case S_IFIFO: printf("FIFO/pipe\n"); break;
case S_IFLNK: printf("symlink\n"); break;
case S_IFREG: {
string ext = requests[1].substr(requests[1].size()-6, 6);
if(ext.find(".html") != string::npos || ext.find(".txt") != string::npos) {cont_type += "text/html file\n";}
else if(ext.find(".jpg") != string::npos || ext.find(".jpeg") != string::npos || ext.find(".gif") != string::npos || ext.find(".png") != string::npos) {cont_type += "image/gif file\n";}
else {cont_type += "regular file\n";}
break;
}
case S_IFSOCK: printf("socket\n"); break;
default: printf("unknown?\n"); break;
}
}
念のため、次を使用して接続できるサーバーであるコード全体を追加しますtelnet localhost 8080
。
#define BUF_LEN 8192
#include<queue>
#include<sstream>
#include<vector>
#include<string>
#include<iostream>
#include<cstring>
#include<cstdlib>
extern "C" {
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/socket.h>
#include<netdb.h>
#include<unistd.h>
#include<stdio.h>
#include<sys/time.h>
}
using std::vector;
using std::string;
using std::cerr;
using std::cout;
using std::endl;
using std::stringstream;
using std::queue;
using std::priority_queue;
char* time_stamp();
class request {
vector<string> requests;
string message;
off_t filesize;
bool valid, isGET, isHEAD;
public:
request() : valid(true) {}
explicit request(char line[]): requests(split_string(line)), valid(true) {
if(requests[0] == "GET") isGET = true;
else if(requests[0] == "HEAD") isHEAD = true;
else {
cerr << "Usage: GET or HEAD (filename) HTTP/1.0" << endl;
}
//chdir();
struct stat sb;
if(stat(requests[1].c_str(), &sb) == -1) {
cerr << "Usage: GET or HEAD (filename) HTTP/1.0" << endl;
valid = false;
// exit(EXIT_FAILURE);
}
filesize = sb.st_size;
string lmstring(ctime(&sb.st_mtime));
string lm = "Last-Modified: " + lmstring;
string cont_type = "Content-Type: ";
switch (sb.st_mode & S_IFMT) {
case S_IFBLK: cout << "block device" << endl; break;
case S_IFCHR: cout << "character device" << endl; break;
case S_IFDIR: cont_type += "directory\n"; cout << "DIRECTORY" <<endl; break;
case S_IFIFO: cout << "FIFO/pipe" << endl; break;
case S_IFLNK: cout << "symlink" << endl; break;
case S_IFREG: {
string ext = requests[1].substr(requests[1].size()-6, 6);
if(ext.find(".html") != string::npos || ext.find(".txt") != string::npos) {cont_type += "text/html file\n";}
else if(ext.find(".jpg") != string::npos || ext.find(".jpeg") != string::npos || ext.find(".gif") != string::npos || ext.find(".png") != string::npos) {cont_type += "image/gif file\n";}
else {cont_type += "regular file\n";}
break;
}
case S_IFSOCK: cout << "socket" << endl; break;
default: cout << "unknown?" << endl; break;
}
stringstream cl_ss;
cl_ss << "Content-Length: " << sb.st_size << '\n';
string cont_len = cl_ss.str();
message = lm + cont_type + cont_len;
FILE * pFile;
long lSize;
char * buffer;
size_t result;
/*
if(isGET) {
pFile = fopen ( requests[1].c_str() , "r" );
if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file:
buffer = (char*) malloc (sizeof(char)*lSize);
if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
// copy the file into the buffer:
result = fread (buffer,1,lSize,pFile);
if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
// the whole file is now loaded in the memory buffer.
// terminate
fclose (pFile);
free (buffer);
}
cout << buffer << endl;*/
}
bool operator<(const request& rhs) const{
return filesize < rhs.filesize;
}
string getMessage() const {
return this->message;
}
vector<string> split_string(char line[]) {
vector<string> vec_str;
char* token;
token = strtok(line, " ");
while (token != NULL) {
string temp_str(token);
vec_str.push_back(temp_str);
token = strtok(NULL, " ");
}
return vec_str;
}
};
int main() {
int status;
struct addrinfo host_info; // The struct that getaddrinfo() fills up with data.
struct addrinfo *host_info_list; // Pointer to the linked list of host_info's.
// The MAN page of getaddrinfo() states "All the other fields in the structure pointed
// to by hints must contain either 0 or a null pointer, as appropriate." When a struct
// is created in C++, it will be given a block of memory. This memory is not necessarily
// empty. Therefore we use the memset function to make sure all fields are NULL.
memset(&host_info, 0, sizeof host_info);
std::cout << "Setting up the structs..." << std::endl;
host_info.ai_family = AF_UNSPEC; // IP version not specified. Can be both.
host_info.ai_socktype = SOCK_STREAM; // Use SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
host_info.ai_flags = AI_PASSIVE; // IP Wildcard
// Now fill up the linked list of host_info struts with ???????????????
status = getaddrinfo(NULL, "8080", &host_info, &host_info_list);
// getaddrinfo returns 0 on success, or some other value when an error occurred.
if (status != 0) std::cout << "getaddrinfo error" << gai_strerror(status);
std::cout << "Creating a socket..." << std::endl;
int socketfd; // The socket descripter
socketfd = socket(host_info_list->ai_family, host_info_list->ai_socktype, host_info_list->ai_protocol);
if(socketfd == -1) std::cout << "socket error " << std::endl;
std::cout << "Binding socket..." << std::endl;
// we use to make the setsockopt() function to make sure the port is not in use
// by a previous execution of our code.
int yes = 1;
status = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
status = bind(socketfd, host_info_list->ai_addr, host_info_list->ai_addrlen);
if (status == -1) std::cout << "bind error" << std::endl;
std::cout << "Listen()ing for connections..." << std::endl;
status = listen(socketfd, 5);
if (status == -1) std::cout << "listen error" << std::endl;
int new_sd;
struct sockaddr_storage their_addr;
socklen_t addr_size = sizeof(their_addr);
new_sd = accept(socketfd, (struct sockaddr *)&their_addr, &addr_size);
if (new_sd == -1) {
std::cout << "listen error" << std::endl;
}
else {
std::cout << "Connection accepted. Using new socketfd : " << new_sd << std::endl;
}
// after success on listen()ing, loop waiting
int done;
ssize_t bytes_received;
char buf[BUF_LEN];
fd_set ready;
while (!done) {
/* fd_set contains the following fields:
typedef struct fd_set {
u_int fd_count; //how many are SET?
SOCKET fd_array[FD_SETSIZE]; //an array of SOCKETs
} fd_set;
*/
FD_ZERO(&ready);
FD_SET(new_sd, &ready);
FD_SET(fileno(stdin), &ready);
if (select((new_sd + 1), &ready, 0, 0, 0) < 0) {
cerr << "Error from select" << endl;
std::exit(1);
}
if (FD_ISSET(fileno(stdin), &ready)) {
if((bytes_received = read(fileno(stdin), buf, BUF_LEN)) <= 0)
done++;
send(new_sd, buf, bytes_received, 0);
}
std::cout << "Waiting to receive data..." << std::endl;
bytes_received = recv(new_sd, buf, BUF_LEN, 0); // recv or recvfrom???
// If no data arrives, the program will just wait here until some data arrives.
if (bytes_received == 0) std::cout << "host shut down." << std::endl;
if (bytes_received == -1) std::cout << "receive error!" << std::endl;
std::cout << bytes_received << " bytes received: " << std::endl;
buf[bytes_received] = '\0';
std::cout << "elements of 'buf'" << buf << std::endl;
std::cout << "Send()ing back a message..." << std::endl;
request tobeadded(buf);
request rq;
if(true) {
queue<request> qr;
qr.push(tobeadded);
rq = qr.front();
qr.pop();
}
else {
priority_queue<request> pqr;
pqr.push(tobeadded);
rq = pqr.top();
pqr.pop();
}
string timeheader = "Time: ";
const char* t_h = timeheader.c_str();
char* timestamp = time_stamp();
string server = "Server: myhttp\n";
const char* serv = server.c_str();
const char* lm_ct_cl = rq.getMessage().c_str();
char *msg = new char[strlen(t_h) + strlen(timestamp) + strlen(serv) + strlen(lm_ct_cl) + 1];
*msg = '\0';
strcat(msg, t_h);
strcat(msg, timestamp);
strcat(msg, serv);
strcat(msg, lm_ct_cl);
int len;
ssize_t bytes_sent;
len = strlen(msg);
bytes_sent = send(new_sd, msg, len, 0);
//write(fileno(stdout), buf, bytes_received);
}
std::cout << "Stopping server..." << std::endl;
freeaddrinfo(host_info_list);
close(new_sd);
close(socketfd);
return 0;
}
char* time_stamp() {
time_t current_time;
char* c_time_string;
/* Obtain current time as seconds elapsed since the Epoch. */
current_time = time(NULL);
if (current_time == ((time_t)-1))
{
(void) fprintf(stderr, "Failure to compute the current time.");
}
/* Convert to local time format. */
c_time_string = asctime(gmtime(¤t_time));
if (c_time_string == NULL)
{
(void) fprintf(stderr, "Failure to convert the current time.");
}
c_time_string[strlen(c_time_string)-1] = ' ';
return strcat(c_time_string, " GMT\n");
}