通过Socket configuration controls 获取网络设备地址

本文介绍了一种在C语言中通过ioctl系统调用获取网络接口信息的方法,包括创建套接字、获取接口地址等关键步骤,并提供了具体的实现代码。

常用的头文件, 基本通用了.

#ifndef _INC_H #define _INC_H #include <stdio.h> #include <errno.h> #include <time.h> #include <signal.h> #include <stdint.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <inttypes.h> #include <sys/types.h> #include <sys/time.h> #include <sys/file.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/select.h> #include <net/if.h> #include <sys/ioctl.h> #endif

代码实现:

其他Socket configuration controls可以在/usr/include/bits/ioctls.h中查看到.

static int iface_sock = -1; int iface_init(void) { int sock = -1; sock = socket( AF_INET, SOCK_DGRAM, 0 ); if ( sock < 0 ) { fprintf( stderr, "create socket for iface fail.\n" ); return -1; } iface_sock = sock; return 0; } void iface_destory(void) { if ( iface_sock < 0 ) return ; close( iface_sock ); } int iface_address_by_name(char *devname, uint32_t *addr) { int err = -1; struct ifreq ifr; if ( iface_sock < 0 ) { goto _out; } strcpy( ifr.ifr_name, devname ); err = ioctl( iface_sock, SIOCGIFADDR, &ifr, sizeof( ifr ) ); if ( err < 0 ) { goto _out; } printf( "DEBUG: NIC-%s: %s\n", devname, inet_ntoa( ((struct sockaddr_in *)( &ifr.ifr_addr ))->sin_addr ) ); if ( addr ) { *addr = ((struct sockaddr_in *)( &ifr.ifr_addr ))->sin_addr.s_addr; } err = 0; _out: return err; } int iface_address_by_index(uint32_t index, uint32_t *addr) { int err = -1; struct ifreq ifr; if ( iface_sock < 0 ) { goto _out; } ifr.ifr_ifindex = index; err = ioctl( iface_sock, SIOCGIFADDR, &ifr, sizeof( ifr ) ); if ( err < 0 ) { goto _out; } printf( "NIC-%u-: ip=%s\n", index, inet_ntoa( ((struct sockaddr_in *)( &ifr.ifr_addr ))->sin_addr ) ); if ( addr ) { *addr = ((struct sockaddr_in *)( &ifr.ifr_addr ))->sin_addr.s_addr; } err = 0; _out: return err; } int lookup_nic_address(char *devname, uint32_t *addr) { int err = -1; int sock = 0; struct ifconf ifcs; struct ifreq *ifr; sock = socket( AF_INET, SOCK_DGRAM, 0 ); if ( sock < 0 ) { goto _out; } ifcs.ifc_len = 0; ifcs.ifc_req = NULL; err = ioctl( sock, SIOCGIFCONF, &ifcs, sizeof( ifcs ) ); if ( err < 0 ) { goto _out; } if ( ifcs.ifc_len > 0 ) { ifcs.ifc_req = malloc(ifcs.ifc_len); if (ifcs.ifc_req) { if (ioctl(sock, SIOCGIFCONF, &ifcs, sizeof( ifcs ) ) < 0) { free(ifcs.ifc_req); goto _out; } else { for (ifr = ifcs.ifc_req; (char *)ifr < (char *)ifcs.ifc_req + ifcs.ifc_len; ++ifr) { /*printf("%s\n", ifr->ifr_name);*/ if (!strcmp(ifr->ifr_name, devname )) { printf( "INIT: NIC-%s index=%u ip=%s\n", devname, ifr->ifr_ifindex, inet_ntoa( ((struct sockaddr_in *)( &ifr->ifr_addr ))->sin_addr ) ); if ( addr ) { *addr = ((struct sockaddr_in *)( &ifr->ifr_addr ))->sin_addr.s_addr; } err = 0; break; } } free(ifcs.ifc_req); } } } _out: if ( sock > 0 ) { close( sock ); } return err; }

