文档章节

C-BlogServer博客-可并发的http服务器

t
 tmj
发布于 2015/08/21 22:02
字数 1485
阅读 221
收藏 7
点赞 0
评论 0

这一部分主要是先把现有代码讲述一下方便后面的博客描写

ps:我也是初学者,不保证代码的可靠性,只是能用,现有代码的问题也许会在后面修正,这取决于个人的学习深度,由于是学习向的,不采用任何的第三方库

运行在Linux环境下

首先来个单线程的

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>

int main()
{
    int server_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);//IPV4+TCP
    struct sockaddr_in server_addr;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);//监听端口
    bind(server_fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));
    listen(server_fd, 1);
    int len = sizeof(struct sockaddr);
    struct sockaddr_in client_addr;
    int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
    char recvBuf[1024];
    recv(client_fd, recvBuf, sizeof(recvBuf), 0);//接收浏览器发过来的请求
    char sendBuf[]="HTTP/1.1 200 OK\r\nServer:BlogServer\r\nContent-Length:18\r\n\r\n<p>hello world</p>";//包括http响应头和正文<p>hello world</p>
    send(client_fd, sendBuf, sizeof(sendBuf), 0);//不对请求分析统一返回上面的字符串
    shutdown(client_fd, SHUT_RDWR);//关闭
    close(client_fd);
    close(server_fd);
    return 0;
}

Unnamed QQ Screenshot20150821174715

然后通过gcc编译一下

Unnamed QQ Screenshot20150821174908

运行

Unnamed QQ Screenshot20150821175155

通过chrome打开可以看到hello world

响应信息:

Unnamed QQ Screenshot20150821175228

 

由于这个程序并不是多线程,也没有做循环处理,所以该程序只能运行一次就会退出

然而再次打开的时候却无法继续使用,为什么呢?

首先使用netstat -an看一下端口的占用情况,然而并没有发现8080端口

但是有个看起来比较可疑的http-alt

Unnamed QQ Screenshot20150821181149
在查询一下服务发现确实是8080的端口的
Unnamed QQ Screenshot20150821181759
LISTEN:侦听来自远方的TCP端口的连接请求
SYN-SENT:再发送连接请求后等待匹配的连接请求
SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认
ESTABLISHED:代表一个打开的连接
FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认
FIN-WAIT-2:从远程TCP等待连接中断请求
CLOSE-WAIT:等待从本地用户发来的连接中断请求
CLOSING:等待远程TCP对连接中断的确认
LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认
TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认
CLOSED:没有任何连接状态

查一下资料发现TIME-WAIT是等待远程tcp的确认

经查询发现可以采用端口复用的方式来避免服务器重启等情况

int val = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int));
setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(int));

在bind()之前加入这两句
Unnamed QQ Screenshot20150821194340

这时候可以重复地连续地进行启动操作

如果让两个终端同时开启程序,第一个开启的(pid比较小的?)会先执行accept()

然后我们再进行并发的编程

#include <pthread.h>

添加必要的头

int main(int argc,char* argv[])
{
    if(argc != 2) return 1;
    int server_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    printf("socket_fd:%d\n", server_fd);
    struct sockaddr_in server_addr;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[1]));
    int val = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int));
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(int));
    if(!bind(server_fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))) {char buf[30];printf("bind:%s:%d\n",inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),(unsigned int)ntohs(server_addr.sin_port));}
    else return 1;
    if(!listen(server_fd, 1000)) {char buf[30];printf("listen:%s:%d\n",inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),(unsigned int)ntohs(server_addr.sin_port));}
    else return 1;

//accept()将在线程中完成
    pthread_t ntid;
    pthread_create(&ntid, NULL, ListenClient, &server_fd);

//主线程开始等待输入
    int loop;
    scanf("%d",&loop);
    close(server_fd);
    return 0;
}

监听线程

void *ListenClient(void *server)
{
    int server_fd = *(int*)server;
    int len = sizeof(struct sockaddr);
    struct sockaddr_in client_addr;
    pthread_t ntid;
    while(1)
    {
        int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
        //printf("client_fd:%d\n", client_fd);
        char buf[30];
        printf("connect:%s:%d\n",inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf)),(unsigned int)ntohs(client_addr.sin_port));
        //printf("%lu:start new response\n",(unsigned long)pthread_self());
        //printf("%d\n",client_fd);
        pthread_create(&ntid, NULL, HttpResponse, &client_fd);//每一次响应都创建新的线程来处理
        pthread_detach(ntid);
    }   
}

