yokila
yokila
Published on 2021-04-05 / 9 Visits
0
0

Windows下C利用Socket编写简易服务端程序

由于一些原因没法用需要安装的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 测试如下:

ee2358ab85f6f6a1104e374c45ab48c820204052.png


Comment