TCP网络计算器--认识网络协议

Bertha 。 2024-04-07 15:33 214阅读 0赞

目录

    • 什么是协议?
    • TCP简易版网络计算器
      • protocol.hpp(协议)
      • server.hpp
      • server.cc
      • client.hpp
      • client.cc
      • 总结

什么是协议?

协议就是一种约定,类似客户端与服务器之间为便于交互而制定的约定;

TCP简易版网络计算器

例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最 后再把结果返回给客户端

方案一:

客户端发送类似于”1+1”的字符串,server收到以后进行解析,计算出结果再返回给客户端;

则有规定:

  • 这个字符串中有两个操作数, 都是整形;
  • 两个数字之间会有一个字符是运算符, 运算符只能是 + ;
  • 数字和运算符之间没有空格;

方案二:

定义结构体来表示我们需要交互的信息;

规定:

  1. //请求
  2. struct Request{
  3. int a;//操作数a
  4. int b;//操作数b
  5. char op;//操作类型:+-*/%
  6. };//规定进行a op b运算;
  7. //响应
  8. struct Response{
  9. int statu = 0;//返回状态码,正常0,异常-1;
  10. int ret;//运算结果:规定返回a op b 的结果 ret;
  11. };

显然方案一和方案二都能完成需求,上述的规定,就是一种server与client为了方便交互而进行的约定,也就是一种协议,下面采取方案2实现;

protocol.hpp(协议)

  1. //请求
  2. struct Request{
  3. int a;//操作数a
  4. int b;//操作数b
  5. char op;//操作类型:+-*/%
  6. };//规定进行a op b运算;
  7. //响应
  8. struct Response{
  9. int statu = 0;//返回状态码,正常0,异常-1;
  10. int ret;//运算结果:规定返回a op b 的结果 ret;
  11. };

server.hpp

  1. #pragma once
  2. #include "protocol.hpp"
  3. #include <iostream>
  4. #include <sys/socket.h>
  5. #include <unistd.h>
  6. #include <sys/wait.h>
  7. #include <cstdio>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <strings.h>
  11. #include <pthread.h>
  12. #include <string>
  13. using namespace std;
  14. class TcpServer
  15. {
  16. int listen_sock;
  17. int _port;
  18. public:
  19. TcpServer(int port) : _port(port)
  20. {
  21. }
  22. void Init()
  23. {
  24. listen_sock = socket(AF_INET, SOCK_STREAM, 0);
  25. sockaddr_in server;
  26. server.sin_family = AF_INET;
  27. server.sin_port = htons(_port);
  28. server.sin_addr.s_addr = INADDR_ANY;
  29. if (bind(listen_sock, (sockaddr *)&server, sizeof(server)))
  30. {
  31. cerr << "bind error" << endl;
  32. exit(1);
  33. }
  34. //服务器初始化完成,进入监听状态;
  35. listen(listen_sock, 5);
  36. }
  37. static void *fun(void *p) //类里搞多线程的执行函数毕需static,不然会多一个参数导致匹配不上;
  38. {
  39. int sock = *(int *)p;
  40. delete (int*)p;
  41. while (true)
  42. {
  43. Request rq;
  44. Response rs;
  45. int c = recv(sock, &rq, sizeof(rq), 0); //接收数据,类似于read, 少了一个反序列化(将所收数据转成对应的类型录入)
  46. if (c > 0)
  47. {
  48. char op = rq.op;
  49. int a = rq.a;
  50. int b = rq.b;
  51. cout<<"client send:"<<a<<op<<b<<endl;
  52. switch (op)
  53. {
  54. //进行运算;
  55. case '+':
  56. rs.ret = a + b;
  57. break;
  58. case '-':
  59. rs.ret = a - b;
  60. break;
  61. case '*':
  62. rs.ret = a * b;
  63. break;
  64. case '/':
  65. if (b == 0)
  66. {
  67. rs.statu = -1; //运算格式错误;
  68. break;
  69. }
  70. rs.ret = a / b;
  71. break;
  72. case '%':
  73. if (b == 0)
  74. {
  75. rs.statu = -1; //运算格式错误;
  76. break;
  77. }
  78. rs.ret = a % b;
  79. break;
  80. default:
  81. rs.statu = -1; //运算格式错误;
  82. }
  83. send(sock, &rs, sizeof(rs), 0); //发回数据,类似于write,少了一个序列化(将所发数据转成一个字符串发送出去)
  84. }
  85. else{
  86. cout<<"client close!,me too"<<endl;
  87. close(sock);
  88. break;
  89. }
  90. }
  91. return nullptr;
  92. }
  93. void Loop()
  94. {
  95. while (true)
  96. {
  97. sockaddr_in peer;
  98. peer.sin_family = AF_INET;
  99. peer.sin_port = htons(_port);
  100. peer.sin_addr.s_addr = INADDR_ANY;
  101. socklen_t len = sizeof(peer);
  102. int sock = accept(listen_sock, (sockaddr *)&peer, &len); //连接到client的socket;
  103. int *p = new int(sock); //防止局部sock销毁导致找不到sock,搞堆上;
  104. pthread_t tid;
  105. pthread_create(&tid, 0, fun, p);
  106. pthread_detach(tid); //线程分离,直接accept下一个client的连接,实现并发
  107. }
  108. }
  109. ~TcpServer()
  110. {
  111. if (listen_sock >= 0)
  112. close(listen_sock);
  113. }
  114. };

