socket学习1
总结学习socket的知识点
什么是Socket
socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
首先我们知道在Linux中一切皆是文件个概念,所以网络连接也是文件,其和普通的文件一样也有文件描述符。
我们可以通过 socket() 函数来创建一个网络连接,或者说打开一个网络文件,socket() 的返回值就是文件描述符。有了文件描述符,我们就可以使用普通的文件操作函数来传输数据了,例如:
- 用 read() 读取从远程计算机传来的数据;
- 用 write() 向远程计算机写入数据。
TCP 和 UDP 的端口是互不干扰的,也就是说系统可以同时开启 TCP 80 端口和 UDP 80 端口。
socket 不属于任何一层网络协议,它是对 TCP 层的封装,方便网络编程。
现实生活中,两个人要邮寄信件,必须知道对方的地址。网通信也是如此,只不过这里通信的是程序。程序的地址由三元组(ip 地址,端口,协议)界定。
如果你了解网络协议模型的话,你就会知道,ip 地址是网络层用来路由和通信的标识符,端口(port) 是传输层管理的。而 socket 是在这两层之上,所以需要这两个地址来标识。这里的协议指的是 ipv4,ipv6 或者其他协议。
Socket的种类
Socket有许多种类包括:DARPA Internet 地址(Internet 套接字)、本地节点的路径名(Unix套接字)、CCITT X.25地址(X.25 套接字)
创建 socket 的时候需要指定 socket 的类型,一般有三种:
- SOCK_STREAM:面向连接的稳定通信,底层是 TCP 协议,我们会一直使用这个。
- SOCK_DGRAM:无连接的通信,底层是 UDP 协议,需要上层的协议来保证可靠性。
- SOCK_RAW:更加灵活的数据控制,能让你指定 IP 头部
Internet套接字
主要的传输方式
https://c.biancheng.net/view/2124.html
流格式套接字:SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。
数据报套接字:数据报格式套接字(Datagram Sockets)也叫“无连接的套接字”,在代码中使用 SOCK_DGRAM 表示。
计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。
流格式套接字的特点:
- 发送、接受不是同步的
- 具有自我修复功能
- 按照顺序传输
这个流格式套接字就像传送带一样。
Socket编程中的常用函数
Socket中常用函数之间的关系
Socket()
这个函数用来创建一个套接字,函数原型如下:
1 |
|
为什么需要第三个参数:一般情况下有了 af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到这样的情况:有两种不同的协议支持同一种地址类型和数据传输类型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。
https://c.biancheng.net/view/2344.html
bind()函数和connect()函数
bind()
这个函数用来将Socket和相应的ip地址、接口绑定起来。
函数原型:
1 |
|
sockaddr_in结构体成员:
1 |
|
sockaddr结构体与sockaddr_in结构体:
为什么要将sockaddr
结构体强制转换为sockadr_in
结构体:
sockaddr 和 sockaddr_in
的长度相同,都是16字节,只是将IP地址和端口号合并到一起,用一个成员 sa_data
表示。要想给 sa_data
赋值,必须同时指明IP地址和端口号,例如”127.0.0.1:80“,遗憾的是,没有相关函数将这个字符串转换成需要的形式,也就很难给 sockaddr 类型的变量赋值,所以使用 sockaddr_in
来代替。这两个结构体的长度相同,强制转换类型时不会丢失字节,也没有多余的字节。
sockaddr
是一种通用的结构体,可以用来保存多种类型的IP地址和端口号,而 sockaddr_in
是专门用来保存 IPv4 地址的结构体。另外还有 sockaddr_in6
,用来保存 IPv6 地址。
connect()函数
函数原型:
1 |
|
listen()函数
开始监听指定的套接字
函数原型:
1 |
|
使用示例:
1 |
|
accept()函数
网络编程的核心一步就是建立客户端和服务器端的连接,使用 accept 来建立 2 者的连接:
函数原型:
1 |
|
使用示例:
1 |
|
send()函数和sendto()函数
send()函数
使用 send 函数发送数据,不同于使用 write 函数。send 函数可以进行 flag 的指定,使之控制更加详细。
函数原型:
1 |
|
使用示例:
1 |
|
recv()函数和recvfrom()函数
既然有发送数据,必然有接收数据的函数,与 send 类似,recv 的功能也跟 read 几乎相同:
1 |
|
使用示例:
1 |
|
参考资料
- https://beej-zhcn.netdpi.net/system_call/sendto_yu_recvfrom__lai_dian_dgram
- https://www.csd.uoc.gr/~hy556/material/tutorials/cs556-3rd-tutorial.pdf
- https://hit-alibaba.github.io/interview/basic/network/IP.html
- https://dlonng.com/posts/network
- https://cizixs.com/2015/03/29/basic-socket-programming/#%E7%BB%91%E5%AE%9A%EF%BC%88bind%EF%BC%89%E5%9C%B0%E5%9D%80%E4%B8%89%E5%85%83%E7%BB%84%E5%88%B0-socket