1、需求
本文限制比较大,大家可以稍微看下。现场项目是对接webservice,为了方便以后的测试想在本地搭建一个webservice测试服务。
2、实现方法
TCP服务。大家要知道无论是HTTP或者是webservice,底层都是tcp,这个实现方法比较暴力,只适用测试;
工具:Wireshark
3、例子
这里我们用天气预报做个示范。
主要看两个节点:
上面是发送post请求,下面是回执结果。这里也可以看出测试webservice其实也可以用postman实现,只是其中的一些规范定义需要注意。
发送内容如下:
根据这个发送内容我们其实就可以组postman的发送参数了,大家有需求可以自测下;
接收内容:
Frame 69: 374 bytes on wire (2992 bits), 374 bytes captured (2992 bits) on interface \Device\NPF_{C7F126CF-73B5-4695-8ECD-B75C44B9E500}, id 0
Ethernet II, Src: ASUSTekC_cd:ca:00 (0c:9d:92:cd:ca:00), Dst: IntelCor_c2:08:17 (28:c6:3f:c2:08:17)
Internet Protocol Version 4, Src: 61.147.124.120, Dst:xxxx
Transmission Control Protocol, Src Port: 80, Dst Port: 6547, Seq: 1401, Ack: 1788, Len: 320
[2 Reassembled TCP Segments (1720 bytes): #68(1400), #69(320)]
Hypertext Transfer Protocol
HTTP/1.1 200 OK\r\n
Cache-Control: private, max-age=0\r\n
Content-Type: text/xml; charset=utf-8\r\n
Content-Encoding: gzip\r\n
Vary: Accept-Encoding\r\n
Server: Microsoft-IIS/7.5\r\n
X-AspNet-Version: 2.0.50727\r\n
X-Powered-By: ASP.NET\r\n
Date: Mon, 20 Feb 2023 02:22:17 GMT\r\n
Content-Length: 1442\r\n
\r\n
[HTTP response 1/1]
[Time since request: 0.008501000 seconds]
[Request in frame: 66]
[Request URI: http://www.webxml.com.cn/WebServices/WeatherWS.asmx/getRegionCountry]
Content-encoded entity body (gzip): 1442 bytes -> 2947 bytes
File Data: 2947 bytes
eXtensible Markup Language
<?xml
version="1.0"
encoding="utf-8"
?>
<ArrayOfString
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://WebXml.com.cn/">
<string>
阿尔及利亚,3320
</string>
<string>
阿根廷,3522
</string>
<string>
阿曼,3170
</string>
<string>
阿塞拜疆,3176
</string>
<string>
埃及,3317
</string>
<string>
埃塞俄比亚,3314
</string>
<string>
爱尔兰,3246
</string>
<string>
奥地利,3237
</string>
<string>
澳大利亚,368
</string>
<string>
巴基斯坦,3169
</string>
<string>
巴西,3580
</string>
<string>
保加利亚,3232
</string>
<string>
比利时,3243
</string>
<string>
波兰,3235
</string>
<string>
朝鲜,3163
</string>
<string>
丹麦,3245
</string>
<string>
德国,3238
</string>
<string>
俄罗斯,3225
</string>
<string>
法国,3241
</string>
<string>
菲律宾,3151
</string>
<string>
芬兰,3248
</string>
<string>
刚果(金),3377
</string>
<string>
哥伦比亚,3524
</string>
<string>
古巴,344
</string>
<string>
韩国,3162
</string>
<string>
荷兰,3244
</string>
<string>
加拿大,347
</string>
<string>
加纳,3313
</string>
<string>
柬埔寨,3154
</string>
<string>
捷克,3236
</string>
<string>
克罗地亚,3233
</string>
<string>
肯尼亚,3316
</string>
<string>
拉托维亚,3228
</string>
<string>
老挝,3155
</string>
<string>
立陶宛,3227
</string>
<string>
马达加斯加,3312
</string>
<string>
马尔代夫,3166
</string>
<string>
马来西亚,3158
</string>
<string>
马里,3319
</string>
<string>
美国,346
</string>
<string>
蒙古,3165
</string>
<string>
秘鲁,3578
</string>
<string>
缅甸,3160
</string>
<string>
莫桑比克,3311
</string>
<string>
墨西哥,345
</string>
<string>
南非,3310
</string>
<string>
尼泊尔,3164
</string>
<string>
尼日利亚,3379
</string>
<string>
挪威,3250
</string>
<string>
葡萄牙,3239
</string>
<string>
日本,3161
</string>
<string>
瑞典,3249
</string>
<string>
瑞士,3242
</string>
<string>
塞内加尔,3318
</string>
<string>
沙特阿拉伯,3172
</string>
<string>
斯里兰卡,3167
</string>
<string>
泰国,3159
</string>
<string>
坦桑尼亚,3315
</string>
<string>
突尼斯,3321
</string>
<string>
土耳其,3229
</string>
<string>
委内瑞拉,342
</string>
<string>
文莱,3153
</string>
<string>
乌克兰,3226
</string>
<string>
乌兹别克斯坦,3175
</string>
<string>
西班牙,3240
</string>
<string>
希腊,3230
</string>
<string>
新加坡,3157
</string>
<string>
新西兰,369
</string>
<string>
匈牙利,3234
</string>
<string>
叙利亚,3174
</string>
<string>
牙买加,343
</string>
<string>
伊朗,3171
</string>
<string>
意大利,3231
</string>
<string>
印度,3168
</string>
<string>
印度尼西亚,3152
</string>
<string>
英国,3247
</string>
<string>
约旦,3173
</string>
<string>
越南,3156
</string>
<string>
智利,3523
</string>
</ArrayOfString>
这里稍微解释下soap协议,SOAP协议可以简单地理解为:
SOAP=RPC+HTTP+XML,即采用HTTP作为通信协议,RPC(Remote Procedure Call Protocol 远程过程调用协议)作为一致性的调用途径,
XML作为数据传送的格式,从而允许服务提供者和服务客户经过防火墙在Internet上进行通信交互。
测试服务实现
//server.cpp
#include<iostream>
#include<winsock.h> //windows平台的网络库头文件
#pragma comment(lib,"ws2_32.lib") //库文件
#define PORT 5566
#define BUFSIZ 4096
int main()
{
//初始化套接字库
//WSA windows socket async windows异步套接字
//WSAStartup启动套接字 参数形式:WORD WSADATA
//parm1:请求的socket版本 2.2 2.1 1.0
//parm2:传出参数
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
//成功:WSAStartup函数返回零
if (WSAStartup(w_req, &wsadata) != 0) {
std::cout << "WSAStartup failed" << std::endl;
return -1;
} else {
std::cout << "WSAStartup succeed" << std::endl;
}
//1.创建空的Socket
//parm1:af 地址协议族 ipv4 ipv6
//parm2:type 传输协议类型 流式套接字(SOCK_STREAM) 数据报
//parm3protocl 使用具体的某个传输协议
SOCKET s_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s_server == INVALID_SOCKET)
{
std::cout << "Create socket failed" << std::endl;
WSACleanup();
return -1;
} else {
std::cout << "Create socket succeed" << std::endl;
}
//2.给socket绑定ip地址和端口号
struct sockaddr_in server_addr; //sockaddr_in, sockaddr 老版本和新版的区别
server_addr.sin_family = AF_INET; //和创建socket时必须一样
server_addr.sin_port = htons(PORT); //端口号 大端(高位)存储(本地)和小端(低位)存储(网络),两个存储顺序是反着的 htons 将本地字节序转为网络字节序
server_addr.sin_addr.s_addr = INADDR_ANY;
//3.绑定
if (bind(s_server, (PSOCKADDR)&server_addr, sizeof(sockaddr_in)) == SOCKET_ERROR) {
std::cout << "Bind failed" << std::endl;
WSACleanup();
return -1;
} else {
std::cout << "Bind succeed" << std::endl;
}
//4.设置套接字为监听状态 SOMAXCONN 监听的端口数
if (listen(s_server, SOMAXCONN) < 0) {
std::cout << "Listen failed" << std::endl;
WSACleanup();
return -1;
} else {
std::cout << "Listen succeed" << std::endl;
}
SOCKET s_client;
std::cout << "Wait client connect..." << std::endl;
struct sockaddr_in client_addr;
int addr_len = sizeof (sockaddr_in);
s_client = accept(s_server, (SOCKADDR*)&client_addr, &addr_len);
if (s_client == INVALID_SOCKET) {
std::cout << "Accept failed" << std::endl;
WSACleanup();
return -1;
}
char buf[BUFSIZ];
ZeroMemory(buf, BUFSIZ);
while (true) {
int ret = recv(s_client, buf, BUFSIZ, 0);
if (ret > 0){
//这里没做解析有需求可以在这里解析
std::cout << "Recv from client: " << buf << std::endl;
} else if (ret == 0) {
std::cout << "s_client closed" << std::endl;
closesocket(s_server);
WSACleanup();
return -1;
} else {
std::cout << "Recv failed: " << GetLastError() << std::endl;
closesocket(s_server);
closesocket(s_client);
WSACleanup();
return -1;
}
//直接组的固定数据,有需求可以变动
//tcp实现webservice主要就是回复的数据,满足他需要的soap协议就行
string strSendDat="";
strSendDat = "HTTP/1.0 200 OK\r\n";
strSendDat += "Cache-Control: private, max-age=0\r\n";
strSendDat += "Content-Type: text/xml; charset=utf-8\r\n";
strSendDat += "Content-Encoding: gzip\r\n";
strSendDat += "Vary: Accept-Encoding\r\n";
strSendDat += "Server: Microsoft-IIS/7.5\r\n";
strSendDat += "X-AspNet-Version: 2.0.50727\r\n";
strSendDat += "X-Powered-By: ASP.NET\r\n";
strSendDat += "Date: Mon, 20 Feb 2023 02:22:17 GMT\r\n";
strSendDat += "Content-Length:";
//回复xml信息
string strXML = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
strXML += "<ArrayOfString xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" \
xmlns=\"http://WebXml.com.cn/\">";
strXML += "<string>\
阿尔及利亚,3320\
</string>\
<string>\
阿根廷,3522\
</string>"
// ...... 太多不写齐了
strXML += "</ArrayOfString>";
long ldalen = strlen(strXML.c_str());
char szTem[32];
sprintf(szTem,"%d \r\n\r\n",ldalen);
strSendDat += szTem;
strSendDat += xmldata;
ret = send(s_client, strSendDat.c_str(), strlen(strSendDat.c_str()), 0);
if (SOCKET_ERROR != ret) {
std::cout << "Send to client: " << buf << std::endl;
} else {
std::cout << "Send failed: " << GetLastError() << std::endl;
closesocket(s_server);
closesocket(s_client);
WSACleanup();
return -1;
}
}
//关闭套接字
closesocket(s_server);
closesocket(s_client);
//释放DLL资源
WSACleanup();
return 0;
}
这只是一个简单的测试,如果需要实现服务可以创建线程将接收拎出来。