响应线程

void *HttpResponse(void *client)
{
    int client_fd = *(int*)client;
    char recvBuf[1024];
    //printf("%lu:start recv\n",(unsigned long)pthread_self());
    recv(client_fd, recvBuf, sizeof(recvBuf), 0);
    //printf("%s\n",recvBuf);
    char sendBuf[]="HTTP/1.1 200 OK\r\nServer:BlogServer\r\nContent-Length:18\r\n\r\n<p>hello world</p>";
    //printf("%lu:start send\n",(unsigned long)pthread_self());
    send(client_fd, sendBuf, sizeof(sendBuf), 0);
    shutdown(client_fd, SHUT_RDWR);
    //printf("%lu:start close\n",(unsigned long)pthread_self());
    close(client_fd);
    pthread_exit((void *)0);
}

编译

gcc main.c -o httpserver -lpthread
这时候可以不停刷新浏览器运行了,还是很不错的

Unnamed QQ Screenshot20150821202203

但此时系统还是不完善的,接着看

Unnamed QQ Screenshot20150821202619

先用ps a看一下进程pid

再用top命令来观察(top –p 1431)

使用py脚本测试,这个比较简单

#coding=utf-8
import urllib
import thread
import time

i = 0
while 1:
    try:
                url = 'http://127.0.0.1:8080'
        res = urllib.urlopen(url)
                s = res.read()
        res.close()
        i = i + 1
        if i % 5 == 0:
                        print str(i)+':'+s
    except Exception as e:
        print str(e)

Unnamed QQ Screenshot20150821202920

在高速地循环运行下,系统依然是很稳健的,毕竟这个简单,但是这是在网络状况极好的状况下,那在网络异常等一下故障情况下,出现超时了是否能处理呢?

import socket
host='127.0.0.1'
port=8080
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,port))
s.send('hello')
print s.recv(1024)
s.close

简单的一次socket连接

Unnamed QQ Screenshot20150821203719

成功

import socket
host='127.0.0.1'
port=8080
while 1:
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((host,port))
    s.send('hello')
    s.recv(1024)
    s.close

循环,这时候跟http版本的相似,也是可以正常运行的

这时候要是把recv和close去掉,那么情况就不用乐观了,服务器几乎1秒就挂了

import socket
import time
host='127.0.0.1'
port=8080
while 1:
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((host,port))
    s.send('hello')
    time.sleep(0.01)
    #s.recv(1024)
    #s.close

加上time.sleep(0.001)会缩短崩溃时间,但也不容乐观

问题出在哪里呢?大量的超时连接导致服务器资源过多奔溃吗?加入对客户端超时发生与接收的处理

struct timeval timeout = {5, 0};
setsockopt(client_fd,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(struct timeval));
setsockopt(client_fd,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(struct timeval));

实际上就算说对超时时间,短时间大量这样的连接还是会奔溃,问题到底出在哪里呢?
也晚了,等下一篇博客继续研究吧

© 著作权归作者所有

共有 人打赏支持
t

tmj

粉丝 3
博文 14
码字总数 14830
作品 0
珠海
将oschina博客转移到docker的服务器中

继续前一章节 弄了一个新的branch,将开源中国上面的博客导出来的html挂到daocloud上 https://github.com/ahelloworld/MiniHttpServer/tree/blogserver dockerfile和makefile都不需要做任何的...

tmj
2015/11/01
0
0
memcached实现集群中的session共享存储优缺点

优点说明: 1. memcached是内存缓存,在读写速度上会比普通files时快很多 2. 可以解决多个服务器公用session的难题 缺点说明: 1. session数据都保存在memory中,持久化方面有所欠缺,但对s...

科技探索者
2017/11/20
0
0
单机部署多CEPH-RGW的方法

引言 在高配置服务器,单个RGW的并发量无法充分使用机器的网络带宽资源,本文用于说明如何在单机上部署多RGW的方法。 如何使nginx和radosgw联合使用 如何使用配置多nginx,多radosgw 1.nginx...

西昆仑
2016/02/04
875
0
VPS服务器性能压力测试工具(转载)

VPS服务器性能 压力测试工具 http_load、webbench、ab、Siege使用教程 一、http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载。但是...

Qadir.luo
2010/11/29
0
0
NodeJS系列~第三个小例子,NodeJs与Redis实现高并发的队列存储

