Open chunpu opened 9 years ago
我发现c语言学习环境极为恶劣, 有问题上网找, 首先c关键词太弱..而且c语言的博客根本没法看, 一段代码基本是不能跑的, 如果有缩进我都已经感激涕零了. 不得不感叹还是js好啊, 毕竟会js的文章不会太丑.
所以我写c有关的学习博客, 都只求代码美观, 能直接运行.
socket方法是用来获取一个文件描述符的。
int fd = socket(PF_INET, SOCK_STREAM, 0);
SOCK_STREAM表示流式数据,也就是tcp协议,udp的参数是SOCK_DGRAM.全程叫datagram,数据豌豆,也就是我们常说的数据报啦。
SOCK_STREAM
SOCK_DGRAM
这下我终于明白为啥nodejs中使用udp协议是require("dgram");了
require("dgram");
AF_INET
PF_INET
在网上找例子的时候, 会看到有的写AF_INET, 有的写PF_INET, AF是address family的缩写, PF是protocol family的缩写
这两个完全相同
AF_INET = 2 PF_INET = 2 AF_INET6 = 10 PF_INET6 = 10
他们的区别在于, AF是BSD的, 而PF是POSIX的.在windows上完全相同, 在我看来直接用2没什么问题.
int bind(int fd, struct sockaddr *addr, socklen_t addrlen);
但我们一般不适用sockaddr, 因为sockaddr只有两个属性, 不能语义化的体现一个套接字地址.(ip, 协议, 端口)
因此我们使用sockaddr_in这个结构体代替, 这两个结构体长度相同, 可以直接强制转换(其实ipv6的sockaddr_in6长度不同依然是强制转换).
sockaddr_in
真正使用的时候一般是这样
int bind(fd, (struct sockaddr *)&addr, sizeof(addr));
一般使用sockaddr_in这个更加语义化的结构体来表示ipv4的套接字地址, ipv6为sockaddr_in6,需要引用netinet/in.h.
sockaddr_in6
netinet/in.h
struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; }
这么多sin是啥意思, 我猜可能是socket inet的缩写.
sin_family_t就是前面的unsigned short类型的PF_INET, 也就是2.
sin_family_t
sin_port当然就是端口, 但不能直接写, 需要用例如honts(80);转化.
sin_port
honts(80);
sin_addr.s_addr同样需要转换,addr.sin_addr.s_addr = inet_addr("0.0.0.0");
sin_addr.s_addr
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
sin_zero仅仅是用来对齐sockaddr大小的。
sin_zero
accept函数是接到请求的时触发的,一般网络出错调试,都可以在accept上设置断点,然后逐步调试,但要注意的是accept并不是进行3步握手, 3步握手是更加底层的系统完成的,accept只是把完成握手的请求从系统中拿出来。
accept是放在while 1中的,这样它永远尝试着去获取新的socket文件描述符。
int len = sizeof(addr); while (1) { req_fd = accept(fd, (struct sockaddr *)&addr, &len); ... }
第二个参数类型是sockaddr, 存在sockaddr_in中,这样方便我们读取其ip和端口信息
第三个参数居然是地址,以我的水平是无法理解的。
accept的任务是获取一个新的请求fd。于是我们就可以通过这个新的fd进行接收数据和发送数据了。
char buf[1000]; recv(req_fd, buff, sizeof(buf), 0);
这函数api很清楚,从req_fd中读数据,放入buf中,buf长度为参数3.
那如果请求头太长怎么办呢?比如一个GET的路径就1k大了。这时候如果长度超出会怎样呢?我们可以做一个小实验。
int req_fd; int size, size2; char[300] buf; char[300] buf2; while (1) { if ((req_fd = accept(fd, addr, &len) == -1) { exit(1); } size = recv(req_fd, buf, 300, 0); size2 = recv(req_fd, buf, 300, 0); }
这时候我们发现size等于300, size2等于133.(这是使用中文chrome请求时的头部),可以看出,size2还没装满300,说明缓冲区已经被我们拿完了,那我们继续recv会怎样呢?我尝试了一下,程序直接没反应了,也没有报错。
char res_str[] = "HTTP/1.1 200 OK\nContent-Type: text/html ..."; send(req_fd, res_str, sizeof(res_str), 0);
可以看到send和recv确实是一对api,参数基本一样,第四个参数都可以不管直接写0
我发现c语言学习环境极为恶劣, 有问题上网找, 首先c关键词太弱..而且c语言的博客根本没法看, 一段代码基本是不能跑的, 如果有缩进我都已经感激涕零了. 不得不感叹还是js好啊, 毕竟会js的文章不会太丑.
所以我写c有关的学习博客, 都只求代码美观, 能直接运行.
socket
socket方法是用来获取一个文件描述符的。
SOCK_STREAM
表示流式数据,也就是tcp协议,udp的参数是SOCK_DGRAM
.全程叫datagram,数据豌豆,也就是我们常说的数据报啦。这下我终于明白为啥nodejs中使用udp协议是
require("dgram");
了AF_INET
和PF_INET
在网上找例子的时候, 会看到有的写AF_INET, 有的写PF_INET, AF是address family的缩写, PF是protocol family的缩写
这两个完全相同
他们的区别在于, AF是BSD的, 而PF是POSIX的.在windows上完全相同, 在我看来直接用2没什么问题.
bind
但我们一般不适用sockaddr, 因为sockaddr只有两个属性, 不能语义化的体现一个套接字地址.(ip, 协议, 端口)
因此我们使用
sockaddr_in
这个结构体代替, 这两个结构体长度相同, 可以直接强制转换(其实ipv6的sockaddr_in6长度不同依然是强制转换).真正使用的时候一般是这样
sockaddr_in
一般使用
sockaddr_in
这个更加语义化的结构体来表示ipv4的套接字地址, ipv6为sockaddr_in6
,需要引用netinet/in.h
.这么多sin是啥意思, 我猜可能是socket inet的缩写.
sin_family_t
就是前面的unsigned short类型的PF_INET, 也就是2.sin_port
当然就是端口, 但不能直接写, 需要用例如honts(80);
转化.sin_addr.s_addr
同样需要转换,addr.sin_addr.s_addr = inet_addr("0.0.0.0");
sin_zero
仅仅是用来对齐sockaddr大小的。accept
accept函数是接到请求的时触发的,一般网络出错调试,都可以在accept上设置断点,然后逐步调试,但要注意的是accept并不是进行3步握手, 3步握手是更加底层的系统完成的,accept只是把完成握手的请求从系统中拿出来。
accept是放在while 1中的,这样它永远尝试着去获取新的socket文件描述符。
第二个参数类型是sockaddr, 存在
sockaddr_in
中,这样方便我们读取其ip和端口信息第三个参数居然是地址,以我的水平是无法理解的。
accept的任务是获取一个新的请求fd。于是我们就可以通过这个新的fd进行接收数据和发送数据了。
接收数据 recv
这函数api很清楚,从req_fd中读数据,放入buf中,buf长度为参数3.
那如果请求头太长怎么办呢?比如一个GET的路径就1k大了。这时候如果长度超出会怎样呢?我们可以做一个小实验。
这时候我们发现size等于300, size2等于133.(这是使用中文chrome请求时的头部),可以看出,size2还没装满300,说明缓冲区已经被我们拿完了,那我们继续recv会怎样呢?我尝试了一下,程序直接没反应了,也没有报错。
发送数据 send
可以看到send和recv确实是一对api,参数基本一样,第四个参数都可以不管直接写0