在类Unix平台实现TCP服务端

在类Unix平台实现TCP客户端

创建服务器socket

在TCP服务器代码中,我们创建一个socket,然后调用bind函数,绑定到这个socket:

	// 创建本地地址配置信息struct addrinfo hints;// 清空hints的东西,为设置新的信息做准备memset(&hints,0,sizeof(hints));// TCP,选用SOCK_STREAMhints.ai_socktype = SOCK_STREAM;// 因为此地址将用作监听socket的地址,因此设置被动状态hints.ai_flags = AI_PASSIVE;// 返回的本地地址信息struct addrinfo *local_address;// getaddrinfo函数初始化本地地址信息,端口设置为8899if(getaddrinfo(NULL,"8899",&hints,&local_address)){fprintf(stderr,"getaddrinfo() failed. (%d)\n",errno);return 1;}printf("Creating a socket...\n");// 使用配置好的本地地址信息创建socketint server_socket = socket(local_address->ai_family,local_address->ai_socktype,local_address->ai_protocol);if(server_socket == -1){fprintf(stderr,"socket() failed. (%d)\n",errno);return 1;}

我们没有指定hints.ai_family = AF_INET或 AF_INET6,因为getaddrinfo可以动态决定它的具体类型,也就是我们可以同时兼容IPv4 和IPv6.

绑定socket到一个本地地址

上一步只是用本地地址信息创建了socket,还要将这个socket与本地地址绑定起来,才能真正关联起来。由bind函数来完成

printf("Binding socket to local address...\n");if(bind(server_socket,local_address->ai_addr,local_address->ai_addrlen)){fprintf(stderr,"bind() failed. (%d)\n",errno);return 1;}// 绑定完成后,本地地址信息后面就不会再使用了,于是我们释放掉它,节省内存freeaddrinfo(local_address);

让socket进入监听状态

调用listen函数让socket可以监听外界对它的访问。我们这里设置了最多有10个等待处理的进来的访问,换句话说,

	printf("Listening...\n");if(listen(server_socket,10) == -1){fprintf(stderr,"listen() failed. (%d)\n",errno);return 1;}

使用select函数处理socket的同步

这一段是最精彩的。

	// 初始化fd_set文件描述符集合fd_set master_set;// 清空文件描述符集合里的东西FD_ZERO(&master_set);// 将刚刚创建的用于监听的socket加入到集合中,当这个socket需要处理进来的请求时,就可以select函数中返回FD_SET(server_socket,&master_set);int fdmax;fdmax = server_socket;// 无限循环,这是正常的,因它是主程序,不能够退出。while(1){// 定义一个读文件描述符集合fd_set read_fds;// 清0集合FD_ZERO(&read_fds);// 将master_set复制一份到read_fdsmemcpy(&read_fds,&master_set,sizeof(master_set));// select函数将read_fds传进去,第一个参数是最大socket编号加1,read_fds包含所有需要监听读变化的文件描述符,如果有变化就会通过read_fds返回(只包含有读变化的socket,进去是全部socket,出来时只有部分),所以前面需要将master_set复制一份到read_fds。select是一个阻塞的函数,除非有变化,否则就卡在这了,这样省了很多资源的。if(select(fdmax+1,&read_fds,0,0,0) == -1){fprintf(stderr,"select() failed. (%d)\n",errno);return 1;}// 来到这一步,说明有变化了,遍历一遍socketfor(int i = 0;i <= fdmax;i++){// 检查socket是否在返回的读文件描述符集合中if(FD_ISSET(i,&read_fds)){// 如果在读文件描述符集合中,那么看看是否是监听用的socket,即服务端socketif(i == server_socket){// 看来是有新的访问要建立连接,处理新连接// 记录对端(客户端)socket的信息struct sockaddr_storage peer_address;socklen_t peer_address_size = sizeof(peer_address);// accept函数创建对端(客户端)socketint peer_socket = accept(server_socket,(struct sockaddr*)&peer_address,&peer_address_size);if(peer_socket == -1){fprintf(stderr,"accept() failed. (%d)\n",errno);return 1;}// 将客户端socket放入文件描述符集合中,以便与其通信FD_SET(peer_socket,&master_set);if(peer_socket > fdmax){fdmax = peer_socket;}char address_buffer[100];// 打印客户端的信息getnameinfo((struct sockaddr*)&peer_address,peer_address_size,address_buffer,sizeof(address_buffer),0,0,NI_NUMERICHOST);printf("Accepted connection on descriptor %d (host=%s)\n",peer_socket,address_buffer);}else{//处理客户端来的信息char read[1024]; // 准备一个接收信息字符数组// 接收客户端的数据int bytes_read = recv(i,read,sizeof(read),0);if(bytes_read <= 0){// 如果接收到数据小于或等于0,那么意味着客户端要关闭//connection closed by clientprintf("Terminated connection on descriptor %d\n",i);// 将客户端socket从master_set中移除FD_CLR(i,&master_set);// 在服务端这边关闭客户端socketclose(i);continue;}printf("Received message (%d bytes): %s\n",bytes_read,read);/////echo message back to clientfor (int j = 0; j < bytes_read; j++){read[j] = toupper(read[j]);}printf("Sending message (%d bytes): %s\n",bytes_read,read);// 向客户端发送数据send(i,read,bytes_read,0);}}}}

