实现迭代回声服务器端/客户端

news2024/9/22 17:28:14

文章目录

  • 1.迭代服务器端/客户端
  • 2.迭代回声服务器端/客户端
    • 2.1 echo_server.c
    • 2.2 echo_client.c
  • 3.回声客户端存在的问题
  • 4.回声客户端问题解决方法

1.迭代服务器端/客户端

之前讨论的 HelloWorld 服务器端处理完 1 1 1 个客户端连接请求即退出,连接请求等待队列实际没有太大意义。但这并非我们想象的服务器端。设置好等待队列的大小后,应向所有客户端提供服务。如果想继续受理后续的客户端连接请求,应怎样扩展代码?最简单的办法就是插入循环语句反复调用 accept 函数,如下图所示。

在这里插入图片描述

从上图可以看出,调用 accept 函数后,紧接着调用 I/O 相关的 read、write 函数,然后调用 close 函数。这并非针对服务器端套接字,而是针对 accept 函数调用时创建的套接字。

调用 close 函数就意味着结束了针对某一客户端的服务。此时如果还想服务于其他客户端,就要重新调用 accept 函数。同一时刻确实只能服务于一个客户端。将来学完进程和线程后,就可以编写同时服务多个客户端的服务器端了。目前只能做到这一步,虽然很遗憾,但请各位不要心急。

即使服务器端以迭代方式运转,客户端代码亦无太大区别。

2.迭代回声服务器端/客户端

回声(echo)服务器端/客户端,顾名思义,服务器端将客户端传输的字符串数据原封不动地传回客户端,就像回声一样。

  • 服务器端在同一时刻只与一个客户端相连,并提供回声服务。
  • 服务器端依次向 5 5 5 个客户端提供服务并退出。
  • 客户端接收用户输入的字符串并发送到服务器端。
  • 服务器端将接收的字符串数据传回客户端,即“回声”。
  • 服务器端与客户端之间的字符串回声一直执行到客户端输入 Q 为止。

2.1 echo_server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int serv_sock, clnt_sock;
	char message[BUF_SIZE];
	int str_len, i;
	
	struct sockaddr_in serv_adr;
	struct sockaddr_in clnt_adr;
	socklen_t clnt_adr_sz;
	
	if (argc != 2)
	{
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}
	
	serv_sock = socket(PF_INET, SOCK_STREAM, 0);

	if (serv_sock == -1)
	{
		error_handling("socket() error");
	}
	
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));

	if (bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr)) == -1)
	{
		error_handling("bind() error");
	}
	
	if (listen(serv_sock, 5) == -1)
	{
		error_handling("listen() error");
	}
	
	clnt_adr_sz = sizeof(clnt_adr);

	// 为处理5个客户端连接而添加的循环语句。共调用5次accept函数,依次向5个客户端提供服务。
	for (i = 0; i < 5; i++)
	{
		clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_adr, &clnt_adr_sz);

		if (clnt_sock == -1)
			error_handling("accept() error");
		else
			printf("Connected client %d\n", i + 1);
		
		// 实际完成回声服务的代码,原封不动地传输读取的字符串。
		while ((str_len = read(clnt_sock, message, BUF_SIZE)) != 0)
		{
			write(clnt_sock, message, str_len);
		}

		// 针对套接字调用close函数,向连接的相应套接字发送EOF。
		// 换言之,客户端套接字若调用close函数,则第62行的循环条件变成假(false),因此执行第69行的代码。
		close(clnt_sock);
	}

	// 向5个客户端提供服务后关闭服务器端套接字并终止程序。
	close(serv_sock);

	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

编译运行:

gcc echo_server.c -o eserver
./eserver 9190

2.2 echo_client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int sock;
	char message[BUF_SIZE];
	int str_len;
	struct sockaddr_in serv_adr;

	if (argc != 3)
	{
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}
	
	sock = socket(PF_INET, SOCK_STREAM, 0);

	if (sock == -1)
	{
		error_handling("socket() error");
	}
	
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
	serv_adr.sin_port = htons(atoi(argv[2]));
	
	// 调用connect函数。若调用该函数引起的连接请求被注册到服务器端等待队列,则connect函数将完成正常调用。
	// 因此,即使通过第41行代码输出了连接提示字符串,如果服务器尚未调用accept函数,也不会真正建立服务关系。
	if (connect(sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr)) == -1)
		error_handling("connect() error!");
	else
		puts("Connected...........");
	
	while (1)
	{
		fputs("Input message(Q to quit): ", stdout);
		fgets(message, BUF_SIZE, stdin);
		
		if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) break;

		write(sock, message, strlen(message));
		str_len = read(sock, message, BUF_SIZE - 1);
		message[str_len] = 0;
		printf("Message from server: %s", message);
	}
	
	// 调用close函数向相应套接字发送EOF(EOF即意味着中断连接)
	close(sock);

	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

编译运行:

