0


UDP服务器—实现数据通信

前言

  1. 在这篇文章中为大家介绍如何通过编码实现数据通信,实现思路是根据前面介绍的网络编程函数编写一个服务端和客户端,实现客户端和服务端双方通信

1.接口介绍

创建套接字

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. int socket(int domain, int type, int protocol);

domain:网络通信采用AF_INET

type:提供的服务类型,包含TCP流式服务和UDP数据包服务

实现UDP服务器参数设置为SOCK_DGRAM

protocol:采用的协议,一般设置为0,前面的两个参数决定了第三个参数

创建套接字的本质是告诉操作系统要进行网络通信,然后由操作系统在底层维护一个文件缓冲区,创建成功返回该文件描述符,后续数据通信,上层选择将数据放到该文件缓冲区中或者从文件缓冲区中去读数据

返回值:打开的文件描述符

绑定端口号

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

sockfd : 创建套接字的返回值

struct sockaddr* addr:使用时填充该结构体的字段

a.协议字段

b.端口号

端口号在进行填充的时候需要先进行转换为网络字节序

  1. #include <arpa/inet.h>
  2. uint16_t htons(uint16_t hostshort);

获取对方传输过来的端口号时需要将网络字节序转化为本机

  1. #include <arpa/inet.h>
  2. uint16_t ntohs(uint16_t netshort);

c.IP地址

一般在服务器的实现中IP地址是不需要绑定的

在客户端中发送数据需要填充服务端的IP地址,因为在用户层IP地址是字符串类型,在网络上传输时IP地址是整型数据,所以需要进行转换

  1. #include <sys/socket.h>
  2. #include <netinet/in.h>
  3. #include <arpa/inet.h>
  4. in_addr_t inet_addr(const char *cp);

需要获取对方网络中传输过来的IP地址需要将整型转换为字符串类型

  1. #include <sys/socket.h>
  2. #include <netinet/in.h>
  3. #include <arpa/inet.h>
  4. char *inet_ntoa(struct in_addr in);

注:因为是网络通信,所以在填充好字段之后需要在传参的时候强转为sockaddr_in

addrlen:结构体的大小

接受数据

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
  4. struct sockaddr *src_addr, socklen_t *addrlen);

sockfd:文件描述符

buf:用户缓冲区

len:用户缓冲区的大小

flags:设置为0表示阻塞式调用

src_addr:输出型参数,用来获取对方的IP地址和port

addrlen:结构体的大小

发送数据

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
  4. const struct sockaddr *dest_addr, socklen_t addrlen);

sockfd:文件描述符

buf:用户缓冲区

len:用户缓冲区的大小

flags:设置为0表示阻塞式调用

dest_addr:填充需要发送对端主机的信息,包含IP地址和port端口号

addrlen:结构体的大小

有了上面这些的这些接口,下面我们就可以正式编写一个客户端和一个服务端了

2.编写服务器

形成makefile

  1. .PHONY:all
  2. all:udpServer udpClient
  3. udpServer:udpServer.cc
  4. g++ -o $@ $^ -std=c++11
  5. udpClient:udpClient.cc
  6. g++ -o $@ $^ -std=c++11
  7. .PHONY:clean
  8. clean:
  9. rm -rf udpServer udpClient

编写服务器:udpServer.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <strings.h>
  5. #include <cerrno>
  6. #include <cstring>
  7. #include <cstdlib>
  8. #include <functional>
  9. #include <unistd.h>
  10. #include <sys/types.h>
  11. #include <sys/socket.h>
  12. #include <arpa/inet.h>
  13. #include <netinet/in.h>
  14. namespace Server
  15. {
  16. using namespace std;
  17. const static string defaultIP = "0.0.0.0";
  18. enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR};
  19. class udpServer
  20. {
  21. public:
  22. udpServer(uint16_t port, const string &ip = defaultIP)
  23. :_port(port),_ip(ip),_sockfd(-1)
  24. {}
  25. void initServer()
  26. {
  27. //1.创建socket
  28. _sockfd = socket(AF_INET,SOCK_DGRAM,0);
  29. if(_sockfd == -1)
  30. {
  31. cerr<<"socket error:" << errno << strerror(errno) << endl;
  32. exit(SOCKET_ERR);
  33. }
  34. //2.绑定port和ip
  35. struct sockaddr_in local;
  36. bzero(&local,sizeof(local));
  37. local.sin_family = AF_INET;
  38. local.sin_port = htons(_port);
  39. local.sin_addr.s_addr = htonl(INADDR_ANY);
  40. int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
  41. if(n == -1)
  42. {
  43. cerr<<"bind error:" << errno << strerror(errno) << endl;
  44. exit(BIND_ERR);
  45. }
  46. }
  47. void startServer()
  48. {
  49. char buffer[1024];
  50. for(;;)
  51. {
  52. struct sockaddr_in peer;
  53. socklen_t len = sizeof(peer);
  54. //peer len:输入,输出型参数:用来保存客户端的ip和port
  55. ssize_t s = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
  56. //打印发送来的数据
  57. if(s)
  58. {
  59. buffer[s] = { 0 };
  60. //inet_ntoa:网络字节序->int int->点分十进制
  61. string clientIp = inet_ntoa(peer.sin_addr);
  62. //ntohl:网络字节序->int
  63. uint16_t clientPort = ntohs(peer.sin_port);
  64. string message = buffer;
  65. cout << clientIp << "[" << clientPort << "]" << message << endl;
  66. }
  67. }
  68. }
  69. ~udpServer()
  70. {}
  71. private:
  72. uint16_t _port;
  73. string _ip;
  74. int _sockfd;
  75. };
  76. }

