一个简单的http_server的c实现

一.  网络编程函数简介
(一)socket 
  
  int socket(int domain, int type,int protocol) 

  domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等). 
        AF_UNIX只能够用于单一的Unix 系统进程间通信,
        而AF_INET是针对Internet的,因而可以允许在远程 
        主机之间通信(当我们 man socket时发现 domain可选项是 PF_*而不是AF_*,因为glibc是posix的实现所以用PF代替了AF,
        不过我们都可以使用的). 

  type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等) 
        SOCK_STREAM表明我们用的是TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. 
        SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信. 

  protocol:由于我们指定了type,所以这个地方我们一般只要用0来代替就可以了 socket为网络通讯做基本的准备.
  成功时返回文件描述符,失败时返回-1,看errno可知道出错的详细情况. 


(二)bind 
  int bind(int sockfd, struct sockaddr *my_addr, int addrlen) 

  sockfd:是由socket调用返回的文件描述符. 

  addrlen:是sockaddr结构的长度. 

  my_addr:是一个指向sockaddr的指针. 在中有 sockaddr的定义 

        struct sockaddr{
                unisgned short  as_family;
                char            sa_data[14];
        };

  不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in) 来代替.在中有sockaddr_in的定义 
        struct sockaddr_in{
                unsigned short          sin_family;     
                unsigned short int      sin_port;
                struct in_addr          sin_addr;
                unsigned char           sin_zero[8];
        }
  我们主要使用Internet所以
        sin_family一般为AF_INET,
        sin_addr设置为INADDR_ANY表示可以和任何的主机通信,
        sin_port是我们要监听的端口号.sin_zero[8]是用来填充的. 
  bind将本地的端口同socket返回的文件描述符捆绑在一起.成功是返回0,失败的情况和socket一样 

(三)listen 
  int listen(int sockfd,int backlog) 

  sockfd:是bind后的文件描述符. 

  backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时, 使用这个表示可以介绍的排队长度. 
  listen函数将bind的文件描述符变为监听套接字.返回的情况和bind一样. 


(四)accept 
  int accept(int sockfd, struct sockaddr *addr,int *addrlen) 

  sockfd:是listen后的文件描述符. 

  addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了. bind,listen和accept是服务器端用的函数,
  accept调用时,服务器端的程序会一直阻塞到有一个 客户程序发出了连接. accept成功时返回最后的服务器端的文件描述符,
  这个时候服务器端可以向该描述符写信息了. 失败时返回-1 

(五)connect 
   int connect(int sockfd, struct sockaddr * serv_addr,int addrlen) 

   sockfd:socket返回的文件描述符. 

   serv_addr:储存了服务器端的连接信息.其中sin_add是服务端的地址 

   addrlen:serv_addr的长度 

   connect函数是客户端用来同服务端连接的.成功时返回0,sockfd是同服务端通讯的文件描述符 失败时返回-1. 


二.一个简单的http_server实现

#include "stdafx.h"
#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>
#include <stdio.h>
 
void http_response( SOCKET con, /*const*/ char *request )
{
    /* get the method */
    char *token = strtok( request, " " );
    char *uri = strtok( 0, " " );
    //char *uri1 = strtok( 0, " " );
    char file[64];
    sprintf( file, ".%s", uri );
 
    
        /* load the file content */
        FILE *fp = fopen( file, "rb" );
        if( fp == 0 )
        {
            /* response 404 status code */
            char response[] = "HTTP/1.1 404 NOT FOUND\r\n\r\n";
            send( con, response, strlen( response ), 0 );
        }
        else
        {
            /* response the resource */
            /* first, load the file */
            int file_size ;
            char *content;
int time = 0;
            char response[1024];
            fseek( fp, 0, SEEK_END );
            file_size = ftell( fp );
            fseek( fp, 0, SEEK_SET );
            content = (char*)malloc( file_size + 1 );
            fread( content, file_size, 1, fp );
            content[file_size] = 0;
sprintf( response, "HTTP/1.1 200 OK\r\nset-cookie: name=name;max-age=60\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n%s", file_size, content );
printf( response );
            send( con, response, strlen( response ), 0 );
            free( content );
        }
    
}
 
BOOL GetCurrentPerformanceCounter(LONGLONG * count)
{
LARGE_INTEGER lCount;
BOOL result;

result = QueryPerformanceCounter(&lCount);
if(result)
{
*count = lCount.QuadPart;

}
return result;
}

double CompareSystemTime(LONGLONG countStart, LONGLONG countEnd)
{
LARGE_INTEGER lFrequency;
LONGLONG interval;
BOOL result;
double time;

interval = countEnd - countStart;


result = QueryPerformanceFrequency(&lFrequency);
if(result)
{

time = (double)interval/lFrequency.QuadPart;
}
return time;
}

int _tmain(int argc, _TCHAR* argv[])
{
start:
    WSADATA wd;
    int ret;
    SOCKET s;
LONGLONG countStart, countEnd;
double timeInterval = 0.0;
    ret = WSAStartup( MAKEWORD( 2, 0 ), &wd );
    if( ret < 0 )
    {
        fprintf( stderr, "winsock startup failed\n" );
        exit( -1 );
    }
    s = socket( AF_INET, SOCK_STREAM, 0 ); //第二个参数用SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信. 
    /* bind and listen */
struct sockaddr_in addr;
    memset( &addr, 0, sizeof( addr ) );//将已开辟内存空间 addr 的首 sizeof( addr ) 个字节的值设为值 0。
    addr.sin_family = AF_INET;
    addr.sin_port = htons( 12345 );
    addr.sin_addr.s_addr = inet_addr( "0.0.0.0" );

    ret = bind( s, (struct sockaddr*)&addr, sizeof( addr ) );//bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,如果没有错误发生,bind()返回0。否则返回值SOCKET_ERROR。
    if( ret < 0 ) 
    {
        fprintf( stderr, "bind failed\n" );
        closesocket( s );
        exit( -1 );
    }
ret = listen( s, 1024 );
if( ret < 0 )
{
fprintf( stderr, "listen failed\n" );
    closesocket( s );
    exit( -1 );
}
SOCKET con;
    while(ret >= 0)
{
GetCurrentPerformanceCounter(&countStart);
        con = accept( s, 0, 0 );//调用accept()后,服务器等待从编号为s的套接字上接受客户连接请求,而连接请求是由客户方的connect()调用发出的。当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr 和addrlen,并创建一个与s有相同特性的新套接字号。新的套接字可用于处理服务器并发请求。
        /* recv request */
GetCurrentPerformanceCounter(&countEnd);
timeInterval = CompareSystemTime(countStart,countEnd);
if(timeInterval < 30)
{
char request[1024] = { 0 };
ret = recv( con, request, sizeof( request ), 0 );
printf( request );
http_response( con, request );
closesocket( con );
}
else
{
closesocket( con );
closesocket( s );
WSACleanup();
goto start;
}
}
    closesocket( s );
    WSACleanup();
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值