c语言编写一个简单的支持并发的服务器<终极版>

这次修改了请求处理代码,在每次请求的时候创建一个线程来处理,使服务器支持并发请求
编写服务器源代码winhttp.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <pthread.h>

#define SERVER_PORT 80

static int debug =1;

void* do_http_request(void* pclient_socket);



int header(int client_sock,FILE *resource);

void cat(int client_sock,FILE *resource);



void not_found(int client_sock);
void inner_error(int client_sock);
void unimplemented(int client_sock);
void bad_request(int client_sock);


void do_http_response_v2(int client_sock,const char *path);



void do_http_response(int client_sock); 

int get_line(int sock,char *buf,int size);



int main(){

    int sock;//
    struct sockaddr_in server_addr;

    sock=socket(AF_INET,SOCK_STREAM,0);

    bzero(&server_addr,sizeof(server_addr));

    server_addr.sin_family=AF_INET;//选择协议族
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//监听本地所有IP地址

    server_addr.sin_port=htons(SERVER_PORT);//绑定端口号

    bind(sock,(struct sockaddr *)&server_addr,sizeof(server_addr));


    listen(sock,120);

    printf("等待客户端的连接\n");
    

    int done=1;

    while(done){
        struct sockaddr_in client;
        int client_sock,len,i;
        char client_ip[64];
        char buf[256];

        pthread_t id;
        int* pclient_socket=NULL;

        socklen_t client_addr_len;
        client_addr_len=sizeof(client);
        client_sock=accept(sock,(struct sockaddr *)&client,&client_addr_len);

        printf("Client ip:%s\t port:%d\n",inet_ntop(AF_INET,&client.sin_addr.s_addr,client_ip,sizeof(client_ip)),ntohs(client.sin_port));

        //do_http_request(client_sock);

        pclient_socket=(int *)malloc(sizeof(int));

        *pclient_socket=client_sock;

        pthread_create(&id,NULL,do_http_request,(void*)pclient_socket);

    }
    close(sock);
    return 0;
}
void* do_http_request(void* pclient_socket){

    int client_sock=*((int*)pclient_socket);
    int len=0;
    char buf[256];
    char method[64];
    char url[256];
    char path[512];

    struct stat st;
    


    
    len=get_line(client_sock,buf,sizeof(buf));

    if(len>0){
        int i=0,j=0;
        while(!isspace(buf[j]) && (i<sizeof(method)-1)){
            method[i]=buf[j];
            i++;
            j++;
        }

        method[i]='\0';
        printf("request method:%s\n",method);


        //处理get请求
        if(strncasecmp(method,"GET",i)==0){
            if(debug){
                printf("method=GET\n");
            }

            //跳过空格
            while(isspace(buf[j++]));

            i=0;
            //读取url
            while(!isspace(buf[j]) && (i<sizeof(url)-1)){
                url[i]=buf[j];
                i++;
                j++;
            }
            url[i]='\0';

            if(debug) {
                printf("url:%s\n",url);
            }


            //继续读取header
            do{
                len=get_line(client_sock,buf,sizeof(buf));
                if(debug)  printf("read line :%s\n",buf);
            }while(len>0);

            char *pos=strchr(url,'?');
            if(pos){
                *pos='\0';
                printf("real url:%s\n",url);
            }

            sprintf(path,"./html_docs/%s",url);
            if(debug) printf("path:%s\n",path);

            //执行http响应
            do_http_response_v2(client_sock,path);          

        }else{
            //识别非get请求
            fprintf(stderr,"Warning!other request [%s]\n",method);


            //继续读取header
            do{
                len=get_line(client_sock,buf,sizeof(buf));
                if(debug)  printf("read line :%s\n",buf);
            }while(len>0);
        
            unimplemented(client_sock);
        } 

        

    }else{
        bad_request(client_sock);
    }
    close(client_sock);
    if(pclient_socket) free(pclient_socket);//释放内存
    return NULL;
    
}



int header(int client_sock,FILE *resource){
     struct stat st;
    int fileid=0;
    char tmp[64];
    char buf[1024]={0};
    strcpy(buf,"HTTP/1.0 200 OK \r\n");
    strcat(buf,"Server:Linux Server\r\n");
    strcat(buf,"Content-type: text/html\r\n");
    strcat(buf,"Connection:Close\r\n");

    fileid=fileno(resource);

    if(fstat(fileid,&st)==-1){
        inner_error(client_sock);
        return -1;
    }
    snprintf(tmp,64,"Content-Length:%d\r\n\r\n",st.st_size);
    strcat(buf,tmp);

    if(debug){
        fprintf(stdout,"header:%s\n",buf);
    }

    if(send(client_sock,buf,strlen(buf),0)<0){
         fprintf(stderr,"send faild.data:%s,reason:%s\n",buf,strerror(errno));
         return -1;
    }
    return 0;
}

//读取html文件的内容并发送到客户端
void cat(int client_sock,FILE *resource){
   
    char buf[1024];
    fgets(buf,sizeof(buf),resource);
    while(!feof(resource)){
        int len=write(client_sock,buf,strlen(buf));
        if(len<0){
            fprintf(stderr,"send body error.reason:%s\n",strerror(errno));
            break;
        }

        if(debug){
            fprintf(stdout,"%s",buf);
        }
        fgets(buf,sizeof(buf),resource);
    }
}



//第二个版本的响应处理
void do_http_response_v2(int client_sock,const char *path){
    FILE *resource=NULL;
    resource=fopen(path,"r");

    if(resource==NULL){
        not_found(client_sock);
        return;
    }

    int result = header(client_sock,resource);
    
    if(!result){
        cat(client_sock,resource);
    }
    


    fclose(resource);
}

void do_http_response(int client_sock){
    const char* main_header=" HTTP/1.0 200 OK \r\n Server:Linux Server\r\n Content-type: text/html\r\n Connection:Close\r\n";
    const char* content="<!DOCTYPE html>\n\
<html lang=\"en\">\n\
<head>\n\
    <meta charset=\"UTF-8\">\n\
    <title>Index</title>\n\
</head>\n\
<body>\n\
<h1>大家好,我是陈东谱,有什么问题欢迎大家加我微信,我的微信号是:<font color='red'>reg183</font></h1>\n\
</body>\n\
</html>";

    int len=write(client_sock,main_header,strlen(main_header));

    if(debug) fprintf(stdout,"...do_http_response...");
    if(debug) fprintf(stdout,"write[%d]:%s",len,main_header);

    char send_buf[64];
    int wc_len=strlen(content);
    len=snprintf(send_buf,64,"Content-Length:%d\r\n\r\n",wc_len);

    len=write(client_sock,send_buf,len);

    if(debug){
        fprintf(stdout,"write[%d]:%s",len,send_buf);
    }

    len=write(client_sock,content,wc_len);

    if(debug){
        fprintf(stdout,"write[%d]:%s",len,content);

    }



}

int get_line(int sock,char *buf,int size){

    int count=0;
    char ch='\0';
    int len=0;
    while((count<size-1) && ch!='\n'){

        len=read(sock,&ch,1);
        if(len==1){
            if(ch=='\r'){
                continue;
            }else if(ch == '\n'){
                buf[count]='\0';
                break;
            }//读取到一般字符
            buf[count]=ch;
            count++;
        }else if(len==-1){
            //读取出错
            perror("read faild");
            count=-1;
            break;
        }else{
            //客户端关闭sock连接
            fprintf(stderr,"client close.\n");
            count=-1;
            break;
        }
    }
    if(count>=0) buf[count]='\0';
    return count;
}

void not_found(int client_sock){
    const char* content="<!DOCTYPE html>\n\
<html lang=\"en\">\n\
<head>\r\n\
    <meta charset=\"UTF-8\">\n\
    <title>Index</title>\n\
</head>\r\n\
<body>\r\n\
<h1>文件没有找到</h1>\n\
</body>\n\
</html>";


    int len=write(client_sock,content,strlen(content));

    if(debug){
        fprintf(stdout,content);
    }

    if(len<=0){
        fprintf(stderr,"send content failed.reason:%s\n",strerror(errno));
    }
}

