❤️ 专栏简介 :网络通信和Socket编程是Linux C/C++服务器开发的基础。本专栏从最基础的内容开始学习网络通信和socket编程的相关内容,循序渐进的掌握网络通信的和socket编程的相关知识。主要内容包括网络通信与socket编程概述、socket通信模型、套接字概述、socket通信交互流程以及Socket通信中各个函数的实现以及功能等。
☀️ 专栏适用人群 :适用于具备基础 Linux 知识的并想从零开始学习网络通信和Socket编程初学者;以及想学习Linux上c/c++服务器开发的朋友们。
🌙专栏特点:通俗易懂、图文并茂、非常详细;
🌴 专栏说明 :如果文章知识点有错误的地方,欢迎大家随时在文章下面评论,我会第一时间改正。让我们一起学习,一起进步。
🍄 专栏地址:https://blog.csdn.net/anchenliang_1002/category_11919076.html
本节我们来详细的对socket的函数进行学习,主要包括函数关系图
、socket函数
、bind函数
、listen函数
、accept函数
、connect函数等
。
socket中各个函数的关系图如下所示:
经过三次握手才能确立连接,才能正常的收发数据;即在客户端,connect()后,经过三次握手,成功,才会读写数据;同样的,在服务端,listen()后,经过三次握手后,调用accept()接受连接,之后读写数据。
socket()
打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对于IPv4,domain参数指定为AF_INET
。对于TCP协议,type参数指定为SOCK_STREAM
,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。protocol参数一般指定为0即可。
#include
#include
int socket(int domain, int type, int protocol);
domain:
AF_INET
这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
AF_INET6
与上面类似,不过是来用IPv6的地址
AF_UNIX
本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
type:
SOCK_STREAM
这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP
来进行传输。
SOCK_DGRAM
这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP
来进行它的连接。
SOCK_SEQPACKET
该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
SOCK_RAW
socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
SOCK_RDM
这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序
protocol:
传0 表示使用默认协议。
成功:返回指向新创建的socket的文件描述符;
失败:返回-1,设置errno
bind()的作用是将参数sockfd和addr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听addr所描述的地址和端口号
。
详细解释:
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址和端口号。
bind()的作用是将参数sockfd和addr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听addr所描述的地址和端口号。前面讲过,struct sockaddr *是一个通用指针类型,addr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度。
#include
#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:
通过socket函数,返回的文件描述符
addr:
构造出IP地址加端口号
addrlen:
sizeof(addr)长度
成功:返回0;
失败:返回-1,设置errno
bind函数的作用是将套接字绑定一个IP地址和端口号。
详细解释:
典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,如果接收到更多的连接请求就忽略。
#include
#include
int listen(int sockfd, int backlog);
sockfd
:
socket文件描述符
backlog
:
在Linux 系统中,它是指排队等待建立3次握手队列长度
查看系统默认backlog
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
改变 系统限制的backlog 大小
vim /etc/sysctl.conf
最后添加
net.core.somaxconn = 1024
net.ipv4.tcp_max_syn_backlog = 1024
保存,然后执行
sysctl -p
成功:返回0;
失败:返回-1,设置errno
accept()函数的功能是接收、接受一个客户端的连接。
详细解释:
三次握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。addr是一个传出参数,accept()返回时传出客户端的地址和端口号。addrlen参数是一个传入传出参数(value-result argument),传入的是调用者提供的缓冲区addr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)。如果给addr参数传NULL,表示不关心客户端的地址。
通俗一点就是说,调用accept函数需要传入三个参数,第一个参数是要接收的socket;第二个参数是我们创建一个存放客户端ip和端口的结构体,并传进去,accept执行后传出该参数,里面就写好了接收到的客户端请求的ip和端口;第三个参数传入的是最大缓冲区的大小,执行完accept后返回的是实际使用的大小。
#include
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockdf
:
socket文件描述符
addr
:
传出参数,返回链接客户端地址信息,含IP地址和端口号
addrlen
:
传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小
成功:返回一个新的socket文件描述符,用于和客户端通信;
失败:返回-1,设置errno
connect()函数的功能连接服务端。
详细解释:
客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。
#include
#include
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockdf
:
socket文件描述符
addr
:
传入参数,指定服务器端地址信息,含IP地址和端口号
addrlen
:
传入参数,传入sizeof(addr)大小
成功:返回0;
失败:返回-1,设置errno