Http服务器实现文件上传与下载(三)
一、引言
在前2章的内容基本上已经讲解了整个的大致流程。在设计Http服务器时,我设计为四层的结构,最底层是网络传输层,就是socket编程。接着一层是请求和响应层,叫做Request和Response。在上一层是URL解析流程走向层。最顶层我设计为索引层。这一层主要多文件时对文件进行内存上的索引,加快文件的查找。或者可能是其他内容。在这一次中也包括了一些浏览器显示的页面内容。这些都是可以读者自行添加。
在写这一章节时,我不知道该是从上往下讲解,还是从下往上讲解能让读者更加清楚我的设计。在思考中.....,最终选择从底层的socket层开始讲解。如果大家对此还有什么疑问可以查看前两章的内容。
二、socket编程
在开始封装的时候,大家可以看看《Unix网路编程》这本书,主要的内容还是在上面,主要的代码和这本书上的大致一致。在这里我在梳理一下。我不会讲解所有的API,客户端上的API我就不讲解了,我只是说一下服务器用到的API。
在开始讲解时,我需要说2种套接字。只要是为后面的内容做一下解释。当我们开始socket编程时,需要创建一个套接字和远程端进行连接的。在这句话中就包含了2种套接字。一种是监听套接字,一种是连接套接字。比如说监听套接字就是我们宿舍楼下的大爷,而连接套接字就是我们。当有一个连接叫做快递员到达楼下时,被宿舍楼下大爷发现了,其实就是监听套接字发现有连接进来了。然后大爷告诉我们,你的快递来了,然后我们下楼向快递员签字拿快递,这就是建立了连接。
从上面得知我们需要一个监听套接字,也就是宿舍大爷。创建如下:
int socket(int family,int type,int protocal);
发现没有,返回值就是套接字,其实就是一个整数,或者称为文件描述符。因为在linux下所有的设备都是文件,所以可以用一个唯一的整数代表某个含义。该返回-1时代表创建套接字失败。一般我们TCP编程参数填写会是listenfd= socket(AF_INET,SOCK_STREAM,0);创建的是流套接字,具体内容可以看上面提到的书籍。
接下来我们需要绑定套接字,为什么?因为我们向系统申请了一个套接字(宿舍大爷)后,可是他还没有工作地点呢,我们需要为他安排工作地点。工作地点也就是端口,要表示一个唯一的工作地点,计算机需要IP和端口同时制定才能确定唯一。
int bind(int sockfd,const struct sockaddr*myaddr,socklen_t addrlen);
若成功返回0,否则返回-1。第一个参数代表着刚才创建的套接字listenfd。struct sockaddr是一个通用套接字结构,其实我们传输的确实struct sockaddr_in{}这样的结构,需要进行强制转换,原因是这些API都比较老了,历史原因造成的,主要是之前没有void*这个类型。socklen_t就是一个无符号的整数类型。调用方式一般为
bind(listenfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
这个serveraddr的填充如下:
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
serveraddr.sin_port=htons(PORT);
而serveraddr的类型确实struct sockaddr_in;
现在也为宿舍大爷安排了工作地点了,接下来无非就是让大爷工作了,不然申请过来干什么...
int listen(int sockfd,int backlog);
若成功返回0失败为-1.第一个套接字还是刚才的监听套接字,backlog这个参数主要涉及到未完成连接队列和已完成连接队列的问题。现在理解为最大的连接数即可,具体看上面的书本。
最后一个就是当大爷发现了快递员,并确定他的身份后,叫我们下来,这是就是下面的api
int accept(int sockfd,struct sockaddr*chliaddr,socklent_t *addrlen);
成功的话返回连接套接字 ,就是我们自己,否则返回-1。在这三个参数中第一个还是这个监听套接字listenfd,第二个和第三个可以获取连接者的身份。来着的IP和端口。
一般如果不用设为NULL。例子如下:
clientlen = sizeof(struct sockaddr_in);
int fd = accept(listenfd,(struct sockaddr *)&clientaddr,&clientlen);
当不为clientlen填写一个默认值的话,程序会报错的,尽管这个参数叫做获取长度,但是我们要是需要先设置大小。
接下来的就是我们与快递员通讯了。我们接受快递员递给我的快递,这个行为就是read。就是把内容写入打自己的存储空间。
ssize_t read(int sockfd,void*buf,size_t nbyte);
读取成功返回实际读取的字节数,如果返回0表示已经输送完毕,小于0表示输入错误,buf就是自己存放接受到的内容,nbyte表示这个空间的大小。这里记住sockfd现在填写的是连接套接字,是我们自己,而不是老大爷了。
ssize_t write(int sockfd,const void* buf,size_t nbytes);
该函数把buf中nbytes字节内容写入sockfd套接字,发送给对方。成功返回实际写了多少字节,失败返回-1。
到这里已经讲完需要用到的API,但是最后两个read和write有点特殊,因为在一些linux系统中,当系统发生中断时,可能会停止read或者write。这是我们需要重新调用该函数。为了能正确的获取数据,需要填写一些代码,仅仅的调用这2个函数是不够的。
下面就是这个套接字的代码段。把套接字封装在一个命名空间为TCP的Socket类中。
头文件(include/socket.h)
1 /*
2 * tcp.h
3 *
4 */
5
6 #ifndef SOCKET_H_
7 #define SOCKET_H_
8 #include<iostream>
9 #include<sys/socket.h>
10 #include<sys/types.h>
11 #include<netinet/in.h>
12 #include<errno.h>
13 #include <string.h>
14 //#include <unistd>
15 namespace TCP{
16 class Socket {
17 public:
18 Socket();
19 ~Socket();
20 int server_socket();
21 int server_listen();
22 int server_accept();
23 int server_bind();
24 void server_init();
25 void getClient(sockaddr_in* caddr);
26 int server_read(int fd,char*recvBuf,ssize_t maxlen);
27 int server_write(int fd,char*sendBuf,ssize_t maxlen);
28 void server_close(int confd) ;
29 private:
30 int __readline(int fd,char*recvBuf,ssize_t maxlen) ;
31 int __writen(int fd,char*sendBuf,ssize_t maxlen) ;
32 int listenfd;
33 int confd;
34 struct sockaddr_in serveraddr;
35 socklen_t serverlen;
36 static const int PORT=80;
37 };
38 }
39 #endif /* SOCKET_H_ */
源文件(src/socket.h)
1 #include "socket.h"
2 namespace TCP{
3 Socket::Socket() {
4 }
5 Socket::~Socket() {
6 }
7 int Socket::server_socket() {
8 listenfd= socket(AF_INET,SOCK_STREAM,0);
9 if(listenfd !=-1){
10 std::cout<<"server_socket() ...succeed"<<std::endl;
11 }else{
12 std::cout<<"server_socket() ...failed"<<std::endl;
13 }
14 return listenfd;
15 }
16
17 int Socket::server_listen() {
18 int ret = listen(listenfd,100);
19 if(ret ==0){
20 std::cout<<"server_listen() ...succeed"<<std::endl;
21 }else{
22 std::cout<<"server_listen() ...failed"<<std::endl;
23 }
24 return ret;
25 }
26 void Socket::server_close(int confd) {
27 close(confd);
28 }
29 int Socket::server_accept() {
30 clientlen = sizeof(struct sockaddr_in);
31 int fd = accept(listenfd,(struct sockaddr *)&clientaddr,&clientlen);
32 if(fd !=-1){
33 std::cout<<"server_accept() ...succeed"<<std::endl;
34 }else{
35 std::cout<<"server_accept() ...failed"<<std::endl;
36 }
37 return fd;
38 }
39 int Socket::server_bind() {
40 int ret =bind(listenfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
41 if(ret ==0){
42 std::cout<<"server_bind() ...succeed"<<std::endl;
43 }else{
44 std::cout<<"server_bind() ...failed"<<std::endl;
45 }
46 return ret;
47 }
48 void Socket::server_init() {
49 bzero(&serveraddr,sizeof(serveraddr));
50 serveraddr.sin_family=AF_INET;
51 serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
52 serveraddr.sin_port=htons(PORT);
53
54 }
55 ssize_t Socket::server_read(int fd,char*recvBuf,ssize_t maxlen) {
56 long long havedreadCount=0;
57 int readCount=0;
58 while(1){
59 readCount = __readline(fd,recvBuf+havedreadCount,maxlen);
60 havedreadCount+=readCount;
61 //std::cout<<"readCount:"<<readCount<<std::endl;
62 if(readCount==0)//当一行是\r\n时,空行,表示这一次读完。
63 break;
64 }
65 return 0;
66
67 }
68 ssize_t Socket::server_write(int fd,char*sendBuf,ssize_t maxlen){
69
70 return __writen(fd,sendBuf,maxlen);
71 }
72 int Socket::__writen(int fd,char*sendBuf,ssize_t maxlen){
73 size_t nleft;
74 ssize_t nwritten;
75 const char *ptr;
76 ptr=sendBuf;
77 nleft=maxlen;
78 //int count=0;
79
80 while(nleft>0){
81 if((nwritten=write(fd,ptr,nleft))<=0){
82 if(nwritten<0&& errno==EINTR)
83 nwritten=0;
84 else{
85 return -1;
86 }
87 }
88 nleft-=nwritten;
89 ptr+=nwritten;
90 }
91 return maxlen;
92 }
93 int Socket::__readline(int fd,char*recvBuf,ssize_t maxlen) {
94 ssize_t n,rc;
95 char c,*ptr;
96 ptr=recvBuf;
97 for(n=1;n<maxlen;n++){
98 again:
99 if((rc=read(fd,&c,1))==1){
100 *ptr++=c;
101 //std::cout<<c;
102 if(c=='\n')
103 break;
104 }else if(rc ==0){
105 *ptr=0;
106 return n-1;
107 }else{
108 if(errno ==EINTR)
109 goto again;
110 return -1;
111 }
112 }
113 *ptr=0;
114 if(n==2&&*(ptr-2)=='\r'&&*(ptr-1)=='\n')
115 n=0;
116 return n;
117 }
118 }
到这里可以看到这一层的内容已经完成,如果感兴趣,请关注我,继续查看下面的讲解《Http服务器实现文件上传与下载(四)》
Http服务器实现文件上传与下载(三)的更多相关文章
- Http服务器实现文件上传与下载(二)
一.引言 欢迎大家接着看我的博客,如何大家有什么想法的话回复我哦,闲话不多聊了,接着上一讲的内容来说吧,在上一节中已经讲到了请求头字符串的解析,并且在解析中我我们已经获取了url.就是上节中提到的/d ...
- java使用Jsch实现远程操作linux服务器进行文件上传、下载,删除和显示目录信息
1.java使用Jsch实现远程操作linux服务器进行文件上传.下载,删除和显示目录信息. 参考链接:https://www.cnblogs.com/longyg/archive/2012/06/2 ...
- Http服务器实现文件上传与下载(五)
一.引言 欢迎大家和我一起编写Http服务器实现文件的上传和下载,现在我回顾一下在上一章节中提到的一些内容,之前我已经提到过文件的下载,在文件的下载中也提到了文件的续下载只需要在响应头中填写Conte ...
- Http服务器实现文件上传与下载(四)
一.引言 欢迎大家来到和我一起编写Http服务器实现文件的上传和下载,现在我稍微回顾一下之前我说的,第一.二章说明说明了整体的HTTP走向,第三章实现底层的网络编程.接着这一章我想给大家讲的是请求获取 ...
- Http服务器实现文件上传与下载(一)
一.引言 大家都知道web编程的协议就是http协议,称为超文本传输协议.在J2EE中我们可以很快的实现一个Web工程,但在C++中就不是非常的迅速,原因无非就是底层的socket网络编写需要自己完成 ...
- java代码实现ftp服务器的文件上传和下载
java代码实现文件上传到ftp服务器: 1:ftp服务器安装: 2:ftp服务器的配置: 启动成功: 2:客户端:代码实现文件的上传与下载: 1:依赖jar包: 2:sftpTools 工具类: ...
- 【问题解决方案】Xshell连接服务器并实现上传和下载文件
参考链接: Xshell连接服务器并实现上传和下载文件 第一步:xshell登录完成 略 第二步: 在服务器安装lrzsz 如果服务器的操作系统是 CentOS,则输入命令[yum install l ...
- java web学习总结(二十四) -------------------Servlet文件上传和下载的实现
在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用 ...
- (转载)JavaWeb学习总结(五十)——文件上传和下载
源地址:http://www.cnblogs.com/xdp-gacl/p/4200090.html 在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传 ...
随机推荐
- Nutch的发展历程(转)
2002年8月由Doug Cutting发起,托管于Sourceforge,之后发布了0.4.0.5.0.6三个版本 2004年9月Oregon State University(俄勒冈州立大学)采用 ...
- ConfigurationManager读取dll的配置文件
ConfigurationManager读取dll的配置文件 最近一个项目,需要发布dll给第三方使用,其中需要一些配置参数. 我们知道.NET的exe工程是自带的App.config文件的,编译之后 ...
- 【PHP】组合条件搜索SQL
前端html多个搜索条件组合 后台一个sql语句,很方便和简洁:仅提供思路. 也可以配合着进行分页操作,非常赞~
- selenium2.0 --常用函数2
新建实例driver = webdriver.Chrome() 1.获取当前页面的Url函数 方法:current_url 实例: driver.current_url 2.获取元素坐标 方法:loc ...
- PHPCMS V9使用中的一些心得体会
官方演示站:http://v9.demo.phpcms.cn/ 在线帮助文档:http://v9.help.phpcms.cn/ 案例展示:互联网 http://www.i-busi.com 1. ...
- python-创建一个登录判断的函数
方法一def account_login(): password = input('Password:') if password == '12345': print('Login success!' ...
- vsftp 虚拟用户高级设置(转载)
发布:xiaokk 来源:net [大 中 小] vsftp 虚拟用户高级设置 本文转自:http://www.jbxue.com/article/1724.html 1.安装所需软件包 ...
- 李洪强iOS经典面试题31-解释垃圾回收的原理
李洪强iOS经典面试题31-解释垃圾回收的原理 问题 我们知道,Android 手机通常使用 Java 来开发,而 Java 是使用垃圾回收这种内存管理方式. 那么,ARC 和垃圾回收对比,有什么优点 ...
- 小案例:struts1.3利用nested标签使用POJO
其中的关键就是这个POJO是你自己去new一个,struts是不会帮你创建的!参考http://luohua.iteye.com/blog/39976 表单页 <%@ page language ...
- url参数
两个参数情况: String url="http://59.78.93.208:9097/Order?id="+id+"&value="+value; ...