启动服务器:udpServer.cc

  1. #include "udpServer.hpp"
  2. #include <memory>
  3. using namespace Server;
  4. static void Usage(string proc)
  5. {
  6. cout << "\nUsage:\n\t" << proc << " local_port\n\n";
  7. }
  8. int main(int argc, char *argv[])
  9. {
  10. if (argc != 2)
  11. {
  12. Usage(argv[0]);
  13. exit(USAGE_ERR);
  14. }
  15. uint16_t port = atoi(argv[1]);
  16. unique_ptr<udpServer> usvr(new udpServer(port));
  17. usvr->initServer();
  18. usvr->startServer();
  19. return 0;
  20. }

3.编写客户端

编写客户端:udpClient.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <strings.h>
  5. #include <cerrno>
  6. #include <cstring>
  7. #include <cstdlib>
  8. #include <functional>
  9. #include <unistd.h>
  10. #include <sys/types.h>
  11. #include <sys/socket.h>
  12. #include <arpa/inet.h>
  13. #include <netinet/in.h>
  14. namespace Client
  15. {
  16. using namespace std;
  17. class udpClient
  18. {
  19. public:
  20. udpClient(const string& serverIp,const uint16_t serverPort)
  21. :_serverIp(serverIp),_serverPort(serverPort),_sockfd(-1) {}
  22. void initClient()
  23. {
  24. //1.创建socket
  25. _sockfd = socket(AF_INET,SOCK_DGRAM,0);
  26. if(_sockfd == -1)
  27. {
  28. cerr<<"socket error:" << errno << strerror(errno) << endl;
  29. exit(2);
  30. }
  31. //client一定需要bind,服务器只有一个,但是客户端有许多个,所以服务端需要显示的绑定端口号
  32. //客户端一般不需要自己显示的绑定,而是由os自动形成端口号进行绑定
  33. }
  34. void run()
  35. {
  36. struct sockaddr_in server;
  37. memset(&server,0,sizeof(server));
  38. server.sin_family = AF_INET;
  39. server.sin_addr.s_addr = inet_addr(_serverIp.c_str());
  40. server.sin_port = htons(_serverPort);
  41. string message;
  42. while(1)
  43. {
  44. cout << "请输入:";
  45. cin >> message;
  46. //发送数据到客户端
  47. sendto(_sockfd,message.c_str(),message.size(),0,(const struct sockaddr*)&server,
  48. sizeof(server));
  49. }
  50. }
  51. private:
  52. string _serverIp;
  53. int _sockfd;
  54. uint16_t _serverPort;
  55. };
  56. }

启动客户端:udpClient.cc

  1. #include"udpClient.hpp"
  2. #include<memory>
  3. using namespace Client;
  4. static void Usage(string proc)
  5. {
  6. cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
  7. }
  8. int main(int argc,char* argv[])
  9. {
  10. if(argc != 3)
  11. {
  12. Usage(argv[0]);
  13. exit(1);
  14. }
  15. string serverip = argv[1];
  16. uint16_t serverport = atoi(argv[2]);
  17. unique_ptr<udpClient> uct(new udpClient(serverip,serverport));
  18. uct->initClient();
  19. uct->run();
  20. return 0;
  21. }

4.测试

说明:因为本次是在一台主机上,所以为了做测试将IP地址绑定为"127.0.0.1",该IP地址的特点是专门用来在本主机上做测试,称为本地环回,后续在不同主机上通信的时候只需要将IP地址改为对方主机的IP地址即可。

此时我们就可以看到当客户端向服务器发送数据时,服务器收到了数据,并且将数据回显出来

总结

通过上面的编码,使用UDP协议简单实现了一个服务器,可以用来进行数据通信了,是不是感觉很神奇呀,看到这里你就会发现,原来网络通信也并不复杂,和系统内部进程间通信有异曲同工之妙关于网络通信的更多细节,后面再一一为大家介绍,我们下次再见!


本文转载自: https://blog.csdn.net/qq_65307907/article/details/132149986
版权归原作者 终为nullptr 所有, 如有侵权,请联系我们删除。

“UDP服务器—实现数据通信”的评论:

还没有评论