互联网中使用的协议按TCP/IP模型划分有很多种,网络层使用的IP、ARP等协议,传输层使用的TCP/UDP协议,而应用层则有大量如HTTP、FTP等常见协议和一些私有的协议。
IP数据报中,存在一个协议字段,在这个字段中可以根据协议号识别出在网络层或传输层使用的协议,比较常见的有6号是TCP、17号是UDP协议。
在https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml中可以看到具体协议号对应的协议。
如下图所示,在TCP或UDP的数据报中,一般使用端口号来区分具体的应用层协议,这些端口号由IANA规定,比较常见的有ssh协议使用端口号22,http协议使用端口号80。 其中0~1023是系统端口,1024~49151是用户端口,49152~65535是动态端口。
makefile的好处就是制定好规则就可以快速的编译一个项目,当我仅仅修改了一份源代码的时候,Makefile可以通过时间戳判断哪些文件需要重新编译
例如有以下几个文件:
main.c
a.h
a.c
b.h
b.c
目标:依赖
命令
命令之前需要有一个【tab】制表符. 依赖太长的时候需要用反斜杠【\】换行
依赖文件必须写全,当依赖被修改的时候,目标才会重新构建
main: main.o a.o b.o
gcc -o main main.o a.o b.o
main.o:main.c a.h b.h
gcc -c main.c
a.o: a.c a.h
gcc -c a.c
b.o: b.c b.h
gcc -c b.c
定义一个变量,使用$(变量名)调用
例如
object = main.o a.o b.o
main:$(object)
gcc -o main $(object)
可以使用clean清除编译中生成的object文件,例如
clean:
rm -fv $(object)
使用make clean
执行
.PHONY两个用途:
all规则的用途:希望生成多个目标的时候用这个
举例:
object = main.o a.o b.o
all:main clean
main:main.c
gcc main.c -o main
.PHONY:clean
clean:
rm -fv $(object)
在目标文件是.o文件的时候,依赖文件可不用写.c 并且会自动推导出相关的编译命令,例如:
main.o:main.c a.h b.h
gcc -c main.c
a.o: a.c a.h
gcc -c a.c
b.o: b.c b.h
gcc -c b.c
可以简写成
main.o: a.h b.h
a.o: a.h
b.o: b.h
最终效果:
object = main.o a.o b.o
main: $(object)
gcc -o $@ $^
main.o: a.h b.h
a.o: a.h
b.o: b.h
clean:
rm -fv $(object)
对~/.vimrc
进行修改即可
set tabstop=4
set nocursorline
"""""""""""""""""""
" 显示相关
"""""""""""""""""""
"set shortmess=atI " 启动的时候不显示那个援助乌干达儿童的提示
"winpos 5 5 " 设定窗口位置
"set lines=40 columns=155 " 设定窗口大小
set nu " 显示行号
set go= " 不要图形按钮
"color asmanian2 " 设置背景主题
set guifont=Courier_New:h10:cANSI " 设置字体
syntax on " 语法高亮
"autocmd InsertLeave * se nocul " 用浅色高亮当前行
"autocmd InsertEnter * se cul " 用浅色高亮当前行
"set ruler " 显示标尺
set showcmd " 输入的命令显示出来,看的清楚些
"set cmdheight=1 " 命令行(在状态行下)的高度,设置为1
"set whichwrap+=<,>,h,l " 允许backspace和光标键跨越行边界(不建议)
"set scrolloff=3 " 光标移动到buffer的顶部和底部时保持3行距离
set novisualbell " 不要闪烁(不明白)
set statusline=%F%m%r%h%w\ [FORMAT=%{&ff}]\ [TYPE=%Y]\ [POS=%l,%v][%p%%]\ %{strftime(\"%d/%m/%y\ -\ %H:%M\")} "状态行显示的内容
set laststatus=1 " 启动显示状态行(1),总是显示状态行(2)
set foldenable " 允许折叠
set foldmethod=manual " 手动折叠
"set background=dark "背景使用黑色
set nocompatible "去掉讨厌的有关vi一致性模式,避免以前版本的一些bug和局限
" 显示中文帮助
if version >= 603
set helplang=cn
set encoding=utf-8
endif
" 设置配色方案
"colorscheme murphy
"字体
"if (has("gui_running"))
" set guifont=Bitstream\ Vera\ Sans\ Mono\ 10
"endif
set fencs=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936
set termencoding=utf-8
set encoding=utf-8
set fileencodings=ucs-bom,utf-8,cp936
set fileencoding=utf-8
"""""""""""""""""""""""""
"""""新文件标题""""""""""
"新建.c,.h,.sh文件,自动插入文件头
autocmd BufNewFile *.cpp,*.[ch],*.sh,*.java exec ":call SetTitle()"
""定义函数SetTitle,自动插入文件头
func SetTitle()
"如果文件类型为.sh文件
if &filetype == 'sh'
call setline(1,"\#########################################################################")
call append(line("."), "\# File Name: ".expand("%"))
call append(line(".")+1, "\# Author: jiangph")
call append(line(".")+2, "\# Created Time: ".strftime("%c"))
call append(line(".")+3, "\#########################################################################")
call append(line(".")+4, "\#!/bin/bash")
call append(line(".")+5, "")
else
call setline(1, "/*************************************************************************")
call append(line("."), " > File Name: ".expand("%"))
call append(line(".")+1, " > Author: jiangph")
call append(line(".")+2, " > Created Time: ".strftime("%c"))
call append(line(".")+3, " ************************************************************************/")
call append(line(".")+4, "")
endif
if &filetype == 'cpp'
call append(line(".")+5, "#include <iostream>")
call append(line(".")+6, "using namespace std;")
call append(line(".")+7, "")
endif
if &filetype == 'c'
call append(line(".")+5, "#include <stdio.h>")
call append(line(".")+6, "")
endif
"新建文件后,自动定位到文件末尾
autocmd BufNewFile * normal G
endfunc
"""""""""""""""""""""""""""""""
"键盘命令
"""""""""""""""""""""""""""""""
nmap <leader>w :w!<cr>
nmap <leader>f :find<cr>
" 映射全选+复制 ctrl+a
map <C-A> ggVGY
map! <C-A> <Esc>ggVGY
map <F12> gg=G
" 选中状态下 Ctrl+c 复制
vmap <C-c> "+y
"去空行
"C,C++ 按F5编译运行
map <F5> :call CompileRunGcc()<CR>
func! CompileRunGcc()
exec "w"
if &filetype == 'c'
exec "!g++ % -o %<"
exec "! ./%<"
elseif &filetype == 'cpp'
exec "!g++ % -o %<"
exec "! ./%<"
elseif &filetype == 'java'
exec "!javac %"
exec "!java %<"
elseif &filetype == 'sh'
:!./%
endif
endfunc
"C,C++的调试
map <F8> :call Rungdb()<CR>
func! Rungdb()
exec "w"
exec "!g++ % -g -o %<"
exec "!gdb ./%<"
endfunc
""""""""""""""""""""""""
""实用设置
""""""""""""""""""""""""
" 设置当文件被改动时自动载入
set autoread
" quickfix模式
autocmd FileType c,cpp map <buffer> <leader><space> :w<cr>:make<cr>
"代码补全
set completeopt=preview,menu
"允许插件
filetype plugin on
"共享剪贴板
set clipboard+=unnamed
"从不备份
set nobackup
"make 运行
:set makeprg=g++\ -Wall\ \ %
"自动保存
set autowrite
set ruler " 打开状态栏标尺
"set cursorline " 突出显示当前行
set magic " 设置魔术
set autoindent "自动缩进
" 侦测文件类型
filetype on
" 载入文件类型插件
filetype plugin on
" 为特定文件类型载入相关缩进文件
filetype indent on
" 统一缩进为4
set softtabstop=4
set shiftwidth=4
" 可以在buffer的任何地方使用鼠标(类似office中在工作区双击鼠标定位)
set mouse=a
set selection=exclusive
set selectmode=mouse,key
" 通过使用: commands命令,告诉我们文件的哪一行被改变过
set report=0
" 在被分割的窗口间显示空白,便于阅读
set fillchars=vert:\ ,stl:\ ,stlnc:\
" 高亮显示匹配的括号
set showmatch
" 匹配括号高亮的时间(单位是十分之一秒)
set matchtime=1
" 光标移动到buffer的顶部和底部时保持3行距离
set scrolloff=3
" 为C程序提供自动缩进
set smartindent
" 高亮显示普通txt文件(需要txt.vim脚本)
au BufRead,BufNewFile * setfiletype txt
"自动补全
:inoremap ( ()<ESC>i
:inoremap ) <c-r>=ClosePair(')')<CR>
":inoremap { {<CR>}<ESC>O
":inoremap } <c-r>=ClosePair('}')<CR>
:inoremap [ []<ESC>i
:inoremap ] <c-r>=ClosePair(']')<CR>
:inoremap " ""<ESC>i
:inoremap ' ''<ESC>i
function! ClosePair(char)
if getline('.')[col('.') - 1] == a:char
return "\<Right>"
else
return a:char
endif
endfunction
filetype plugin indent on
"打开文件类型检测, 加了这句才可以用智能补全
set completeopt=longest,menu
To be finish…
设置下载的响应头
Content-Type:application/octet-stream\r\n
Content-Disposition:attachment;filename=[文件名]\r\n
代码:
/*
Description:
处理下载文件的请求/?download=[文件名]
Parameters:
int client_sock [IN] 客户端的socket
char *arg: [IN] 解析的参数
Return:
NULL
*/
void response_download(int client_sock,char *arg)
{
int fd;
ssize_t size = -1;
char buf[MAX_SIZE],header[MAX_SIZE];
char file_name[NAME_LEN];
if(sscanf(arg,"/?download=%s",file_name)==EOF)
{
//匹配失败!
printf("error:%s\n",arg);
construct_header(header,404,"text/html");
write(client_sock,header,strlen(header));
return;
}
fd = open(file_name,O_RDONLY);
if(fd == -1)
{
construct_header(header,404,"text/html");
write(client_sock,header,strlen(header));
return;
}
//构建下载的相应头部
printf("\tdownloading %s\n",file_name);
construct_download_header(header,200,file_name);
write(client_sock,header,strlen(header));
while(size)
{
//size代表读取的字节数
size = read(fd,buf,MAX_SIZE);
if(size > 0)
{
send(client_sock,buf,size,0);
}
}
printf("\n");
}
HTTP的Keep-Alive与TCP的Keep Alive,有些不同,两者意图不一样。前者主要是TCP连接复用,避免建立过多的TCP连接。而TCP的Keep Alive的意图是在于保持TCP连接的存活,就是发送心跳包。隔一段时间给连接对端发送一个探测包,如果收到对方回应的 ACK,则认为连接还是存活的,在超过一定重试次数之后还是没有收到对方的回应,则丢弃该 TCP 连接。
关于tcp的keep-alive可以参考这篇文章https://blog.csdn.net/weicao1990/article/details/81740014
可以看到使用了setsockopt这个函数来规定Socket的keep-alive属性。
写的非常好的一篇文章 HTTP Keep-Alive工作原理
分块传输编码(Chunked transfer encoding) 是超文本传输协议(HTTP)中的一种数据传输机制,允许HTTP由应用服务器发送给客户端应用( 通常是网页浏览器)的数据可以分成多个部分。分块传输编码在HTTP/1.1中提供。
通常,HTTP应答消息中发送的数据是整个发送的,Content-Length消息头字段表示数据的长度。数据的长度很重要,因为客户端需要知道哪里是应答消息的结束,以及后续应答消息的开始。然而,使用分块传输编码,数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小。通常数据块的大小是一致的,但也不总是这种情况。
HTTP 1.1引入分块传输编码提供了以下几点好处:
在进行Chunked编码传输时,在回复消息的Headers有transfer-coding域值为chunked,表示将用chunked编码传输内容。即
Transfer-Encoding: chunked\r\n
下图为用wireshark抓取的一个使用了chunk的包,过滤规则为http contains "Transfer-Encoding: chunked"
,是从网易云音乐这个应用里捕获的包。
可以看到,每一个chunk都是以\r\n结尾的。 CRLF即回车换行(代表\r\n),对应的ASCII码值分别为13、10,换算成16进制是0d0a。
代码:
/*
Description:
处理下载文件的请求/?download=[文件名]
以chunk分块传输
Parameters:
int client_sock [IN] 客户端的socket
char *arg: [IN] 解析的参数
Return:
NULL
*/
void response_download_chunk(int client_sock,char *arg)
{
int fd;
ssize_t size = -1;
char buf[MAX_SIZE],header[MAX_SIZE],*chunk_head;
char file_name[NAME_LEN];
if(sscanf(arg,"/?download=%s",file_name)==EOF)
{
//匹配失败!
printf("error:%s\n",arg);
construct_header(header,404,"text/html");
write(client_sock,header,strlen(header));
return;
}
fd = open(file_name,O_RDONLY);
if(fd == -1)
{
construct_header(header,404,"text/html");
write(client_sock,header,strlen(header));
return;
}
//构建下载的相应头部
printf("\tdownloading %s\n",file_name);
construct_download_header(header,200,file_name);
write(client_sock,header,strlen(header));
while(size)
{
//size代表读取的字节数
size = read(fd,buf,MAX_SIZE);
chunk_head = (char *)malloc(MIN_SIZE*sizeof(char));
sprintf(chunk_head,"%x\r\n",size);//需要转换为16进制
send(client_sock,chunk_head,strlen(chunk_head),0);
if(size > 0)
{
send(client_sock,buf,size,0);
}
send(client_sock,CRLF,strlen(CRLF),0);
free(chunk_head);
}
send(client_sock,CRLF,strlen(CRLF),0);
printf("\n");
}
项目地址:https://github.com/jiangph1001/Servette
本节主要有关socket编程所需要的函数
htonl = host to network long
头文件: #include <netinet/in.h>
函数名 | 功能
– | –
htonl|转换成ip地址
htons|转换成端口号
ntohl|ip地址转换为主机字节序
ntohs|端口号转换成主机字节序
头文件: #include <bits/socket.h>
类型: sa_family_t
协议族|地址族|描述|协议族的含义|长度 –|–|–|–|– PF_UNIX|AF_UNIX|UNIX本地域协议族|路径名|108字节 PF_INET|AF_INET| TCP/IPV4协议族|16bit端口号、32bit地址|6字节 PF_INET6|AF_INET6| TCP/IPV6协议族|128 bit的IPv6地址|26字节 其中PF_和AF_可以混用
头文件: #include <sys/un.h>
struct sockaddr_in
{
sa_family_t sin_family; //AF_INET
u_int16_t sin_port; //网络字节序的端口号
struct in_addr sin_addr; //IPV4地址
}
头文件: #include <arpa/inet.h>
in_addr_t inet_addr( const char* strptr)
该函数有不可重入性!
char* inet_ntoa(struct in_addr in)
目前公式会不显示,复制到其他markdown编辑器查看
普通的线性回归模型:$f(x)=w^Tx+b$
logist回归其实是一个二分类问题,就是根据线性回归模型预测的值判断属于哪一类。
因为希望输出的值 $y\in (0,1)$ ,那么就要对f(x)进行变换,这里使用的是Sigmod函数
$y = \frac {1}{1+e^{-z}} $
将线性回归模型套入得
$y = \frac{1}{1+e^{-(w^Tx+b)}}$ (1)
进行变换得到
$\ln \frac{y}{1-y} = w^Tx+b $ (2)
$\because P(y=1 | x) = 1-P(y=0 | x)$ , 将(2)中的y视为后验概率$P(y=1 | x)$ |