一、服务端
1.编写流程
1.1 创建套接字
在内核中创建socket结构体,将进程与网卡关联起来。
1.2 为套接字绑定地址信息
给创建的套接字socket结构体描述源端地址信息。
作用:
1)告诉系统,网卡收到的那条数据应该交给我来处理;
2)当发生数据时,使用绑定的地址信息作为源端地址信息。
1.3 接收数据
从socket的接收缓冲区中取出数据。
1.4 发送数据
把要发送的数据放到发送缓冲区中。
1.5 关闭套接字
2. 代码实现
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<netinet/in.h> //struct_sockaddr_in 结构 #include<arpa/inet.h> //字节序转换接口 #include<sys/socket.h> //socket接口头文件 #include<string.h> int main() { //1.创建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sockfd < 0) { perror("Create socket error!"); return -1; } //2.为套接字绑定地址信息 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(8888); addr.sin_addr.s_addr = inet_addr("192.168.247.128"); socklen_t len = sizeof(struct sockaddr_in); int ret = bind(sockfd, (struct sockaddr*)&addr, len);//绑定地址信息 if (ret < 0) { perror("bind error!"); return -1; } //接收&发送 数据 while (1) { //3.接收数据 char buf[1024] = {0}; struct sockaddr_in client;//保存发送方的地址信息 ret = recvfrom(sockfd, buf, 1023, 0, (struct sockaddr*)&client, &len); if (ret < 0) { perror("recvfrom error!"); close(sockfd); return -1; } //打印客户端的信息 printf("client:%s 端口:%d 数据:%s\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port), buf); //4.发送数据 printf("server send: "); fflush(stdout); char data[1024] = {0}; fgets(data, 1023, stdin); ret = sendto(sockfd, data, strlen(data), 0, (struct sockaddr*)&client, len); if (ret < 0) { perror("sendto error!"); close(sockfd); return -1; } } //5.关闭套接字 close(sockfd); return 0; }
二、客户端
1.编写流程
1.1 创建套接字
1.2 为套接字绑定地址信息(不推荐)
客户端并不推荐主动绑定地址。
原因:
1)绑定之后,程序只能启动一个;
2)客户端并不需要固定使用某个地址。
1.3 向服务器发送数据
发送数据前,若socket没有绑定指定地址,则系统会自动选择一个合适的地址信息进行绑定。
1.4 接收数据
1.5 关闭套接字
2. 代码实现
通过封装一个客户端类,可以更加便捷的创建多个客户端。
2.1 客户端头文件-类封装
#include<iostream> #include<string> #include<unistd.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/socket.h> //封装UDPsocket类 //通过类实例化对象,调用成员接口,简单的完成服务器与客户端搭建 class UDPsocket { private: int _sockfd; public: UDPsocket() : _sockfd(-1) {} ~UDPsocket() {if(_sockfd != -1) close(_sockfd);} //1.创建套接字 bool Socket() { _sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (_sockfd < 0) { perror("create socket error!"); return false; } return true; } //2.绑定地址信息 bool Bind(const std::string &ip, uint16_t port) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip.c_str()); socklen_t len = sizeof(struct sockaddr_in); int ret = bind(_sockfd, (struct sockaddr*)&addr, len); if (ret < 0) { perror("bind error!"); return false; } return true; } //3.发送数据 bool Send(const std::string &data, const std::string &ip, uint16_t port) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip.c_str()); socklen_t len = sizeof(struct sockaddr_in); int ret = sendto(_sockfd, data.c_str(), data.size(), 0, (struct sockaddr*)&addr, len); if (ret < 0) { perror("sendto error!"); return false; } return true; } //4.接收数据 bool Recve(std::string *buf, std::string *ip = NULL, uint16_t *port = NULL) { struct sockaddr_in addr; socklen_t len = sizeof(struct sockaddr_in); char temp[4096] = {0}; int ret = recvfrom(_sockfd, temp, 4095, 0, (struct sockaddr*)&addr, &len); if (ret < 0) { perror("recvfrom error!"); return false; } if (ip != NULL) *ip = inet_ntoa(addr.sin_addr); if (port != NULL) *port = ntohs(addr.sin_port); *buf = temp; return true; } //5.关闭套接字 bool Close() { if (_sockfd != -1) close(_sockfd); return true; } };
2.2 客户端实现
#include "udp_socket.hpp" #define CHECK_RETURN(x) if ((x) == false) {return -1;} int main(int argc, char *argv[]) { if (argc != 3) { std::cout<<"Usage: ./udp_client ip port"<< std::endl; std::cout<<"Server address!"<< std::endl; return -1; } UDPsocket sock;//创建客户端对象 std::string srv_ip = argv[1]; int srv_port = std::stoi(argv[2]); //1.创建套接字 CHECK_RETURN(sock.Socket()); //2. 绑定地址信息----客户端不推荐 while (1) { std::cout<<"client send:"; fflush(stdout); std::string buf; std::cin>>buf; //3.发送数据 CHECK_RETURN(sock.Send(buf, srv_ip, srv_port)); //4.接收数据 buf.clear(); CHECK_RETURN(sock.Recve(&buf)); std::cout<<"server reply:" << buf << std::endl; } //5.关闭套接字 CHECK_RETURN(sock.Close()); return 0;; }