readv函数将数据从文件描述符读到分散的内存块中,writev函数则将多块分散的内存块一并写入文件描述符
#include
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
//成功时返回发送的字节数,失败时返回-1
//filedes:表示数据传输对象的套接字文件描述符,但该函数并不只限于套接字,因此,可以像read函数一样向其传递文件或标准输出描述符
//iov:iovec结构体数组的地址值,结构体iovec中包含待发送数据的位置和大小信息
//iovcnt:向第二个参数传递的数组长度
#include
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
//成功时返回接收的字节数,失败时返回-1
//filedes:传递接收数据的文件(或套接字)描述符
//iov:包含数据保存位置和大小信息的iovec结构体数组的地址值
//iovcnt:第二个参数中数组的长度
数组iovec结构体的声明如下:
struct iovec
{
void *iov_base; //缓冲地址
size_t iov_len; //缓冲大小
};
响应报文:三部分
状态行:版本、状态吗、短语,以空格隔开,后接CR+LF(回车+换行)
首部航:说明浏览器、服务器或报文主体的一些信息。首部字段名+它的值,以空格隔开,后接CRLF,整个首行结束后,还有一空行将首部行和后面的实体主体分开。
实体主体
请求报文:
从状态机:parse_line函数,从从buffer中解析出一行
main函数中
循环调用recv函数往buffer中读入客户数据,没次读取成功就调用parse_content函数来分析新读入的数据。
parse_content函数首先调用parse_line函数来获取下一行
读取到完整的行,就交给主状态机来处理
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 4096
enum CHECK_STATE { CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT };
enum LINE_STATUS { LINE_OK = 0, LINE_BAD, LINE_OPEN };
enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, FORBIDDEN_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };
static const char* szret[] = { "I get a correct result\n", "Something wrong\n" };
LINE_STATUS parse_line( char* buffer, int& checked_index, int& read_index )
{
char temp;
for ( ; checked_index < read_index; ++checked_index )
{
temp = buffer[ checked_index ];
if ( temp == '\r' )
{
if ( ( checked_index + 1 ) == read_index )
{
return LINE_OPEN;
}
else if ( buffer[ checked_index + 1 ] == '\n' )
{
buffer[ checked_index++ ] = '\0';
buffer[ checked_index++ ] = '\0';
return LINE_OK;
}
return LINE_BAD;
}
else if( temp == '\n' )
{
if( ( checked_index > 1 ) && buffer[ checked_index - 1 ] == '\r' )
{
buffer[ checked_index-1 ] = '\0';
buffer[ checked_index++ ] = '\0';
return LINE_OK;
}
return LINE_BAD;
}
}
return LINE_OPEN;
}
HTTP_CODE parse_requestline( char* szTemp, CHECK_STATE& checkstate )
{
char* szURL = strpbrk( szTemp, " \t" );
if ( ! szURL )
{
return BAD_REQUEST;
}
*szURL++ = '\0';
char* szMethod = szTemp;
if ( strcasecmp( szMethod, "GET" ) == 0 )
{
printf( "The request method is GET\n" );
}
else
{
return BAD_REQUEST;
}
szURL += strspn( szURL, " \t" );
char* szVersion = strpbrk( szURL, " \t" );
if ( ! szVersion )
{
return BAD_REQUEST;
}
*szVersion++ = '\0';
szVersion += strspn( szVersion, " \t" );
if ( strcasecmp( szVersion, "HTTP/1.1" ) != 0 )
{
return BAD_REQUEST;
}
if ( strncasecmp( szURL, "http://", 7 ) == 0 )
{
szURL += 7;
szURL = strchr( szURL, '/' );
}
if ( ! szURL || szURL[ 0 ] != '/' )
{
return BAD_REQUEST;
}
//URLDecode( szURL );
printf( "The request URL is: %s\n", szURL );
checkstate = CHECK_STATE_HEADER;
return NO_REQUEST;
}
HTTP_CODE parse_headers( char* szTemp )
{
if ( szTemp[ 0 ] == '\0' )
{
return GET_REQUEST;
}
else if ( strncasecmp( szTemp, "Host:", 5 ) == 0 )
{
szTemp += 5;
szTemp += strspn( szTemp, " \t" );
printf( "the request host is: %s\n", szTemp );
}
else
{
printf( "I can not handle this header\n" );
}
return NO_REQUEST;
}
HTTP_CODE parse_content( char* buffer, int& checked_index, CHECK_STATE& checkstate, int& read_index, int& start_line )
{
LINE_STATUS linestatus = LINE_OK;
HTTP_CODE retcode = NO_REQUEST;
while( ( linestatus = parse_line( buffer, checked_index, read_index ) ) == LINE_OK )
{
char* szTemp = buffer + start_line;
start_line = checked_index;
switch ( checkstate )
{
case CHECK_STATE_REQUESTLINE:
{
retcode = parse_requestline( szTemp, checkstate );
if ( retcode == BAD_REQUEST )
{
return BAD_REQUEST;
}
break;
}
case CHECK_STATE_HEADER:
{
retcode = parse_headers( szTemp );
if ( retcode == BAD_REQUEST )
{
return BAD_REQUEST;
}
else if ( retcode == GET_REQUEST )
{
return GET_REQUEST;
}
break;
}
default:
{
return INTERNAL_ERROR;
}
}
}
if( linestatus == LINE_OPEN )
{
return NO_REQUEST;
}
else
{
return BAD_REQUEST;
}
}
int main( int argc, char* argv[] )
{
if( argc <= 2 )
{
printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] );
struct sockaddr_in address;
bzero( &address, sizeof( address ) );
address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &address.sin_addr );
address.sin_port = htons( port );
int listenfd = socket( PF_INET, SOCK_STREAM, 0 );
assert( listenfd >= 0 );
int ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );
assert( ret != -1 );
ret = listen( listenfd, 5 );
assert( ret != -1 );
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof( client_address );
int fd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );
if( fd < 0 )
{
printf( "errno is: %d\n", errno );
}
else
{
char buffer[ BUFFER_SIZE ];
memset( buffer, '\0', BUFFER_SIZE );
int data_read = 0;
int read_index = 0;
int checked_index = 0;
int start_line = 0;
CHECK_STATE checkstate = CHECK_STATE_REQUESTLINE;
while( 1 )
{
data_read = recv( fd, buffer + read_index, BUFFER_SIZE - read_index, 0 );
if ( data_read == -1 )
{
printf( "reading failed\n" );
break;
}
else if ( data_read == 0 )
{
printf( "remote client has closed the connection\n" );
break;
}
read_index += data_read;
HTTP_CODE result = parse_content( buffer, checked_index, checkstate, read_index, start_line );
if( result == NO_REQUEST )
{
continue;
}
else if( result == GET_REQUEST )
{
send( fd, szret[0], strlen( szret[0] ), 0 );
break;
}
else
{
send( fd, szret[1], strlen( szret[1] ), 0 );
break;
}
}
close( fd );
}
close( listenfd );
return 0;
}
HTTP应答包含一个状态行,多个头部字段,一个空行和文档内容。其中前3部分内容可能被web服务器放置一块内存中,而文档的内容通常被读入到另一块内存中
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 1024
//定义两种HTTP状态码和状态信息
static const char* status_line[2] = { "200 OK", "500 Internal server error" };
int main( int argc, char* argv[] )
{
if( argc <= 3 )
{
printf( "usage: %s ip_address port_number filename\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] );
const char* file_name = argv[3];
struct sockaddr_in address;
bzero( &address, sizeof( address ) );
address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &address.sin_addr );
address.sin_port = htons( port );
int sock = socket( PF_INET, SOCK_STREAM, 0 );
assert( sock >= 0 );
int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
assert( ret != -1 );
ret = listen( sock, 5 );
assert( ret != -1 );
struct sockaddr_in client;
socklen_t client_addrlength = sizeof( client );
int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
if ( connfd < 0 )
{
printf( "errno is: %d\n", errno );
}
else
{
//用于保存HTTP应答的状态行、头部字段和一个空行的缓冲区
char header_buf[ BUFFER_SIZE ];
memset( header_buf, '\0', BUFFER_SIZE );
//用于存放目标文件内容的应用程序缓冲
char* file_buf;
//用于获取目标文件的属性,比如是否为目录,文件大小等
struct stat file_stat;
//记录目标文件是否有效
bool valid = true;
//缓冲区header_buf目前已经使用了多少字节的空间
int len = 0;
if( stat( file_name, &file_stat ) < 0 )
{
valid = false;
}
else
{
if( S_ISDIR( file_stat.st_mode ) )//目标文件是一个目录
{
valid = false;
}
else if( file_stat.st_mode & S_IROTH )//当期用户有读取目标文件的权限
{
//动态分配缓存区file_buf,并指定其大小为目标文件的大小file_stat.st_size加1,然后将目标文件读入缓存区file_buf中
int fd = open( file_name, O_RDONLY );
file_buf = new char [ file_stat.st_size + 1 ];
memset( file_buf, '\0', file_stat.st_size + 1 );
if ( read( fd, file_buf, file_stat.st_size ) < 0 )
{
valid = false;
}
}
else
{
valid = false;
}
}
//若目标文件有效,发送正常的HTTP应答
if( valid )
{
//
ret = snprintf( header_buf, BUFFER_SIZE-1, "%s %s\r\n", "HTTP/1.1", status_line[0] );
len += ret;
ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "Content-Length: %d\r\n", file_stat.st_size );
len += ret;
ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "%s", "\r\n" );
//iovec封装了一块内存的起始位置和长度
struct iovec iv[2];
iv[ 0 ].iov_base = header_buf;
iv[ 0 ].iov_len = strlen( header_buf );
iv[ 1 ].iov_base = file_buf;
iv[ 1 ].iov_len = file_stat.st_size;
ret = writev( connfd, iv, 2 );
}
else//如果目标文件无效,通知客户端发生内部错误
{
ret = snprintf( header_buf, BUFFER_SIZE-1, "%s %s\r\n", "HTTP/1.1", status_line[1] );
len += ret;
ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "%s", "\r\n" );
send( connfd, header_buf, strlen( header_buf ), 0 );
}
close( connfd );
delete [] file_buf;
}
close( sock );
return 0;
}