using System; using System.ComponentModel; using System.Net; using System.Text; using System.Windows.Forms; using HslCommunication; using HslCommunication.ModBus; using Newtonsoft.Json; using StackExchange.Redis; using static System.Net.WebRequestMethods; namespace WinFormsApp2 { public partial class Form1 : Form { private ModbusTcpNet plcClient; private TextBox txtResult; private Button btnConnect; private Button btnRead; private Button btnDisconnect; private ComboBox cboRegisterType; private TextBox txtStartAddress; private TextBox txtLength; private Label lblRegisterType; private Label lblStartAddress; private Label lblLength; private CheckBox chkHexFormat; // 添加十六进制格式选项 private BackgroundWorker plcWorker; private HttpListener httpListener; private Thread serverThread; private bool isServerRunning = false; //http://localhost:5000/api/devices 获取所有的IP //http://localhost:5000/api/device?id=device:ip-192.168.1.88 //获取单独IP的数据 private const string ApiBaseUrl = "http://localhost:5000/"; private ConnectionMultiplexer _redisConnection; private IDatabase _redisDb; public Form1() { InitializeHttpServer(); // 添加控件到窗体 InitializeRedis(); // 初始化后台工作组件 plcWorker = new BackgroundWorker(); plcWorker.WorkerSupportsCancellation = true; plcWorker.DoWork += PlcWorker_DoWork; plcWorker.RunWorkerCompleted += PlcWorker_RunWorkerCompleted; InitializeComponent(); // 事件绑定 //btnRead.Click += (s, e) => ReadPLCData(); //btnDisconnect.Click += (s, e) => DisconnectPLC(); ////btnConnect.Click += (s, e) => ConnectToPLC(); // 修改事件绑定 btnRead.Click += (s, e) => StartPlcReading(); btnDisconnect.Click += (s, e) => StopPlcReading(); } // 启动PLC读取 private void StartPlcReading() { if (plcWorker.IsBusy) { AppendResultText("PLC读取已在后台运行"); return; } btnRead.Enabled = false; btnDisconnect.Enabled = true; AppendResultText("启动PLC后台读取..."); // 获取要连接的PLC列表 string[] plcList = txtStartAddress.Text.Split(','); // 启动后台工作 plcWorker.RunWorkerAsync(plcList); } private void InitializeRedis() { var config = new ConfigurationOptions { EndPoints = { "localhost:6379" }, Password = "ymkj", AbortOnConnectFail = false, ConnectRetry = 3, ConnectTimeout = 5000 }; _redisConnection = ConnectionMultiplexer.Connect(config); _redisDb = _redisConnection.GetDatabase(); } // 停止PLC读取 private void StopPlcReading() { if (plcWorker.IsBusy) { plcWorker.CancelAsync(); AppendResultText("正在停止PLC后台读取..."); } else { AppendResultText("没有正在运行的PLC读取任务"); } btnRead.Enabled = true; btnDisconnect.Enabled = false; } // 后台工作执行 private void PlcWorker_DoWork(object sender, DoWorkEventArgs e) { while (true) { BackgroundWorker worker = (BackgroundWorker)sender; string[] plcList = (string[])e.Argument; foreach (string plcInfo in plcList) { // 检查是否请求取消 if (worker.CancellationPending) { e.Cancel = true; return; } string[] plcDetails = plcInfo.Split(':'); if (plcDetails.Length < 2) { AppendResultText($"无效的PLC配置: {plcInfo}"); continue; } string ip = plcDetails[0]; string port = plcDetails[1]; try { AppendResultText($"连接PLC: {ip}:{port}"); ConnectToPLC(ip, port); if (plcClient == null) { AppendResultText($"无法连接到PLC: {ip}:{port}"); continue; } AppendResultText($"开始读取PLC数据: {ip}:{port}"); // 读取数据 ReadMBits(0, 2005); ReadXBits(0, 35); ReadYBits(0, 31); ReadDRegisters(1, 3428); AppendResultText($"完成读取PLC数据: {ip}:{port}"); } catch (Exception ex) { AppendResultText($"读取PLC数据异常({ip}:{port}): {ex.Message}"); } finally { // 添加延迟,避免过于频繁的连接 Thread.Sleep(1000); } // 检查是否请求取消 if (worker.CancellationPending) { e.Cancel = true; return; } } } } // 后台工作完成 private void PlcWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { AppendResultText("PLC后台读取已取消"); } else if (e.Error != null) { AppendResultText($"PLC后台读取发生错误: {e.Error.Message}"); } else { AppendResultText("PLC后台读取完成"); } btnRead.Enabled = true; btnDisconnect.Enabled = false; } private void InitializeHttpServer() { httpListener = new HttpListener(); httpListener.Prefixes.Add(ApiBaseUrl); // 添加API路由 this.Controls.Add(new Label { Text = $"API服务地址: {ApiBaseUrl}", Location = new System.Drawing.Point(0, 0), AutoSize = true }); // 添加服务器控制按钮 var btnStartServer = new Button { Text = "启动API服务", Location = new System.Drawing.Point(430, 0), AutoSize = true }; btnStartServer.Click += (s, e) => StartHttpServer(); var btnStopServer = new Button { Text = "停止API服务", Location = new System.Drawing.Point(590, 0), AutoSize = true }; btnStopServer.Click += (s, e) => StopHttpServer(); this.Controls.Add(btnStartServer); this.Controls.Add(btnStopServer); } // 启动HTTP服务器 private void StartHttpServer() { if (isServerRunning) return; try { httpListener.Start(); isServerRunning = true; serverThread = new Thread(ListenForRequests); serverThread.IsBackground = true; serverThread.Start(); AppendResultText($"HTTP服务器已启动,监听地址: {ApiBaseUrl}"); } catch (Exception ex) { AppendResultText($"启动HTTP服务器失败: {ex.Message}"); } } // 停止HTTP服务器 private void StopHttpServer() { if (!isServerRunning) return; isServerRunning = false; httpListener.Stop(); AppendResultText("HTTP服务器已停止"); } // 监听HTTP请求 private void ListenForRequests() { while (isServerRunning) { try { var context = httpListener.GetContext(); ThreadPool.QueueUserWorkItem(ProcessRequest, context); } catch (Exception ex) { AppendResultText($"HTTP监听异常: {ex.Message}"); } } } // 处理HTTP请求 private void ProcessRequest(object state) { var context = state as HttpListenerContext; var request = context.Request; var response = context.Response; // 统一设置CORS头,允许来自 http://localhost:8080 的请求 response.Headers.Add("Access-Control-Allow-Origin", "*"); response.Headers.Add("Access-Control-Allow-Methods", "*"); response.Headers.Add("Access-Control-Allow-Headers", "*"); response.Headers.Add("Access-Control-Max-Age", "86400"); // 处理OPTIONS请求 if (request.HttpMethod == "OPTIONS") { response.StatusCode = 200; response.Close(); return; } try { var path = request.Url.AbsolutePath.ToLower(); switch (path) { case "/api/devices": HandleGetDevices(context); break; case "/api/device": HandleGetDevice(context); break; default: SendResponse(response, "Not Found", 404); break; } } catch (Exception ex) { SendResponse(response, $"Error: {ex.Message}", 500); } finally { } } // 处理获取所有设备请求 private void HandleGetDevices(HttpListenerContext context) { var response = context.Response; // 使用全局Redis连接 if (_redisDb == null || _redisConnection == null) { SendResponse(response, "Redis连接未初始化", 500); return; } try { // 获取所有设备键 var server = _redisConnection.GetServer("localhost", 6379); var deviceKeys = server.Keys(pattern: "device:*").ToArray(); // 构建设备列表 var devices = new List<object>(); foreach (var key in deviceKeys) { // 直接使用key.ToString()避免多次转换 string keyStr = key.ToString(); // 更安全的IP提取方式 string ip = keyStr.StartsWith("device:ip-") ? keyStr.Substring("device:ip-".Length) : keyStr; devices.Add(new { id = keyStr, ip }); } var result = new { success = true, count = devices.Count, devices }; SendJsonResponse(response, result); } catch (Exception ex) { SendResponse(response, $"获取设备列表失败: {ex.Message}", 500); } } // 处理获取特定设备数据请求 private void HandleGetDevice(HttpListenerContext context) { var response = context.Response; var request = context.Request; var deviceId = request.QueryString["id"]; // 验证设备ID参数 if (string.IsNullOrEmpty(deviceId)) { SendResponse(response, "缺少设备ID参数", 400); return; } // 使用全局Redis连接 if (_redisDb == null || _redisConnection == null) { SendResponse(response, "Redis连接未初始化", 500); return; } try { // 检查设备是否存在 if (!_redisDb.KeyExists(deviceId)) { SendResponse(response, $"设备不存在: {deviceId}", 404); return; } // 获取设备所有数据 var entries = _redisDb.HashGetAll(deviceId); // 转换为字典 var data = new Dictionary<string, string>(); foreach (var entry in entries) { data[entry.Name] = entry.Value; } var result = new { success = true, device = deviceId, data }; SendJsonResponse(response, result); } catch (Exception ex) { // 更详细的错误日志 AppendResultText($"获取设备数据失败({deviceId}): {ex}"); SendResponse(response, $"获取设备数据失败: {ex.Message}", 500); } } // 发送JSON响应 private void SendJsonResponse(HttpListenerResponse response, object data) { try { response.ContentType = "application/json"; response.ContentEncoding = Encoding.UTF8; var json = JsonConvert.SerializeObject(data); var buffer = Encoding.UTF8.GetBytes(json); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); } finally { if (response.OutputStream.CanWrite) { response.OutputStream.Close(); } } } // 发送文本响应 private void SendResponse(HttpListenerResponse response, string message, int statusCode) { try { // 记录错误信息 AppendResultText($"[HTTP {statusCode}] {message}"); response.StatusCode = statusCode; response.ContentType = "text/plain"; response.ContentEncoding = Encoding.UTF8; var buffer = Encoding.UTF8.GetBytes(message); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); } finally { response.OutputStream.Close(); } } private void ReadMBits(int start, int length) { // 使用数字地址读取M寄存器(辅助继电器) // M寄存器通常映射到Modbus的线圈地址(0x) // 起始地址 = start + 0(根据PLC型号可能需要偏移) OperateResult<bool[]> readResult = plcClient.ReadCoil(start.ToString(), (ushort)length); if (readResult.IsSuccess) { StringBuilder sb = new StringBuilder(); var hashEntries = new List<HashEntry>(); sb.AppendLine($"M寄存器读取成功 (M{start} - M{start + length - 1}):"); for (int i = 0; i < readResult.Content.Length; i++) { sb.AppendLine($"M{start + i}: {readResult.Content[i]}"); string field = $"M{start + i}"; string value = readResult.Content[i].ToString(); hashEntries.Add(new HashEntry(field, value)); } AppendResultText(sb.ToString()); SetRedis(hashEntries); } else { AppendResultText($"M寄存器读取失败: {readResult.Message}"); } } private void ReadXBits(int start, int length) { // 使用数字地址读取X寄存器(输入继电器) // X寄存器通常映射到Modbus的离散输入地址(1x) // 起始地址 = start + 0(根据PLC型号可能需要偏移) OperateResult<bool[]> readResult = plcClient.ReadDiscrete(start.ToString(), (ushort)length); if (readResult.IsSuccess) { StringBuilder sb = new StringBuilder(); var hashEntries = new List<HashEntry>(); sb.AppendLine($"X寄存器读取成功 (X{start} - X{start + length - 1}):"); for (int i = 0; i < readResult.Content.Length; i++) { sb.AppendLine($"X{start + i}: {readResult.Content[i]}"); string field = $"X{start + i}"; string value = readResult.Content[i].ToString(); hashEntries.Add(new HashEntry(field, value)); } AppendResultText(sb.ToString()); SetRedis(hashEntries); } else { AppendResultText($"X寄存器读取失败: {readResult.Message}"); } } private void ReadYBits(int start, int length) { // 使用数字地址读取Y寄存器(输出继电器) // Y寄存器通常映射到Modbus的线圈地址(0x) // 起始地址 = start + 0(根据PLC型号可能需要偏移) OperateResult<bool[]> readResult = plcClient.ReadCoil(start.ToString(), (ushort)length); if (readResult.IsSuccess) { StringBuilder sb = new StringBuilder(); var hashEntries = new List<HashEntry>(); sb.AppendLine($"Y寄存器读取成功 (Y{start} - Y{start + length - 1}):"); for (int i = 0; i < readResult.Content.Length; i++) { sb.AppendLine($"Y{start + i}: {readResult.Content[i]}"); string field = $"Y{start + i}"; string value = readResult.Content[i].ToString(); hashEntries.Add(new HashEntry(field, value)); } AppendResultText(sb.ToString()); SetRedis(hashEntries); } else { AppendResultText($"Y寄存器读取失败: {readResult.Message}"); } } private void ReadDRegisters(int start, int length) { // 使用数字地址读取D寄存器(数据寄存器) // D寄存器通常映射到Modbus的保持寄存器地址(4x) // 起始地址 = start + 0(根据PLC型号可能需要偏移) OperateResult<short[]> readResult = plcClient.ReadInt16(start.ToString(), (ushort)length); if (readResult.IsSuccess) { StringBuilder sb = new StringBuilder(); var hashEntries = new List<HashEntry>(); sb.AppendLine($"D寄存器读取成功 (D{start} - D{start + length - 1}):"); for (int i = 0; i < readResult.Content.Length; i++) { //if (chkHexFormat.Checked) //{ // sb.AppendLine($"D{start + i}: 0x{readResult.Content[i]:X4} (十进制: {readResult.Content[i]})"); // string field = $"D{start + i}"; // string value = readResult.Content[i].ToString(); // hashEntries.Add(new HashEntry(field, value)); //} //else //{ sb.AppendLine($"D{start + i}: {readResult.Content[i]}"); string field = $"D{start + i}"; string value = readResult.Content[i].ToString(); hashEntries.Add(new HashEntry(field, value)); //} } AppendResultText(sb.ToString()); SetRedis(hashEntries); } else { AppendResultText($"D寄存器读取失败: {readResult.Message}"); } } private void ConnectToPLC(string ip, string duration) { try { // 创建Modbus TCP客户端(替换为实际PLC的IP和端口) plcClient = new ModbusTcpNet(ip, int.Parse(duration)); // 尝试连接 OperateResult connectResult = plcClient.ConnectServer(); if (connectResult.IsSuccess) { AppendResultText("PLC连接成功!"); btnRead.Enabled = true; btnDisconnect.Enabled = true; } else { AppendResultText($"连接失败: {connectResult.Message}"); plcClient = null; } } catch (Exception ex) { AppendResultText($"连接异常: {ex.Message}"); } } private void AppendResultText(string text) { if (txtResult.InvokeRequired) { txtResult.Invoke(new Action<string>(AppendResultText), text); } else { txtResult.AppendText($"[{DateTime.Now:HH:mm:ss}] {text}\r\n"); txtResult.ScrollToCaret(); } } protected override void OnFormClosing(FormClosingEventArgs e) { base.OnFormClosing(e); plcClient?.ConnectClose(); } //private void SetRedis(List<HashEntry> hashEntries) //{ // var config = new ConfigurationOptions // { // EndPoints = { "localhost:6379" }, // Password = "ymkj", // 确保密码正确 // AbortOnConnectFail = false, // ConnectTimeout = 10000, // 可选:增加连接超时时间 // SyncTimeout = 10000 // 可选:增加同步操作超时时间 // }; // using (var redis = ConnectionMultiplexer.Connect(config)) // { // var db = redis.GetDatabase(); // // 读取数据 // // 获取全部字段(返回HashEntry数组) // db.HashSet("device:"+"ip-"+plcClient.IpAddress, hashEntries.ToArray()); // } //} private void SetRedis(List<HashEntry> hashEntries) { try { string deviceKey = $"device:ip-{plcClient.IpAddress}"; _redisDb.HashSet(deviceKey, hashEntries.ToArray()); } catch (Exception ex) { AppendResultText($"Redis存储失败: {ex.Message}"); } } } } Form1.cs 设计没显示出来
06-21
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值