void inner_error(int client_sock){
    const char* content="<!DOCTYPE html>\n\
<html lang=\"en\">\n\
<head>\r\n\
    <meta charset=\"UTF-8\">\n\
    <title>Inner Error</title>\n\
</head>\n\
<body>\n\
<h1>服务器内部出错.</h1>\n\
</body>\n\
</html>";


    int len=write(client_sock,content,strlen(content));

    if(debug){
        fprintf(stdout,content);
    }

    if(len<=0){
        fprintf(stderr,"send content failed.reason:%s\n",strerror(errno));
    }
}



void unimplemented(int client_sock){
    const char* content="<!DOCTYPE html>\n\
<html lang=\"en\">\n\
<head>\r\n\
    <meta charset=\"UTF-8\">\n\
    <title>Inner Error</title>\n\
</head>\n\
<body>\n\
<h1>请求了一个未实现的方法.</h1>\n\
</body>\n\
</html>";


    int len=write(client_sock,content,strlen(content));

    if(debug){
        fprintf(stdout,content);
    }

    if(len<=0){
        fprintf(stderr,"send content failed.reason:%s\n",strerror(errno));
    }
}

void bad_request(int client_sock){
    const char* content="<!DOCTYPE html>\n\
<html lang=\"en\">\n\
<head>\r\n\
    <meta charset=\"UTF-8\">\n\
    <title>Inner Error</title>\n\
</head>\n\
<body>\n\
<h1>请求了一个错误的链接.</h1>\n\
</body>\n\
</html>";


    int len=write(client_sock,content,strlen(content));

    if(debug){
        fprintf(stdout,content);
    }

    if(len<=0){
        fprintf(stderr,"send content failed.reason:%s\n",strerror(errno));
    }
}

添加目录html_docs
并在其中添加一个html文件index.html,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1>大家好,我是陈东谱,有什么问题欢迎大家加我微信,我的微信号是:<font color="red">reg183</font></h1>
</body>
</html>


打包生成可执行文件

[root@localhost c++]# gcc winhttp.c -pthread -o winhttp.exe
winhttp.c: 在函数‘main’中:
winhttp.c:83:31: 警告:隐式声明与内建函数‘malloc’不兼容 [默认启用]
         pclient_socket=(int *)malloc(sizeof(int));
                               ^
winhttp.c: 在函数‘do_http_request’中:
winhttp.c:182:24: 警告:隐式声明与内建函数‘free’不兼容 [默认启用]
     if(pclient_socket) free(pclient_socket);//释放内存
                        ^

警告不用管

启动服务器

[root@localhost c++]# ./winhttp.exe 
等待客户端的连接



再开一个终端 ,打开一个telnet

[root@localhost ~]# telnet 127.0.0.1 80
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

同时请求网页,不糊出现卡顿,页面加载中的情况
在这里插入图片描述

控制台输出

[root@localhost c++]# ./winhttp.exe 
等待客户端的连接
Client ip:127.0.0.1	 port:38390
Client ip:192.168.137.1	 port:3566
Client ip:192.168.137.1	 port:3567
request method:GET
method=GET
url:index.html
read line :Host: 192.168.137.151
read line :Connection: keep-alive
read line :Upgrade-Insecure-Requests: 1
read line :User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
read line :Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
read line :Accept-Encoding: gzip, deflate
read line :Accept-Language: zh-CN,zh;q=0.9
read line :Cookie: PHPSESSID=495343c481b1e5f7c0c03dad27e0817d; thinkphp_show_page_trace=0|0
read line :
path:./html_docs/index.html
header:HTTP/1.0 200 OK 
Server:Linux Server
Content-type: text/html
Connection:Close
Content-Length:267


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1>大家好,我是陈东谱,有什么问题欢迎大家加我微信,我的微信号是:<font color="red">reg183</font></h1>
</body>
</html>
client close.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

reg183

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值