ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Socket网络编程:TCP与UDP

Socket网络编程:TCP与UDP 文章目录一、Socket网络编程的原理1.TCPTCP Socket编程核心流程(经典C/S架构)2.UDP3.通用步骤4.函数详解(1)创建Socketsocket() —— 创建“电话机”(2)绑定地址bind() —— 给电话机“绑定号码”服务器必须做(3)监听连接请求listen() [仅适用于TCP] —— 将电话机设为“待机接听”状态(4)接受连接accept() —— 从队列里“拿起”一个来电(5)发起连接connect() [仅适用于TCP客户端] —— 客户端“拨号”连接(6)发送数据send() 或write()—— 通话发送/写数据(7)接收数据revc() 或 read()—— 通话接收/读数据(8)关闭Socket连接close() —— 挂断电话二、socket 套接字三、多线程四、IO模型1.阻塞式IO模型2.非阻塞式IO模型3.IO多路复用五、select、poll、epoll1.select2.poll3.epoll4.三者对比六、Redis一、Socket网络编程的原理1.TCPTCP Socket编程核心流程(经典C/S架构)TCP 编程遵循“服务器被动等待客户端主动连接”的模式。流程图如下服务器端socket()→bind()→listen()→accept()阻塞→recv()/send()→close()客户端socket()→connect()发起握手→send()/recv()→close()2.UDP3.通用步骤①创建Socketsocket()②绑定地址bind()③监听连接请求listen()④接受连接accept()⑤发起连接connect()⑥发送数据send()、接收数据revc()⑦关闭连接close()4.函数详解(1)创建Socketsocket() —— 创建“电话机”intsockfdsocket(AF_INET,SOCK_STREAM,0);AF_INET表示使用 IPv4 协议。SOCK_STREAM表示使用面向连接的字节流即 TCP。返回值是一个文件描述符Linux 下一切皆文件后续操作都基于这个 fd。#includesys/socket.hintsocket(intdomain,inttype,intprotocol);作用创建一个新的Socket。参数domain指定通信协议族如AF_INETIPv4或AF_INET6IPv6。type指定Socket的类型如SOCK_STREAMTCP或SOCK_DGRAMUDP。protocol指定协议通常为0表示使用默认协议。返回值成功返回Socket描述符失败返回-1。1.参数intsocket(intaddress_family,inttype,intprotocol);(1)address family①AF_INETIPv4 网络协议。用于TCP/IP和UDP/IP网络通信。②AF_INET6IPv6 网络协议。用于TCP/IP和UDP/IP网络通信但支持IPv6地址。③AF_UNIX或AF_LOCAL本地通道通信。用于在同一台机器上的进程间通信。2)type①SOCK_STREAMTCP协议提供面向连接的稳定数据传输保证数据能够按顺序、完整地到达。②SOCK_DGRAMUDP协议提供无连接的数据传输服务。发送的是独立的消息不保证顺序或数据完整性。③SOCK_RAW提供原始网络协议访问。在网络模型中这种类型的套接字允许直接访问IP层通常用于网络协议的开发和测试。(3)protocol默认协议填02.返回值①成功时socket() 返回一个非负整数即新创建的套接字文件描述符。②出错时返回 -1并设置全局变量 errno 以表示具体的错误类型。3.创建一个使用IPv4地址和TCP协议的套接字intsockfdsocket(AF_INET,SOCK_STREAM,0);if(sockfd-1){perror(socket creation failed);}这里AF_INET 指定使用IPv4地址SOCK_STREAM 指定使用面向连接的数据传输方式(TCP)0 表示自动选择使用TCP协议。(2)绑定地址bind() —— 给电话机“绑定号码”服务器必须做bind(sockfd,(structsockaddr*)serv_addr,sizeof(serv_addr));将服务器的 IP 地址和端口号绑定到 socket 上。客户端需要知道这个 IP 和端口才能找到你#includesys/socket.hintbind(intsockfd,conststructsockaddr*addr,socklen_taddrlen);作用将Socket绑定到一个地址。参数sockfdSocket描述符。addr指向要绑定的地址结构的指针。addrlen地址结构的长度。返回值成功返回0失败返回-1。(3)监听连接请求listen() [仅适用于TCP] —— 将电话机设为“待机接听”状态listen(sockfd,BACKLOG);将主动 socket 转变为被动监听 socketBACKLOG 是已完成三次握手的连接队列的最大长度注意不是最大并发数#includesys/socket.hintlisten(intsockfd,intbacklog);作用开始监听连接请求。参数sockfdSocket描述符。backlog等待连接队列的最大长度。返回值成功返回0失败返回-1。(4)接受连接accept() —— 从队列里“拿起”一个来电intclient_fdaccept(sockfd,(structsockaddr*)client_addr,addrlen);这是一个阻塞函数。如果没有客户端连接程序会停在这里。重要区分sockfd 是监听套接字只负责接听client_fd 是通信套接字负责收发数据。在高并发服务器中这个 client_fd 通常会被交给子线程或子进程去处理。#includesys/socket.hintaccept(intsockfd,structsockaddr*addr,socklen_t*addrlen);作用接受客户端的连接请求。参数sockfdSocket描述符。addr用于存储客户端地址信息的结构体指针。addrlen客户端地址结构体的长度。返回值成功返回一个新的Socket描述符失败返回-1。(5)发起连接connect() [仅适用于TCP客户端] —— 客户端“拨号”连接connect(sockfd,(structsockaddr*)serv_addr,sizeof(serv_addr));客户端调用此函数发起三次握手连接成功返回 0失败返回 -1#includesys/socket.hintconnect(intsockfd,conststructsockaddr*addr,socklen_taddrlen);作用TCP客户端向服务器端发起连接请求。参数sockfdSocket描述符。addr指向服务器端地址结构的指针。addrlen地址结构的长度。返回值成功返回0失败返回-1。#includesys/types.h#includesys/socket.hintconnect(intsockfd,conststructsockaddr*addr,socklen_t addrlen);connect()成功返回0失败返回-1以下是一个简单的 TCP 客户端示例展示了如何使用 connect() 连接到服务器#includestdio.h#includestring.h#includesys/socket.h#includenetinet/in.h#includearpa/inet.hintmain(){intsockfdsocket(AF_INET,SOCK_STREAM,0);if(sockfd0){perror(Socket creation failed);return-1;}structsockaddr_inserver_addr;memset(server_addr,0,sizeof(server_addr));server_addr.sin_familyAF_INET;server_addr.sin_porthtons(12345);// 服务器监听的端口inet_pton(AF_INET,192.168.1.100,server_addr.sin_addr);// 服务器的IP地址if(connect(sockfd,(structsockaddr*)server_addr,sizeof(server_addr))0){perror(Connection failed);return-1;}printf(Connected to the server\n);// 之后可以使用sockfd进行数据传输close(sockfd);// 关闭套接字return0;}在这个示例中客户端程序创建了一个套接字设置服务器的 IP 地址和端口然后尝试与服务器建立连接。如果 connect() 调用成功客户端就与服务器建立了连接并可以通过该套接字进行数据通信。(6)发送数据send() 或write()—— 通话发送/写数据send(client_fd,buffer,len,0);#includesys/types.h#includesys/socket.hssize_tsend(intsockfd,constvoid*buf,size_tlen,intflags);作用发送数据。参数sockfdSocket描述符。buf指向要发送数据的缓冲区。len要发送的数据长度。flags传输控制标志通常为0。返回值成功返回发送的字节数失败返回-1发送数据将数据放到发送缓冲区。由内核决定什么时候将数据发送出去。(7)接收数据revc() 或 read()—— 通话接收/读数据recv(client_fd,buffer,sizeof(buffer),0);收发数据。recv 默认是阻塞的会一直等待数据到来。#includesys/types.h#includesys/socket.hssize_trecv(intsockfd,void*buf,size_tlen,intflags);作用接收数据。参数sockfdSocket描述符。buf用于存储接收数据的缓冲区。len缓冲区的大小。flags接收控制标志通常为0。返回值成功返回接收的字节数失败返回-1。接收数据当数据送到Linux内核后数据不是立即给到应用程序而是放在接收缓冲区等应用程序什么时候调用recv()函数什么时候才由内核给到应用程序。(8)关闭Socket连接close() —— 挂断电话close(client_fd);close(sockfd);关闭套接字释放资源。关闭通信套接字会触发 TCP 四次挥手。#includeunistd.hintclose(intsockfd);作用关闭Socket连接。参数sockfdSocket描述符。返回值成功返回0失败返回-1。二、socket 套接字socket套接字(IP:port)①IP用来定位是哪台电脑端口号用来定位是哪个进程②socket_fd 是 int类型的数字故套接字socket可理解为一套用于连接的数字三、多线程多线程的弊端需要CPU上下文切换四、IO模型1.阻塞式IO模型就等着你反馈。程序查询方式(独占查询)2.非阻塞式IO模型轮询检查程序查询方式(定时查询)3.IO多路复用1.定义IO多路复用是一种同步IO模型。①IO操作系统中数据在用户态和内核态之间的读写操作②多路多个TCP连接 (多个socket)③复用一个或多个线程资源④IO多路复用一个或多个线程处理多个TCP连接2.作用利用IO多路复用可以实现一个线程监控多个文件句柄。3.IO多路复用的使用场景要设计一个高性能的网络服务器。这个高性能服务器可以供多个客户端同时连接并且能处理这些客户端传上来的请求。4.实现IO多路复用的三种模型①select②poll③epoll对于待检测集合select和poll是基于线性方式处理的epoll是基于红黑树来管理待检测集合的。select和poll每次都会线性扫描整个待检测集合集合越大速度越慢epoll使用的是回调机制效率高处理效率也不会随着检测集合的变大而下降腾讯面试请描述select、poll、epoll这三种多路复用技术的原理IO多路复用之epollC语言实现QQ聊天室小项目 [完整源码]Linux网络编程基础及多线程并发案例好文推荐Linux IO模式及 select、poll、epoll详解五、select、poll、epoll1.select1.原理select采用轮询 遍历方式遍历bitmap将需要监听的文件描述符置1不需要监听的文件描述符置0三种fd写描述符、读描述符、异常描述符select会监视这三种文件描述符当数据可读/可写/异常/超时 时会返回。返回后通过遍历fdset(文件描述符的集合)来找到就绪的fd去触发相应的IO操作。2.优点3.缺点①文件描述表为bitmap结构长度最大为1024②fdset不可重用每次循环遍历都得重新创建③频繁的用户态和内核态拷贝性能开销较大④需要对文件描述符表进行遍历轮询时间复杂度为O(n)4.代码5.模型演示2.poll1.原理poll也是采用轮询 遍历方式但poll采用的是链表方式来存储fd2.优缺点3.代码//pollfd结构体原型structpollfd{intfd;//文件描述符shortevents;//注册的事件,用户关注的事件,如读写事件shortrevents;//实际发生的事件,由内核填充。系统内核填充并返回的事件};4.模型演示3.epollepoll模型是为了高效地处理大量并发连接1.原理epoll采用事件通知机制将轮询改成了回调。epoll的底层数据结构是红黑树 双向链表。①epoll_create()在系统启动时在Linux内核中申请一个红黑树结构的文件系统再返回epoll对象(fd对象)②epoll_ctl()创建客户端和服务器端的连接并把连接注册到eventpoll中。在每新建一个连接的时候同步更新epoll对象中的fd并绑定一个callback()回调函数。通过 epoll_ctl() 将 sockfd 添加到 epfd 的监控列表中并指定我们感兴趣的事件是 EPOLLIN即套接字可读通常表示有新的连接请求。这样当 sockfd 上发生了 EPOLLIN 事件时epoll_wait() 将能够检测到这一事件。③epoll_wait()检查双向链表list中是否有已就绪的事件若有则立刻返回不阻塞为空则阻塞等待。轮询所有的callback集合并触发对应的IO操作。分布式系统中常用的组件Redis、Nginx都采用epoll模型2.优缺点3.代码4.模型演示4.三者对比selectpollepoll六、RedisRedis为什么这么快①内存②单线程③IO多路复用④内部数据结构
返回列表