server.cc

  1. #include"server.hpp"
  2. int main(int argc,char*argv[])
  3. {
  4. TcpServer svr(atoi(argv[1]));
  5. svr.Init();
  6. svr.Loop();
  7. return 0;
  8. }

client.hpp

  1. #pragma once
  2. #include"server.hpp"
  3. class Client{
  4. char* _ip;
  5. int _port;
  6. int sock;
  7. public:
  8. Client(char* ip,int port):_ip(ip),_port(port)
  9. {
  10. }
  11. void Init()
  12. {
  13. sock = socket(AF_INET,SOCK_STREAM,0);
  14. //client不需要bind listen之类的了
  15. }
  16. void Run()
  17. {
  18. sockaddr_in peer;
  19. peer.sin_family = AF_INET;
  20. peer.sin_port = htons(_port);
  21. peer.sin_addr.s_addr = inet_addr(_ip);
  22. connect(sock,(sockaddr*)&peer,sizeof(peer));
  23. while(true)
  24. {
  25. Request rq;
  26. Response rp;
  27. cout<<"请输入第一个数据:";
  28. cin>>rq.a;
  29. cout<<"请输入运算符:";
  30. cin>>rq.op;
  31. cout<<"请输入第二个数据:";
  32. cin>>rq.b;
  33. send(sock,&rq,sizeof(rq),0); //发数据,类似于write,少了一个序列化(将所发数据转成一个字符串发送出去)
  34. int c = recv(sock,&rp,sizeof(rp),0); //接收数据,类似于read, 少了一个反序列化(将所收数据转成对应的类型录入)
  35. if(rp.statu!=0){
  36. cout<<"输入有误!statu:"<<rp.statu<<endl;
  37. continue;
  38. }
  39. if(c>0){
  40. cout<<"运算结果:"<<rp.ret<<endl;
  41. }
  42. }
  43. }
  44. ~Client(){
  45. if(sock>=0) close(sock);}
  46. };

client.cc

  1. #include"client.hpp"
  2. int main(int argc,char*argv[])
  3. {
  4. Client cl(argv[1],atoi(argv[2]));
  5. cl.Init();
  6. cl.Run();
  7. return 0;
  8. }

运行结果:

在这里插入图片描述

总结

这里订制的协议就是当服务器获得client发出的request类型以后,需要计算出其三个成员a op b的结果,顺序类型都已固定不可变,结果存入response类型后返回给client,client使用一个response类型进行接收;

发表评论

表情:
评论列表 (有 0 条评论,214人围观)

还没有评论,来说两句吧...

相关阅读

    相关 网络协议TCP 拥塞控制

    概述 TCP 拥塞控制能够提高网络利用率,降低丢包率,并保证网络资源对每条数据流的公平性。拥塞控制主要有几种:慢启动、拥塞避免、快速重传 以及快速恢复。 拥塞控制的最终