文档章节

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

t
 tmj
发布于 2015/08/21 22:02
字数 1485
阅读 222
收藏 7

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

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
VPS服务器性能压力测试工具(转载)

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

Qadir.luo
2010/11/29
0
0
单机部署多CEPH-RGW的方法

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

西昆仑
2016/02/04
875
0
memcached实现集群中的session共享存储优缺点

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

科技探索者
2017/11/20
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

没有更多内容

加载失败,请刷新页面

加载更多

升压变换器 Boost

工作特点 输入输出极性相同。 开关管 MOS 和负载构成并联,在MOS 导通时,电流通过 L 滤波,电源对 L 充电。 当 MOS 断开时,L 向负载及电源放电,输出电压将是 Ui+U L ,达到升压的目的。 ...

colinux
29分钟前
1
0
OSChina 周一乱弹 —— 你狗命在我手上

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 小小编辑:推荐歌曲,《I.W.A.B.N》- Lil Ghost 《I.W.A.B.N》- Lil Ghost 手机党少年们想听歌,请使劲儿戳(这里) 几天没见, 大王(@罗马的...

小小编辑
31分钟前
169
7
轻量级 memcached缓存代理 twemproxy实践

本文内容脑图如下: 文章共 533字,阅读大约需要 2分钟 ! 概 述 twemproxy(nutcracker) 是 Twitter开源的轻量级 memcached / redis 代理服务器,本质就是一个集群管理工具,主要用来弥补 ...

CodeSheep
51分钟前
7
0
Apache日志不记录访问静态文件,访问日志切割,静态元素过期时间设置

Apache配置不记录访问静态文件的日志 网站大多元素为静态文件,如图片、css、js等,这些元素可以不用记录 vhost原始配置 <VirtualHost *:80> ServerAdmin test@163.com DocumentRoo...

野雪球
今天
3
0
聊聊storm的ICommitterTridentSpout

序 本文主要研究一下storm的ICommitterTridentSpout ICommitterTridentSpout storm-core-1.2.2-sources.jar!/org/apache/storm/trident/spout/ICommitterTridentSpout.java public interface......

go4it
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部