【网络编程】第五章 (协议+序列化反序列化+网络版计算器)

🏆个人主页企鹅不叫的博客

​ 🌈专栏

⭐️ 博主码云gitee链接:代码仓库地址

⚡若有帮助可以【关注+点赞+收藏】,大家一起进步!

💙系列文章💙

【网络编程】第一章 网络基础(协议+OSI+TCPIP+网络传输的流程+IP地址+MAC地址)

【网络编程】第二章 网络套接字(socket+UDP协议程序)

【网络编程】第三章 网络套接字(TCP协议程序+多进程+多线程+线程池)

【网络编程】第四章 网络套接字(守护进程+TCP英译汉+TCP通协议讯流程+TCP和UDP对比)



💎一、协议

🏆1.概念

协议,网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定,比如怎么建立连接、怎么互相识别等。

🏆2.序列化和反序列化

  • 为什么要序列化和反序列化?因为如果传输数据是一个字符串,我们可以一个一个字符传输,但是如果传输数据是一个结构化的数据,不能一个一个字节传输。
  • 序列化将结构化的数据转变为字符串,需要编码。
  • 反序列化将字符串变为结构化的数据,需要解码。

将日期结构体拼接成一个字符串

struct date
{
    int year;
    int month;
    int day;
};
year-month-day

🏆3.网络版计算器

成员

//请求
typedef struct request{
	int x; //左操作数
	int y; //右操作数
	char op; //操作符
}request_t;

//响应
typedef struct response{
	int code; //计算状态
	int result; //计算结果
}response_t;

服务器

服务器采用多线程方案,调用accept获得的套接字作为参数传递给线程

//./tcpserver _server_port 
int main(int argc, char* argv[])
{
	if (argc != 2){
		cerr << "Usage: " << argv[0] << " port" << endl;
		exit(1);
	}
	int port = atoi(argv[1]);

	//创建套接字
	int listen_sock = socket(AF_INET, SOCK_STREAM, 0);

	//绑定
	struct sockaddr_in local;
	memset(&local, 0, sizeof(local));
	local.sin_family = AF_INET;
	local.sin_port = htons(port);
	local.sin_addr.s_addr = htonl(INADDR_ANY);
	
	if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
		cerr << "bind error!" << endl;
		exit(3);
	}

	//监听
	if (listen(listen_sock, 5) < 0){
		cerr << "listen error!" << endl;
		exit(4);
	}

	//启动服务器
	struct sockaddr peer;
	while(1){
		socklen_t len = sizeof(peer);
		int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
		if (sock < 0){
			cerr << "accept error!" << endl;
			continue;
		}
		pthread_t tid;
		int* p = new int(sock);
		pthread_create(&tid, nullptr, Routine, p);
	}
	return 0;
}

如果客户端发来的计算请求存在除0、模0、非法运算等问题,就将响应结构体当中的状态字段对应设置为1、2、3即可

void* Routine(void* arg)
{
	pthread_detach(pthread_self()); //分离线程
	int sock = *(int*)arg;
	
	while (1){
		request_t rq;
        ssize_t size = read(sock, &rq, sizeof(rq));
		if (size > 0){
			response_t rp = { 0, 0 };
			switch (rq.op){
			case '+':
				rp.result = rq.x + rq.y;
				break;
			case '-':
				rp.result = rq.x - rq.y;
				break;
			case '*':
				rp.result = rq.x * rq.y;
				break;
			case '/':
				if (rq.y == 0){
					rp.code = 1; //除0错误
				}
				else{
					rp.result = rq.x / rq.y;
				}
				break;
			case '%':
				if (rq.y == 0){
					rp.code = 2; //模0错误
				}
				else{
					rp.result = rq.x % rq.y;
				}
				break;
			default:
				rp.code = 3; //非法运算
				break;
			}
            write(sock, &rp, sizeof(rp));
		}
		else if (size == 0){
			cout << "service done" << endl;
			break;
		}
		else{
			cerr << "read error" << endl;
			break;
		}
	}
	close(sock);
	return nullptr;
}

客户端

//./tcpClient  _server_ip _server_port
int main(int argc, char* argv[])
{
	if (argc != 3){
		cerr << "Usage: " << argv[0] << " server_ip server_port" << endl;
		exit(1);
	}
	string server_ip = argv[1];
	int server_port = atoi(argv[2]);

	//创建套接字
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0){
		cerr << "socket error!" << endl;
		exit(2);
	}

	//连接服务器
	struct sockaddr_in peer;
	peer.sin_family = AF_INET;
	peer.sin_port = htons(server_port);
	peer.sin_addr.s_addr = inet_addr(server_ip.c_str());
	if (connect(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0){
		cerr << "connect failed!" << endl;
		exit(3);
	}

	//发起请求
	while (true){
		//构建请求
		request_t rq;
		cout << "请输入左操作数# ";
		cin >> rq.x;
		cout << "请输入右操作数# ";
		cin >> rq.y;
		cout << "请输入需要进行的操作[+-*/%]# ";
		cin >> rq.op;
        write(sock, &rq, sizeof(rq));
		
		//接收请求响应
		response_t rp;
        ssize_t size = read(sock, &rp, sizeof(rp));

		cout << "status: " << rp.code << endl;
		cout << rq.x << rq.op << rq.y << "=" << rp.result << endl;
	}
	return 0;
}

测试

运行服务端后再让客户端连接服务端,此时服务端就会对客户端发来的计算请求进行处理,并会将计算后的结果响应给客户端。

image-20240727115736380


  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

penguin_bark

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值