gcc echo_client.c -o eclient
./eclient 127.0.0.1 9190

3.回声客户端存在的问题

while (1)
{
	fputs("Input message(Q to quit): ", stdout);
	fgets(message, BUF_SIZE, stdin);
	
	if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) break;

	write(sock, message, strlen(message));
	str_len = read(sock, message, BUF_SIZE - 1);
	message[str_len] = 0;
	printf("Message from server: %s", message);
}

以上代码有个错误假设:“每次调用 read、write 函数时都会以字符串为单位执行实际的 I/O 操作”,当然,每次调用 write 函数都会传递 1 1 1 个字符串,因此这种假设在某种程度上也算合理。

但“TCP不存在数据边界”,上述客户端是基于TCP的,因此,多次调用 write 函数传递的字符串有可能一次性传递到服务器端。此时客户端有可能从服务器端收到多个字符串,这不是我们希望看到的结果。

还需考虑服务器端的如下情况:“字符串太长,需要分 2 2 2 个数据包发送”。服务器端希望通过调用 1 1 1 次 write 函数传输数据,但如果数据太大,操作系统就有可能把数据分成多个数据包发送到客户端。另外,在此过程中,客户端有可能在尚未收到全部数据包时就调用 read 函数。

所有这些问题都源自 TCP 的数据传输特性。那该如何解决呢?

4.回声客户端问题解决方法

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

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

相关文章

唯品会盈利十年:韧性有余,冲劲不足

配图来自Canva可画唯品会在电商市场是一个特殊的存在&#xff0c;从2012年第四季度首次盈利至今&#xff0c;唯品会已经连续十年保持盈利。这十年&#xff0c;电商产业逐渐走向成熟&#xff0c;电商玩家新老接替成层出不穷&#xff0c;曾经家喻户晓的蘑菇街、聚美优品、苏宁易购…

Vue2.0开发之——购物车案例-Goods组件封装-商品数量的加减及总数量(53)

一 概述 Goods点击加减实现修改数量的原理Goods点击增加实现实例Goods点击-减少实现实例Footer计算商品总数量 二 Goods点击加减实现修改数量的原理 点击Counter组件里面的加减&#xff0c;修改Counter组件里面的数量Counter组件的数量变化时&#xff0c;Goods商品的数量相应…

leetcode-每日一题-807(中等,数组)

正常情况第一眼看这道题&#xff0c;看懂意思的话很简单就可以解出来。给你一座由 n x n 个街区组成的城市&#xff0c;每个街区都包含一座立方体建筑。给你一个下标从 0 开始的 n x n 整数矩阵 grid &#xff0c;其中 grid[r][c] 表示坐落于 r 行 c 列的建筑物的 高度 。城市的…

C++各类设计模式及实现详解

软件领域中的设计模式为开发人员提供了一种使用专家设计经验的有效途径。设计模式中运用了面向对象编程语言的重要特性&#xff1a;封装、继承、多态&#xff0c;真正领悟设计模式的精髓是可能一个漫长的过程&#xff0c;需要大量实践经验的积累。最近看设计模式的书&#xff0…

Jetpack Compose 中的重组作用域和性能优化

只有读取可变状态的作用域才会被重组 这句话的意思是只有读取 mutableStateOf() 函数生成的状态值的那些 Composable 函数才会被重新执行。注意&#xff0c;这与 mutableStateOf() 函数在什么位置被定义没有关系。读取操作指的是对状态值的 get 操作。也就是取值的操作。 从一…

Rasa 3.x 学习系列-Rasa [3.4.4] - 2023-02-17新版本发布

Rasa 3.x 学习系列-Rasa [3.4.4] - 2023-02-17新版本发布 任何人都可以学习Rasa之优秀Rasa学习资源推荐 欢迎同学们报名Gavin老师的Rasa系列课程,任何人都可以学习Rasa之优秀Rasa学习资源推荐: 1.NLP on Transformers高手之路137课 2 .Rasa 3.X 智能对话机器人案例开发硬核…

Linux如何性能优化,怎么理解平均负载?如何利用系统缓存优化程序的运行效率?内存泄漏如何定位和处理?如何“快准狠”找到系统内存存在的问题?

Linux如何性能优化&#xff0c;怎么理解平均负载&#xff1f;如何利用系统缓存优化程序的运行效率&#xff1f;内存泄漏如何定位和处理&#xff1f;如何“快准狠”找到系统内存存在的问题&#xff1f;1. 性能优化1.1 性能指标1.2 到底应该怎么理解"平均负载"1.3 平均…

插件系列 vue2安装tailwindcss

先说结论&#xff0c;tailwindcss在vue2中引入&#xff0c;可以与其它css预处理混用&#xff0c;并不冲突, vue3可以直接参考官网的安装方式即可。 官方网址&#xff1a;https://www.tailwindcss.cn/ 安装步骤&#xff1a; 直接安装 创建文件 tailwindcss.css main.js全局引…

