C++socket网络编程——udp服务器

C++socket网络编程——udp服务器

目录

一.端口号 VS  PID

二.套接字编程的类型

三.socket编程接口

四.基于udp的服务端和客户端全部代码

客户端

服务端

五.解释与运行

一些细节:

六.总结


一.端口号 VS  PID

pid已经能够标识一台主机上的一个唯一一个进程了,为什么还需要端口号?

  1. 不是所有的进程都需要网络通信,但是所有的进程都需要都pid;
  2. 系统和网络功能解耦。

        另外,一个进程可以绑定多个端口,但一个端口只能被一个进程绑定。

        系统内定的端口号【0,1023】一般都要有固定的应用层协议使用,如http:80,https:443。

二.套接字编程的类型

  1. 域间套接字编程——同一个机器内
  2. 原始套接字编程——网络工具
  3. 网络套接字编程——用户间的网络通信

        不同的套接字编程类型的接口需要是相同的。

        如同用一个统一的sockaddr作为基类,sockaddr_in和sockaddr_un作为子类。使用的时候我们用if语句区分具体是什么类型,如同下面这段代码:

if(address->type == AF_INET) { // 网络套接字 } else if(address->type == AF_UNIX) { // 域间套接字 }

        为什么操作系统接口设计者在设计的时候不直接使用void*呢,因为那时候C语言还没有void*

三.socket编程接口

// 创建 socket 文件描述符(TCP/UDP,客户端 + 服务器) int socket(int domain, int type, int protocol); // 绑定端口号(TCP/UDP,服务器) int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // 开始监听socket(TCP,服务器) int listen(int sockfd, int backlog); // 接收请求(TCP,服务器) int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 建立连接(TCP,客户端) int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 

四.基于udp的服务端和客户端全部代码

客户端

代码如下:

client.cc:

#include <iostream> #include <sys/types.h> #include <sys/socket.h> // #include <stdio.h> #include <unistd.h> #include <strings.h> #include <arpa/inet.h> // sockaddr_in using namespace std; void usage(const std::string& proc) { cout << "Please use like this : " + proc + " [ip] [port]" << endl; } int main(int argc, char* argv[]) { if(argc != 3) { usage(argv[0]); exit(1); } string serverip = argv[1]; uint16_t serverport = stoi(argv[2]); int sockFd = socket(AF_INET, SOCK_DGRAM, 0); if(sockFd < 0) cerr << "sockFd error" << endl; struct sockaddr_in server; socklen_t sz = sizeof(server); bzero(&server, sz); server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(serverip.c_str()); server.sin_port = htons(serverport); std::string temp; while(true) { cout << "请输入消息@ "; getline(cin, temp); int ret = sendto(sockFd, temp.c_str(), temp.size(), 0, (struct sockaddr*)&server, sz); if(ret < 0) std::cerr << "send error" << std::endl; char buffer[1024]; struct sockaddr_in retServer; socklen_t len = sizeof(retServer); int n = recvfrom(sockFd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&retServer, &len); if(n > 0) { buffer[n] = 0; cout << "收到消息:" << buffer << endl; } } close(sockFd); return 0; } 

服务端

代码如下:

UdpServer.hpp:

#include <string> #include <sys/types.h> #include <sys/socket.h> #include <strings.h> #include <unistd.h> // #include <netinet/in.h> #include <arpa/inet.h> // sockaddr_in #include <iostream> #include <vector> #include <functional> using func_t = std::function<std::string(const std::string&)>; const std::string defaultip = "0.0.0.0"; const uint16_t defaultport = 8080; class UdpServer { public: UdpServer():_sockFd(-1) {} ~UdpServer() { if(_sockFd >= 0) close(_sockFd); } void init(const std::string& ip = defaultip, uint16_t port = defaultport) { _sockFd = socket(AF_INET, SOCK_DGRAM, 0); if(_sockFd < 0) std::cerr << "创建套接字失败" << std::endl; struct sockaddr_in local; bzero(&local, sizeof(local)); local.sin_family = AF_INET; local.sin_addr.s_addr = inet_addr(ip.c_str()); local.sin_port = htons(port); int ret = bind(_sockFd, (const struct sockaddr*)&local, sizeof(local)); if(ret < 0) std::cerr << "绑定本地套接字信息失败" << std::endl; } void run(func_t func) { while(true) { struct sockaddr_in client; socklen_t sz = sizeof(client); char buffer[1024]; int n = recvfrom(_sockFd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&client, &sz); buffer[n] = 0; std::string clientip = inet_ntoa(client.sin_addr); uint16_t clientport = ntohs(client.sin_port); std::cout << "[" << clientip + ":" << clientport << "] send information : " << buffer << std::endl; std::string temp = func(buffer); int ret = sendto(_sockFd, temp.c_str(), temp.size(), 0, (const struct sockaddr*)&client, sz); if(ret < 0) std::cerr << "send error" << std::endl; } } private: int _sockFd; }; 

main.cc:

#include "UdpServer.hpp" #include <memory> #include <string> #include <string.h> std::string echo(const std::string& cData) { return "server : " + cData; } std::string command(const std::string& cData) { std::string ret; FILE* fp = popen(cData.c_str(), "r"); if(fp == nullptr) { perror("popen"); exit(1); } while(true) { char buffer[1024]; memset(buffer, sizeof(buffer), 0); char* ok = fgets(buffer, sizeof(buffer) - 1, fp); if(ok == nullptr) break; ret += buffer; } pclose(fp); return ret; } int main() { std::unique_ptr<UdpServer> udpServer(new UdpServer()); udpServer->init(); udpServer->run(command); return 0; } 

makefile:

.PHONY:all all:UdpServer client UdpServer:main.cc g++ -o $@ $^ -std=c++11 client:client.cc g++ -o $@ $^ -std=c++11 -lpthread .PHONY:clean clean: rm -f UdpServer client

五.解释与运行

  • 客户端:接收用户输入的字符串(可以是普通消息或系统命令),通过 UDP 发送给指定 IP 和端口的服务端,然后等待并打印服务端的响应。
  • 服务端:以类的形式封装(UdpServer),监听指定端口,接收客户端的消息后,通过回调函数处理(默认是执行客户端发送的系统命令并返回执行结果,也可切换为简单回显),再将处理结果通过 UDP 返回给客户端。

运行结果:

        这样,我们就可以远程输入命令了,xshell远程连接服务器就是这样类似的原理。

一些细节:

        AF_INET指明使用ipv4协议,sock_DGRAM指明面向数据报的udp协议,第2个参数协议类型不用管。

        绑定之前将自己的信息填入sockaddr_in这个数据结构中。AF_INET表明是网络socket编程,s_addr表明服务端的ip地址,调用inet _addr函数将它转换成32位数字并且转换为网络字节序列,也就是大端。htons也就是将short类型数据转换为网络字节序列,也就是大端。

        倒数第一个参数len 既是输入型参数,也是输出型参数;倒数第二个参数是输出型参数。

        网络转成主机的字节序,并且32位数字的ip转换为点分十进制字符串形式。

六.总结

        我们还可以进行扩展。将这个服务弄成多人聊天。服务端用一个哈希和客户端的ip地址唯一标识一个客户端,管理起来之后就可以对每一条消息进行广播给每一个客户端了,客户端是多线程的,一个线程负责发送消息,一个线程负责接收消息,为了发送和接收不在一个终端进行,我们可以将接收线程里的标准输出或者标准错误重定向到/dev/pts/某某序号的这个文件夹,用dup2这个函数就可以做到,然后我们客户端就可以在一个终端发送消息,一个终端接收消息。

Read more

jdk 17 下载

可从 Oracle 官方 JDK 17 下载页 直接获取适用于 Windows、macOS、Linux 的 JDK 17 安装包Oracle,链接:https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.htmlOracle 下载方式(按系统选择) 系统推荐下载链接备注WindowsWindows x64 安装包Oracle双击运行安装,适合大多数用户macOS IntelmacOS x64 DMGOracle直接安装macOS Apple SiliconmacOS arm64 DMGOracleM1/M2 芯片适用Linux x64Linux x64 压缩包Oracle解压后配置环境变量Linux ARM64Linux arm64 压缩包Oracle树莓派等设备适用 安装与验证 1. 下载 对应系统安装包。 2.

By Ne0inhk

Java后如何系统准备八股文-30d(小林coding)

本博客资源均来自小林coding Java面试题介绍 | 小林coding 每天 1-1.5 小时,30 天内高效刷完核心内容,附具体时间安排和执行技巧: 附: 我个人觉得急于面试的话,可以把javase、spring、mysql、redis、jvm、计网先看了,再准备自己的项目和场景题。 一、整体策略:按「高频考点优先级」分层突击 核心原则 1. 抓大放小:优先攻克大厂高频模块(Java 并发、JVM、Spring、MySQL、Redis),这些模块占面试题的 70%+ 2. 真题驱动:每个模块先刷「追问型真题」(如 “讲讲 AQS 原理→那 ReentrantLock 怎么用 AQS 实现的?→锁升级过程是怎样的?

By Ne0inhk
用户选剧情,AI写故事:Trae Solo+GLM-4.6实现沉浸式小说创作体验

用户选剧情,AI写故事:Trae Solo+GLM-4.6实现沉浸式小说创作体验

用户选剧情,AI写故事:Trae Solo+GLM-4.6实现沉浸式小说创作体验 项目背景 在人工智能技术迅猛发展的今天,内容创作正经历从“人工主导”向“人机协同”的深刻变革。传统小说创作往往面临灵感枯竭、结构混乱、节奏把控难等痛点,而现有AI写作工具多为单向输出,缺乏互动性与叙事张力。为打破这一局限,我们打造了一款轻量级、免登录的 AI小说创作平台,旨在通过前沿大模型能力赋能每一位故事创作者。 本项目深度融合 Trae Solo 的高效前端开发与任务调度能力,以及 GLM-4.6 在中文叙事、情节构建和风格一致性上的卓越表现,构建出“用户选剧情,AI写故事”的沉浸式创作闭环。用户只需输入一个故事开头,系统即刻生成三个风格各异的情节分支概览;选定其一后,AI将续写300–500字的高质量正文,并在此基础上持续衍生新分支,形成一棵动态生长的故事树。整个过程无需注册,API Key 通过本地存储安全保存,兼顾隐私与便捷。 平台采用 新粗野主义(

By Ne0inhk
Java 时间类(上):JDK7 及以前时间类 Date、SimpleDateFormat、Calendar 最全总结

Java 时间类(上):JDK7 及以前时间类 Date、SimpleDateFormat、Calendar 最全总结

🏠个人主页:黎雁 🎬作者简介:C/C++/JAVA后端开发学习者 ❄️个人专栏:C语言、数据结构(C语言)、EasyX、JAVA、游戏、规划、程序人生 ✨ 从来绝巘须孤往,万里同尘即玉京 文章目录 * Java 时间类(上):JDK7 及以前时间类 Date、SimpleDateFormat、Calendar 最全总结 🕒 * 📝 文章摘要 * 一、时间相关基础知识点 ⏱ * 1. 时间标准 * 2. 时间单位与换算 * 二、Date 时间类 📅 * 1. 概述 * 2. 构造方法 * 3. 成员方法 * 4. 代码示例 * 三、SimpleDateFormat 格式化与解析 ✍️ * 1. 作用

By Ne0inhk