使用GSMComm库获取USSD结果遇超时:代码排查与方案咨询
问题:GSM调制解调器USSD查询超时(No data received from phone)
背景与代码
你尝试用GSMComm库结合ASP.NET Web Forms和C#开发SIM卡余额查询功能,调试确认调制解调器已连接,但调用ExecAndReceiveMultiple时出现超时错误:No data received from phone after waiting for 29999 / 30030 ms。以下是你的代码:
public string SendUssdRequest2(string request) { comm = ConnectAndGetComm(); string data = TextDataConverter.StringTo7Bit(request); string msg = ""; var asPDUencoded = Calc.IntToHex(TextDataConverter.SeptetsToOctetsInt(data)); try { IProtocol protocol = comm.GetProtocol(); string gottenString = protocol.ExecAndReceiveMultiple("AT+CUSD=1," + asPDUencoded + ",15"); var re = new Regex(".*?"); int i = 0; if (!re.IsMatch(gottenString)) { do { protocol.Receive(out gottenString); ++i; } while (!(i >= 5 || re.IsMatch(gottenString) || gottenString.Contains("\r\nOK") || gottenString.Contains("\r\nERROR") || gottenString.Contains("\r\nDONE"))); } string m = re.Match(gottenString).Value.Trim('"'); return PduParts.Decode7BitText(Calc.HexToInt(m)); } catch(Exception e) { msg = e.Message; } finally { comm.ReleaseProtocol(); } return msg; }
代码中的明显问题
无效的正则表达式:
var re = new Regex(".*?");这个正则是匹配任意字符的非贪婪模式,完全无法捕捉USSD的标准响应格式(比如+CUSD: 1,"余额信息",15),导致循环一直等待“匹配成功”,最终触发超时。USSD指令格式错误:
- 拼接的AT指令缺少双引号包裹PDU内容,正确格式应为
AT+CUSD=1,"<PDU_HEX>",15,你当前的代码会生成AT+CUSD=1,xxxxxx,15,调制解调器无法识别这种格式的指令。 - 部分运营商或调制解调器对
CUSD第三个参数(编码方案)要求不同,15代表GSM 7位编码,但如果运营商采用UCS2编码,需要改为8。
- 拼接的AT指令缺少双引号包裹PDU内容,正确格式应为
响应处理逻辑不合理:
ExecAndReceiveMultiple适合处理指令执行后直接返回多条响应的场景,但USSD响应是调制解调器主动上报的消息,并非指令执行后的即时返回,用这个方法等待无法捕获到目标响应。
可能的超时原因
除代码问题外,还有这些常见诱因:
- 调制解调器配置缺失:未提前设置字符集(比如发送
AT+CSCS="GSM"确保编码匹配),或未关闭之前的USSD会话(发送AT+CUSD=0)。 - 串口参数不匹配:即使连接正常,波特率、奇偶校验等参数与调制解调器不一致,也会导致数据接收失败。
- 运营商响应特性:部分运营商的USSD响应延迟较高,或需要特定的指令前缀/后缀才能触发响应。
修复后的代码示例
这里提供调整后的版本,修正正则、指令格式和响应监听逻辑:
public string SendUssdRequestFixed(string request) { string responseMsg = ""; comm = ConnectAndGetComm(); try { IProtocol protocol = comm.GetProtocol(); // 先设置字符集为GSM,确保编码匹配 protocol.ExecAndReceive("AT+CSCS=\"GSM\""); // 正确编码并拼接USSD指令,添加双引号包裹PDU string ussdPdu = TextDataConverter.StringTo7Bit(request); int octets = TextDataConverter.SeptetsToOctetsInt(ussdPdu); string hexPdu = Calc.IntToHex(octets); string atCommand = $"AT+CUSD=1,\"{hexPdu}\",15"; // 发送指令后不等待即时返回,改为监听主动上报的响应 protocol.Send(atCommand); // 循环监听响应,每次等待5秒,最多尝试10次 int retryCount = 0; string receivedData; var ussdRegex = new Regex(@"\+CUSD:\s*1,""(.*?)""", RegexOptions.Singleline); do { if (protocol.Receive(out receivedData, 5000)) { Match match = ussdRegex.Match(receivedData); if (match.Success) { string encodedBalance = match.Groups[1].Value; responseMsg = PduParts.Decode7BitText(Calc.HexToInt(encodedBalance)); break; } else if (receivedData.Contains("\r\nERROR")) { responseMsg = "USSD指令执行失败"; break; } } retryCount++; } while (retryCount < 10); if (string.IsNullOrEmpty(responseMsg)) { responseMsg = "未收到USSD响应"; } } catch (Exception ex) { responseMsg = $"错误:{ex.Message}"; } finally { comm.ReleaseProtocol(); } return responseMsg; }
替代库与实现方案
如果GSMComm库兼容性问题难以解决,可以尝试这些方案:
- 直接使用
System.IO.Ports.SerialPort:手动控制串口发送AT指令、逐行监听响应,灵活性更高,适合调试和定制化需求,能直接排查指令和响应的细节问题。 - GsmCommunication.NET:这是GSMComm的更新分支,针对.NET Framework和.NET Core做了优化,支持更多现代调制解调器。
- 串口调试工具前置测试:先用PuTTY等串口助手手动发送AT指令测试USSD流程,确认指令格式和响应格式后再编写代码,快速排除硬件或运营商侧的问题。
内容的提问来源于stack exchange,提问作者Shafikul Islam
相关产品推荐
相关产品推荐