分析了近500位产品经理后,得出这些产品经理的日常

前些天,我从实现网上抓取了497位产品经理的个人信息,这些产品人平均工作时间超过5年,来自200多家公司,其中152人拥有BAT 、京东、美团、网易、小米等一线互联网公司工作经历,当然也有很大一部分来自不知名的创业公司。 以下是正文: 对这里面各产品经理擅长技

HBase单机版安装详细步骤

目录 一、安装HBase前置环境 1.jdk 2.hadoop 3.zookeeper 二、安装HBase步骤 (一)解压 (二)修改文件名 (三)修改配置文件 (四)配置HBase的环境变量 (五)启动HBase 1.启动hadoop 2.启动zookeeper 3.启动hbase 4.jps出现HMaster和HRegionServer 5.HBase的WebUI (…

Docker那点事

Docker引擎是用于运行和编排容器的基础设施工具。有VMware管理经验的可以将其类比为ESXi。ESXi是运行虚拟机的核心管理程序,而Docker引擎是运行容器的核心容器运行时。 其他Docker公司或第三方的产品都是围绕Docker引擎进行开发和集成的。如图 所示,Docker引擎位于中心,其…

100种思维模型之升维思维模型-026

爱因斯坦曾说&#xff1a;这个层次的问题&#xff0c;很难靠这个层次的思考来解决。 如&#xff0c;你很穷&#xff0c;然后紧衣缩食&#xff0c;结果却依然入不敷出&#xff1b;你很胖&#xff0c;然后拼命节食&#xff0c;结果却依然大腹便便&#xff1b;你很忙&#xff0c;然…

海洋风场数据

本篇文章主要介绍了三种海洋风场数据集基本信息&#xff08;从官网中提取我认为比较重要的信息&#xff09;&#xff0c;以及如何下载&#xff08;下载中也遇到很多问题&#xff0c;有的问题现在也存在&#xff0c;大家集思广益&#xff0c;看看有没有好的方法&#xff01;&…

CSS - 选择器详解 - 子代、后代选择器详解 - 伪类选择器 - 测试

目录测试准备&#xff1a;子代选择器 >测试代码&#xff1a;指定id的子代选择器后代选择器 (以空格隔开)指定 id 的后代选择器指定 class 类 的后代选择器多空格后代选择器详解 (特别重要)伪类选择器 :参考链接&#xff1a;测试准备&#xff1a; 新建一个测试项目文件夹 te…

WebSocket与Socket、TCP、HTTP的关系

目录&#xff1a;1、名词解析&#xff1b;2、WebSocket简介与原理&#xff1b;3、WebSocket和Http的关系和异同点&#xff1b;4、WebSocket与Socket的区别&#xff1b;5、Socket和TCP/IP&#xff1b;6、一个应用程序的通信链路&#xff1b;1、基础名词解析&#xff1a;&#xf…

十三、Spring对事务的支持

1 事务概述 什么是事务 在一个业务流程当中&#xff0c;通常需要多条DML&#xff08;insert delete update&#xff09;语句共同联合才能完成&#xff0c;这多条DML语句必须同时成功&#xff0c;或者同时失败&#xff0c;这样才能保证数据的安全。多条DML要么同时成功&#xf…

Antlr4:使用grun命令,触发NoClassDefFoundError

1. 意外的发现 在学习使用grun命令时&#xff0c;从未遇到过错误 最近使用grun命令&#xff0c;却遇到了NoClassDefFoundError的错误&#xff0c;使得grun测试工具无法成功启动 错误复现&#xff1a; 使用antlr4命令编译Hello.g4文件&#xff0c;并为指定package&#xff08;…

人工智能学习07--pytorch10--目标检测:RCNN、Faster RCNN

括号里都是弹幕大佬的高赞发言 1 前言 Two Stage检测过程分两步走 前景&#xff1a;需要检测的目标 背景&#xff1a;不感兴趣的 生成候选框&#xff1a;将感兴趣目标框选出来&#xff0c;但是没有进行分类 具体使用哪一种&#xff0c;根据项目需求 自定义数据集 自己写一…

CAS 与 ABA问题

本文通过学习&#xff1a;周阳老师-尚硅谷Java大厂面试题第二季 总结的CAS和ABA相关的笔记一、CAS1、CAS定义CAS Compare-And-Swap&#xff0c;它是CPU并发原语。比较当前工作内存中的值和主物理内存中的值&#xff0c;如果相同则执行规定操作&#xff0c;否者继续比较直到主内…

【MySQL】第18章_MySQL8其它新特性

第18章_MySQL8其它新特性 1. MySQL8新特性概述 MySQL从5.7版本直接跳跃发布了8.0版本&#xff0c;可见这是一个令人兴奋的里程碑版本。MySQL 8版本在功能上做了显著的改进与增强&#xff0c;开发者对MySQL的源代码进行了重构&#xff0c;最突出的一点是多MySQL Optimizer优化器…