众所周知 redis量个强大的缓存组件,可以部署在win32和linux环境之上,它有五大存储结构,其中有一种为列表list,它可以实现quene和stack的功能,即队列和堆栈的功能。 redis相关文章,可以看...

mcy247
2017/12/06
0
0
Centos6.4下Nginx安装

Centos6.4下Nginx安装 Nginx ("engine x") 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发...

Eynauy
2014/05/23
0
0
三大WEB服务器对比分析(apache ,lighttpd,nginx)

一.软件介绍(apache lighttpd nginx) 1. lighttpd Lighttpd是一个具有非常低的内存开销,cpu占用率低,效能好,以及丰富的模块等特点。lighttpd是众多OpenSource轻量级的web server中较为优...

SomaLihq
06/27
0
0
三大WEB服务器对比分析(apache ,lighttpd,nginx)

一.软件介绍(apache lighttpd nginx) 1. lighttpd Lighttpd是一个具有非常低的内存开销,cpu占用率低,效能好,以及丰富的模块等特点。lighttpd是众多OpenSource轻量级的web server中较为优...

欧阳小瓜
2013/01/28
0
4
如何使用 Apache ab 以及 OneAPM 进行压力测试?

下一个 release 准备小长假后就要 go-live ,所有的测试 case 都 cover 过了,但还未进行过压力测试,有点不放心,刚好过节期间家人都回家去了,假期终于可以抽点时间压测一把。 Apache ab ...

OneAPM蓝海讯通
2016/03/28
39
0
【译】Java 7 NIO.2 异步 IO vs ANSI C 性能测试

原文:http://www.hernandezgomez.com/2011/01/23/java-7-nio-2-asynchronous-io-vs-ansi-c-performance/ 我喜欢JAVA,我也喜欢ANSI C。 曾经,我认为ANSI C比JAVA 快的多,但是最近看起来这...

老盖
2011/10/27
1K
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

JavaEE——JavaScript

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。 JavaScript 内置对象 String对象方法: date对象...

凯哥学堂
9分钟前
0
0
Git 远程代码回滚master

方式一(推荐): git revert commit-id 方式二(不推荐):不推荐原因:推送到线上后,其它开发人员需要重新clone git reset --hard commit-id git push origin HEAD --force...

浮躁的码农
9分钟前
0
0
Elasticesearch学习(7)—— ES查询与Java API的对应关系

1、普通查询 类型 ES查询语句 Java查询实现 结果 查询格式 { "query": { "bool": { "must": [], "must_not": [], "should": [], "filter": [] } }, "from": 0, "size": 10, "sort": [] } Que......

叶枫啦啦
10分钟前
4
0
getElementsByClassName()与getElementById()区别

1.document.getElementsByClassName() 返回的是数组 使用:document.getElementsByClassName("className")[0].innerText='具体内容' 2.document.getElementById() 返回的是单个元素 使用:d......

botkenni
20分钟前
0
0
MyBatis入门

一、安装 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version></dependency> 二、从 XML 中构建 SqlSessionFactory String r......

一个yuanbeth
21分钟前
0
0
聊聊spring cloud的LoadBalancerAutoConfiguration

序 本文主要研究一下spring cloud的LoadBalancerAutoConfiguration RibbonAutoConfiguration spring-cloud-netflix-ribbon-2.0.0.RC2-sources.jar!/org/springframework/cloud/netflix/ribb......

go4it
24分钟前
0
0
【转】使用Lombok来优雅的编码

前言 Lombok 是一种 Java™ 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO)。它通过注解实现这一目的。 正文 添加依赖 在 pom.xml 文件中添加相关依赖:...

HAVENT
26分钟前
0
0
Dubbo 源码解读 —— 可支持序列化及自定义扩展

一、概述 从源码中,我们可以看出来。目前,Dubbo 内部提供了 5 种序列化的方式,分别为 fastjson、Hessian2、Kryo、fst 及 Java原生支持的方式 。 针对不同的序列化方式,对比内容如下: 名...

Ryan-瑞恩
33分钟前
0
0
MySQL内存设置—— MySQL server has gone away

set global max_allowed_packet=268435456

一梦心草
43分钟前
0
0
推导式

列表、集合和字典推导式 列表推导式是Python最受喜爱的特性之一。它允许用户方便的从一个集合过滤元素,形成列表,在传递参数的过程中还可以修改元素。形式如下: [expr for val in collect...

火力全開
48分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部