聊天室

					/////echo message back to clientfor (int j = 0; j < bytes_read; j++){read[j] = toupper(read[j]);}printf("Sending message (%d bytes): %s\n",bytes_read,read);// 向客户端发送数据send(i,read,bytes_read,0);

将上述代码,用下面的代码代替,就可以将程序变成聊天室。聊天室就要将信息发给每一个客户端,服务端和自己是不需要收到发的信息的。


for(int j = 0; j <= fdmax; j++){// 检查socket中在不在master_setif(FD_ISSET(j,&master_set)){// 如果是服务端socket则进入下一个if(j == server_socket){continue;}// 如果是自己,即同一个socket则进入下一轮if(j == i){continue;}// 给其他客户端发信息send(j,read,bytes_read,0);}
}

完整代码

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>int main(int argc,char *argv[]){printf("Configuring local address...\n");struct addrinfo hints;memset(&hints,0,sizeof(hints));hints.ai_socktype = SOCK_STREAM;hints.ai_flags = AI_PASSIVE;struct addrinfo *local_address;if(getaddrinfo(NULL,"8080",&hints,&local_address)){fprintf(stderr,"getaddrinfo() failed. (%d)\n",errno);return 1;}printf("Creating a socket...\n");int server_socket = socket(local_address->ai_family,local_address->ai_socktype,local_address->ai_protocol);if(server_socket == -1){fprintf(stderr,"socket() failed. (%d)\n",errno);return 1;}printf("Binding socket to local address...\n");if(bind(server_socket,local_address->ai_addr,local_address->ai_addrlen)){fprintf(stderr,"bind() failed. (%d)\n",errno);return 1;}freeaddrinfo(local_address);printf("Listening...\n");if(listen(server_socket,10) == -1){fprintf(stderr,"listen() failed. (%d)\n",errno);return 1;}fd_set master_set;FD_ZERO(&master_set);FD_SET(server_socket,&master_set);int fdmax;fdmax = server_socket;while(1){fd_set read_fds;FD_ZERO(&read_fds);memcpy(&read_fds,&master_set,sizeof(master_set));if(select(fdmax+1,&read_fds,0,0,0) == -1){fprintf(stderr,"select() failed. (%d)\n",errno);return 1;}for(int i = 0;i <= fdmax;i++){if(FD_ISSET(i,&read_fds)){if(i == server_socket){//handle new connectionstruct sockaddr_storage peer_address;socklen_t peer_address_size = sizeof(peer_address);int peer_socket = accept(server_socket,(struct sockaddr*)&peer_address,&peer_address_size);if(peer_socket == -1){fprintf(stderr,"accept() failed. (%d)\n",errno);return 1;}FD_SET(peer_socket,&master_set);if(peer_socket > fdmax){fdmax = peer_socket;}char address_buffer[100];getnameinfo((struct sockaddr*)&peer_address,peer_address_size,address_buffer,sizeof(address_buffer),0,0,NI_NUMERICHOST);printf("Accepted connection on descriptor %d (host=%s)\n",peer_socket,address_buffer);}else{//handle data from clientchar read[1024];int bytes_read = recv(i,read,sizeof(read),0);if(bytes_read <= 0){//connection closed by clientprintf("Terminated connection on descriptor %d\n",i);FD_CLR(i,&master_set);close(i);continue;}printf("Received message (%d bytes): %s\n",bytes_read,read);//echo message back to client// for (int j = 0; j < bytes_read; j++)// {// 	read[j] = toupper(read[j]);// }// printf("Sending message (%d bytes): %s\n",bytes_read,read);// send(i,read,bytes_read,0);for(int j = 0; j <= fdmax; j++){if(FD_ISSET(j,&master_set)){if(j == server_socket){continue;}if(j == i){continue;}send(j,read,bytes_read,0);}}}}}}printf("Closing socket\n");close(server_socket);printf("Finished.\n");return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/796564.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

博客部署001-centos安装docker

1、安装docker 1.1 卸载旧版本的 Docker sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine1.2 设置 Docker 仓库 安装 Docker Engine 之前&#xff0c;首先需要设置…

redis 哨兵

文章目录 前言主从复制的问题怎么人工恢复故障主节点 Redis Setinel 架构使用 docker 来配置哨兵结构安装 docker编排 redis 主从节点编排 redis 哨兵节点 观察哨兵模式的作用主从切换的具体流程小结 前言 redis 主从复制模式下, 一旦主节点出现故障, 不能提供服务的时候, 就需…

C++ | Leetcode C++题解之第13题罗马数字转整数

题目&#xff1a; 题解&#xff1a; class Solution { private:unordered_map<char, int> symbolValues {{I, 1},{V, 5},{X, 10},{L, 50},{C, 100},{D, 500},{M, 1000},};public:int romanToInt(string s) {int ans 0;int n s.length();for (int i 0; i < n; i) …

jmeter下载与使用

下载 官网下载地址&#xff1a;Apache JMeter - Apache JMeter™ 由于jmeter是由java语言编写的&#xff0c;所以要先安装jdk1.8或者以上的版本 配置环境变量 配置classpath环境变量 %JMETER_HOME%\lib\ext\ApacheJMeter_core.jar;%JMETER_HOME%\lib\jorphan.jar;%JMETER_HO…

很详细的单应矩阵分解R、t过程

很详细的单应矩阵分解R、t过程 附赠自动驾驶学习资料和量产经验&#xff1a;链接 已有多种方法将单应矩阵H分解为R、t&#xff0c;在《Deeper understanding of the homography decomposition for vision-based control》一文中介绍了三种方法&#xff1a; O. Faugeras and F.…

react api:forwardRef

forwardRef 允许组件使用 ref 将 DOM 节点暴露给父组件。 ** import { forwardRef } from ‘react’; const MyInput forwardRef(function MyInput(props, ref) { // … }); 使用 forwardRef() 让组件接收 ref 并将其传递给子组件&#xff1a; forwardRef 返回一个可以在 …

Qt Creator 界面

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、认识 Qt Creator 界面 1、总览 2、左边栏 3、代码编辑区 4、UI设计界面 5、构建区 一、认识 …

摄影杂记一

摄影小白&#xff0c;最近买了一台微单&#xff0c;型号是佳能R10&#xff0c;加上18-150套机镜头和佳能RF 50 F1.8定焦镜头。开始学习摄影。 PS&#xff1a;摄影穷三代&#xff0c;单反毁一生。嘿嘿。 一、分镜头拍摄四步提升法 B站&#xff1a;六斤 拍视频三件事&#xff1…

成都龙渊网络 UE客户端开发(20min)

自我介绍 为什么想做游戏 C熟还是C#熟 C的虚函数是什么 虚函数解决了什么问题 为什么析构函数一定要是虚函数呢 stl了解吗 说一下map的底层实现 map底层的数据结构时间复杂度是多少 unordered_map的底层实现 unordered_map底层的数据结构时间复杂度是多少 哈希冲突有…

专注项目管理的Mac工具 - Project Office Pro 最新版

Project Office Pro for Mac是一款功能强大的项目管理软件&#xff0c;旨在帮助用户更好地管理和跟踪项目进展&#xff0c;提高工作效率和质量。以下是该软件的主要功能介绍&#xff1a; 项目创建与编辑&#xff1a;用户可以根据自己的需求自定义项目计划&#xff0c;包括设置…

每天五分钟计算机视觉:模型集成和数据扩充在基准测试中的应用

本文重点 在基准测试中,模型集成和数据扩充是两个关键的技巧,它们对于提升测试的性能和准确性具有显著的影响。以下是从模型集成和数据扩充两个技巧在基准测试中的应用进行的总结。 模型集成在基准测试中的应用 在确定神经网络架构之后,我们可以独立训练几个神经网络,并…

【51单片机入门记录】RTC(实时时钟)-DS1302概述

目录 一、基于三线通信的RTC-DS1302 &#xff08;1&#xff09;简介 &#xff08;2&#xff09;特性 &#xff08;3&#xff09;引脚介绍 &#xff08;4&#xff09;控制字的格式 &#xff08;5.0&#xff09;日历时钟寄存器介绍 &#xff08;5.1&#xff09;日历时钟寄存…

4.1.k8s的pod-创建,数据持久化,网络暴露,env环境变量

目录 一、Pod介绍 二、指令创建和管理Pod 三、资源清单创建pod 1.挂载hostPath存储卷 2.NFS存储卷 所有节点安装nfs k8s3编辑NFS配置文件 k8s1&#xff0c;k8s2节点开机挂载 编辑pod资源清单&#xff0c;挂载nfs 四、pod网络暴露 1.hostNetwork使用宿主机的网络 2.…

【单片机】74HC4052电路图,单片机端口复用电路

74HC4052电路图 如下图&#xff0c;还是很好理解&#xff0c;PA9、PA10是单片机引脚。 当A和B是00&#xff0c;那么就是X-COM和0X短路&#xff0c;Y-COM和0Y短路。 当A和B是01&#xff0c;那么就是X-COM和1X短路&#xff0c;Y-COM和1Y短路。 以此类推。 74HC 工艺可以直接3.…

【Android】图解View的工作流程原理

文章目录 入口DecorView如何加载到Window中MeasureSpec MeasureView的测量ViewGroup的测量 LayoutView的layout() Draw1、绘制背景3、绘制View内容4、绘制子View6、绘制装饰 入口 DecorView如何加载到Window中 MeasureSpec 该类是View的内部类&#xff0c;封装View的规格尺寸…

FlutterFlame游戏实践#08 | 打砖块 -关卡设计

theme: cyanosis 本文为稀土掘金技术社区首发签约文章&#xff0c;30天内禁止转载&#xff0c;30天后未获授权禁止转载&#xff0c;侵权必究&#xff01; Flutter\&Flame 游戏开发系列前言: 该系列是 [张风捷特烈] 的 Flame 游戏开发教程。Flutter 作为 全平台 的 原生级 渲…

CTF之矛盾

这一题就是php的弱比较“” 这里要求输入的不是数字&#xff0c;并且输入要为1才打印flag 那我们就输入一个1后面接随便什么字符&#xff0c;因为php的弱比较将字符与数字进行比较的时候&#xff0c;会把字符转换成数字再比较&#xff0c;当转换到字符时后面便都为空了 flag{…

单身狗---进阶版(撞色搭配)

#目录 一、问题 二、思路 三、代码 四、代码讲解 1.拆分 2.得到单身狗 3.返回 五、注意 一、问题 整数数组 sockets 记录了一个袜子礼盒的颜色分布情况&#xff0c;其中 sockets[i] 表示该袜子的颜色编号。礼盒中除了一款撞色搭配的袜子&#xff0c;每种颜色的袜子均有…

域名应该如何实名?域名应该如何备案?域名如何解析到服务器

大家好欢迎来到易极赞&#xff0c;今天我们来跟大家聊一下“域名应该如何实名以及备案”这个话题。 域名实名认证是验证域名所有者身份的过程&#xff0c;以确保域名的合法性&#xff0c;通常需要登录到域名服务商后台&#xff0c;进行域名的注册&#xff0c;注册后创建域名模…

WPF中嵌入3D模型通用结构

背景&#xff1a;wpf本身有提供3D的绘制&#xff0c;但是自己通过代码描绘出3D是比较困难的。3D库helix-toolkit支持调用第三方生成的模型&#xff0c;比如Blender这些&#xff0c;所以在wpf上使用3D就变得非常简单。这里是一个通过helix-toolkit库调用第三方生成的3d模型的样例…