由于一些原因没法用需要安装的TCP/UDP测试工具,又懒得去找绿色版,所以我干脆弄了一个简单的检测收发的c的服务端程序,仅支持一轮的收发过程(可自行依据需求扩展)。
注意:仅支持一轮消息的收发
注意:这是在windows下的,linux之类的环境的话会有些不一样(需要修改部分内容)
注意:代码中大量的注释是为了更方便调整,免得还要找一次资料
注意:直接拷贝就能使用,不需要再加什么包或配置(注意代码中设置的端口)
注意:只是随手搞得,也只是一个临时的工具,代码不是很精妙
#include <stdio.h>
#include <winsock2.h>
#include <Ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main() {
WSADATA wsaData; //用于填充套接字库版本的有关信息
//加载Winsock 2.2版本
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("WSAStartup failed");
return -1;
}
/*
int socket(int domain, int type, int protocol);
在参数表中,domain指定使用何种的地址类型,比较常用的有:
PF_INET, AF_INET: Ipv4网络协议;
PF_INET6, AF_INET6: Ipv6网络协议。
type参数的作用是设置通信的协议类型,可能的取值如下所示:
SOCK_STREAM: 提供面向连接的稳定数据传输,即TCP协议。
OOB: 在所有数据传送前必须使用connect()来建立连接状态。
SOCK_DGRAM: 使用不连续不可靠的数据包连接。
SOCK_SEQPACKET: 提供连续可靠的数据包连接。
SOCK_RAW: 提供原始网络协议存取。
SOCK_RDM: 提供可靠的数据包连接。
SOCK_PACKET: 与网络驱动程序直接通信。
参数protocol用来指定socket所使用的传输协议编号。这一参数通常不具体设置,一般设置为0即可。
#define IPPROTO_IP 0 dummy for IP(IP虚拟机)
#define IPPROTO_ICMP 1 control message protocol(控制消息协议)
#define IPPROTO_IGMP 2 internet group management protocol (internet组管理协议)
#define IPPROTO_GGP 3 gateway^2 (deprecated)(网关^2(已弃用))
#define IPPROTO_TCP 6 tcp
#define IPPROTO_PUP 12 pup
#define IPPROTO_UDP 17 user datagram protocol(用户数据报协议)
#define IPPROTO_IDP 22 xns idp
#define IPPROTO_ND 77 UNOFFICIAL net disk proto(非官方网络磁盘协议)
#define IPPROTO_RAW 255 raw IP packet(原始的IP包)
#define IPPROTO_MAX 256
*/
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 创建套接字
if (sockfd == INVALID_SOCKET) {
perror("socket");
return -1;
} // 创建失败的错误处理
printf("socket..............\n"); // 成功则打印“socket。。。。”
struct sockaddr_in myaddr; // 创建“我的地址”结构体
memset(&myaddr, 0, sizeof(myaddr)); // 对内存清零(保险起见)
myaddr.sin_family = AF_INET; // 选择IPV4地址类型
myaddr.sin_port = htons(7000); // 选择端口号
myaddr.sin_addr.s_addr = htonl(2130706433); // 选择IP地址
if (bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)) == SOCKET_ERROR) { // 绑定套接字
perror("bind");
return -1;
}
printf("bind..........\n");
if (listen(sockfd, 8) == SOCKET_ERROR) { // 调用listen对指定端口进行监听
perror("listen");
return -1;
}
printf("listen............\n");
//循环接收数据
SOCKET sClient;
struct sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
printf("waiting link...\n");
sClient = accept(sockfd, (SOCKADDR*)& remoteAddr, &nAddrlen);
while (1) {
if (sClient == INVALID_SOCKET) {
printf("accept error !");
break;
}
char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &remoteAddr.sin_addr, str, sizeof(str));
printf("receive a link:%s \n", str);
//接收数据
/*
函数原型:int recv( SOCKET s, char *buf, int len, int flags )
功能:不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。
参数一:指定接收端套接字描述符;
参数二:指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
参数三:指明buf的长度;
参数四 :一般置为0。
*/
char revData[256];
int ret = recv(sClient, revData, 255, 0);
if (ret <= 0) {
printf("%d \n", ret);
perror("recv");
printf("%d \n", WSAGetLastError());
// 10054错误表示连接被远程主机重置
// 所以为了c-s之间能稳定交互,要保证交互结束前不能结束程序的运行
break;
}
revData[ret] = 0x00;
printf("got data:\n");
printf("%s", revData);
//发送数据
printf("\nsend message to Client \n");
const char* sendData = "hello! client \n";
send(sClient, sendData, strlen(sendData), 0);
break;
}
Sleep(10000);
closesocket(sClient);
closesocket(sockfd);
WSACleanup();
printf("over \n");
system("pause");
return 0;
}
使用 Postman 测试如下: