• 27、通过visual s'tudio 验证 SOCKET编程:搭建一个TCP服务器


       本文就是在windows下进行socket编程,搭建一个TCP客户端。

      在visual studio下编程,首先在windows下进行初始化(这点在linux下是不需要的):

    /*
    初始化 Winsock
    */
    
    #include<stdio.h>
    #include<winsock2.h>
    
    #pragma comment(lib,"ws2_32.lib") 
    
    int main(int argc, char *argv[])
    {
        WSADATA wsa;
    
        printf("
    初始化中Initialising Winsock...");
        if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)//Winsock 启动或初始化winsock库,第一个为加载的版本,第二个为WSADATA结构
        //WSAStartup应该与WSACleanup成对使用,WSAStartup的功能是初始化Winsock DLL,WSACleanup是来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
        {
            printf("Failed. Error Code : %d", WSAGetLastError());
            return 1;
        }
    
        printf("初始化成功Initialised.");
    
        return 0;
    }

    运行结果:

    可以看出winsock的环境已经搭建完成了。

    下面就是创建一个套接字:

    /*
    Create a TCP socket
    */
    
    #include<stdio.h>
    #include<winsock2.h>
    
    #pragma comment(lib,"ws2_32.lib") //Winsock Library
    
    int main(int argc, char *argv[])
    {
        WSADATA wsa;
        SOCKET s;
    
        printf("
    初始化Initialising Winsock...");
        if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        {
            printf("失败Failed. Error Code : %d", WSAGetLastError());
            return 1;
        }
    
        printf("Initialised.
    ");
    
    
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
            //函数socket()创建一个套接字并返回一个可用于其他网络命令的套接字描述符。
            /*
            地址系列:AF_INET(这是IP版本4)
            类型:SOCK_STREAM(这意味着面向连接的TCP协议)
            协议:0 [或IPPROTO_TCP,IPPROTO_UDP]
            */
        {
            printf("创建失败Could not create socket : %d", WSAGetLastError());
        }
    
        printf("成功Socket created.
    ");
    
        return 0;
    }

     

    好的,此时我们已经成功地创建了一个套接字。

     下面就是通过bind绑定套接字:

    #include<stdio.h>
    #include<winsock2.h>
    
    #pragma comment(lib,"ws2_32.lib") //Winsock Library
    
    int main(int argc, char *argv[])
    {
        WSADATA wsa;
        SOCKET s;
        struct sockaddr_in server;
    
        printf("
    Initialising Winsock...");
        if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        {
            printf("Failed. Error Code : %d", WSAGetLastError());
            return 1;
        }
    
        printf("Initialised.
    ");
    
        //Create a socket
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {
            printf("Could not create socket : %d", WSAGetLastError());
        }
    
        printf("Socket created.
    ");
    
        //配置sockaddr_in 结构体
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(8888);
    
        //Bind
        if (bind(s, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
        {
            printf("Bind failed with error code : %d", WSAGetLastError());
        }
    
        puts("Bind done");
    
        closesocket(s);
    
        return 0;
    }

    进行调试编译,结果如下:

    现在,绑定完成了,它的时间让套接字监听连接。我们将套接字绑定到特定的IP地址和特定的端口号。通过这样做,我们确保所有到这个端口号的传入数据都被这个应用程序接收。

    下面进行listen对端口监听,然后accept接收得到的数据。

    /*
    Bind socket to port 8888 on localhost
    */
    
    #include<stdio.h>
    #include<winsock2.h>
    
    #pragma comment(lib,"ws2_32.lib") //Winsock Library
    
    int main(int argc, char *argv[])
    {
        WSADATA wsa;
        SOCKET s, new_socket;
        struct sockaddr_in server, client;
        int c;
    
        printf("
    Initialising Winsock...");
        if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        {
            printf("Failed. Error Code : %d", WSAGetLastError());
            return 1;
        }
    
        printf("Initialised.
    ");
    
        //Create a socket
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {
            printf("Could not create socket : %d", WSAGetLastError());
        }
    
        printf("Socket created.
    ");
    
        //Prepare the sockaddr_in structure
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(8888);
    
        //Bind
        if (bind(s, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
        {
            printf("Bind failed with error code : %d", WSAGetLastError());
        }
    
        puts("Bind done");
    
    
        //Listen to incoming connections
        if (listen(s, 3) != 0)
        {
            printf("listen is error");
        }
    
        //Accept and incoming connection
        puts("Waiting for incoming connections...");
    
        c = sizeof(struct sockaddr_in);
        new_socket = accept(s, (struct sockaddr *)&client, &c);
        if (new_socket == INVALID_SOCKET)
        {
            printf("accept failed with error code : %d", WSAGetLastError());
        }
    
        puts("Connection accepted");
    
        closesocket(s);
        WSACleanup();
    
        return 0;
    }

     此时,一个客户端就搭建完成了,下面通过软件对客户端进行实验验证。

    创建一个客户端

     

    创建完成后运行称程序,点击连接按钮,可以看到如下输出

    上面我们接受了一个传入的连接,但立即关闭。这不是很有成效。传入连接建立后,可以做很多事情。毕竟连接是为了沟通的目的而建立的。所以让我们用send函数回复客户。

    /*
    Bind socket to port 8888 on localhost
    */
    #include<io.h>
    #include<stdio.h>
    #include<winsock2.h>
    
    #pragma comment(lib,"ws2_32.lib") //Winsock Library
    
    int main(int argc, char *argv[])
    {
        WSADATA wsa;
        SOCKET s, new_socket;
        struct sockaddr_in server, client;
        int c;
        char *message;
    
        printf("
    Initialising Winsock...");
        if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        {
            printf("Failed. Error Code : %d", WSAGetLastError());
            return 1;
        }
    
        printf("Initialised.
    ");
    
        //Create a socket
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {
            printf("Could not create socket : %d", WSAGetLastError());
        }
    
        printf("Socket created.
    ");
    
        //Prepare the sockaddr_in structure
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(8888);
    
        //Bind
        if (bind(s, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
        {
            printf("Bind failed with error code : %d", WSAGetLastError());
        }
    
        puts("Bind done");
    
        //Listen to incoming connections
        listen(s, 3);
    
        //Accept and incoming connection
        puts("Waiting for incoming connections...");
    
        c = sizeof(struct sockaddr_in);
        new_socket = accept(s, (struct sockaddr *)&client, &c);
        if (new_socket == INVALID_SOCKET)
        {
            printf("accept failed with error code : %d", WSAGetLastError());
        }
    
        puts("Connection accepted");
    
        //Reply to client
        message = "Hello Client , I have received your connection. But I have to go now, bye
    ";
        send(new_socket, message, strlen(message), 0);
    
        getchar();
    
        closesocket(s);
        WSACleanup();
    
        return 0;
    }

    同样运行程序,然后点击连接,可以看到调试软件有如下的反馈输出:

    虽然有反馈了但是对客户端发送过来的数据没有recv进行显示处理,這里,将整个程序优化如下:

    /*
    TCP Echo server example in winsock
    Live Server on port 8888
    */
    #include<stdio.h>
    #include<winsock2.h>
    
    #pragma comment(lib, "ws2_32.lib") //Winsock Library
    
    int main(int argc, char *argv[])
    {
        WSADATA wsa;
        SOCKET master, new_socket, client_socket[30], s;
        struct sockaddr_in server, address;
        int max_clients = 30, activity, addrlen, i, valread;
        char *message = "ECHO Daemon v1.0 
    ";
    
        //size of our receive buffer, this is string length.
        int MAXRECV = 1024;
        //set of socket descriptors
        fd_set readfds;
        //1 extra for null character, string termination
        char *buffer;
        buffer = (char*)malloc((MAXRECV + 1) * sizeof(char));
    
        for (i = 0; i < 30; i++)
        {
            client_socket[i] = 0;
        }
    
        printf("
    Initialising Winsock...");
        if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        {
            printf("Failed. Error Code : %d", WSAGetLastError());
            exit(EXIT_FAILURE);
        }
    
        printf("Initialised.
    ");
    
        //Create a socket
        if ((master = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {
            printf("Could not create socket : %d", WSAGetLastError());
            exit(EXIT_FAILURE);
        }
    
        printf("Socket created.
    ");
    
        //Prepare the sockaddr_in structure
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(8888);
    
        //Bind
        if (bind(master, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
        {
            printf("Bind failed with error code : %d", WSAGetLastError());
            exit(EXIT_FAILURE);
        }
    
        puts("Bind done");
    
        //Listen to incoming connections
        listen(master, 3);
    
        //Accept and incoming connection
        puts("Waiting for incoming connections...");
    
        addrlen = sizeof(struct sockaddr_in);
    
        while (TRUE)
        {
            //clear the socket fd set
            FD_ZERO(&readfds);
    
            //add master socket to fd set
            FD_SET(master, &readfds);
    
            //add child sockets to fd set
            for (i = 0; i < max_clients; i++)
            {
                s = client_socket[i];
                if (s > 0)
                {
                    FD_SET(s, &readfds);
                }
            }
    
            //wait for an activity on any of the sockets, timeout is NULL , so wait indefinitely
            activity = select(0, &readfds, NULL, NULL, NULL);
    
            if (activity == SOCKET_ERROR)
            {
                printf("select call failed with error code : %d", WSAGetLastError());
                exit(EXIT_FAILURE);
            }
    
            //If something happened on the master socket , then its an incoming connection
            if (FD_ISSET(master, &readfds))
            {
                if ((new_socket = accept(master, (struct sockaddr *)&address, (int *)&addrlen))<0)
                {
                    perror("accept");
                    exit(EXIT_FAILURE);
                }
    
                //inform user of socket number - used in send and receive commands
                printf("New connection , socket fd is %d , ip is : %s , port : %d 
    ", new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
    
                //send new connection greeting message
                if (send(new_socket, message, strlen(message), 0) != strlen(message))
                {
                    perror("send failed");
                }
    
                puts("Welcome message sent successfully");
    
                //add new socket to array of sockets
                for (i = 0; i < max_clients; i++)
                {
                    if (client_socket[i] == 0)
                    {
                        client_socket[i] = new_socket;
                        printf("Adding to list of sockets at index %d 
    ", i);
                        break;
                    }
                }
            }
    
            //else its some IO operation on some other socket :)
            for (i = 0; i < max_clients; i++)
            {
                s = client_socket[i];
                //if client presend in read sockets            
                if (FD_ISSET(s, &readfds))
                {
                    //get details of the client
                    getpeername(s, (struct sockaddr*)&address, (int*)&addrlen);
    
                    //Check if it was for closing , and also read the incoming message
                    //recv does not place a null terminator at the end of the string (whilst printf %s assumes there is one).
                    valread = recv(s, buffer, MAXRECV, 0);
    
                    if (valread == SOCKET_ERROR)
                    {
                        int error_code = WSAGetLastError();
                        if (error_code == WSAECONNRESET)
                        {
                            //Somebody disconnected , get his details and print
                            printf("Host disconnected unexpectedly , ip %s , port %d 
    ", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
    
                            //Close the socket and mark as 0 in list for reuse
                            closesocket(s);
                            client_socket[i] = 0;
                        }
                        else
                        {
                            printf("recv failed with error code : %d", error_code);
                        }
                    }
                    if (valread == 0)
                    {
                        //Somebody disconnected , get his details and print
                        printf("Host disconnected , ip %s , port %d 
    ", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
    
                        //Close the socket and mark as 0 in list for reuse
                        closesocket(s);
                        client_socket[i] = 0;
                    }
    
                    //Echo back the message that came in
                    else
                    {
                        //add null character, if you want to use with printf/puts or other string handling functions
                        buffer[valread] = '';
                        printf("%s:%d - %s 
    ", inet_ntoa(address.sin_addr), ntohs(address.sin_port), buffer);
                        send(s, buffer, valread, 0);
                    }
                }
            }
        }
    
        closesocket(s);
        WSACleanup();
    
        return 0;
    }

    运行,程序,打开仿真软件配置后点击连接,可以看到输入如下

    输入数据点击发送后可以看到,输出如下:

    至此,一个功能基本完备的服务器已经搭建完成了,但是其实这个服务器还是不完善的,这个将在下片文章中进行说明。

    下篇文章:

     

    vSocket模型详解及select应用详解

  • 相关阅读:
    《Asp.Net Forums2.0深入分析》之 Asp.Net Forums是如何实现代码分离和换皮肤的
    Community Server专题五:IHttpHandlerFactory
    自定义 HttpModule 示例
    动态加入控件的方法
    JS应用DOM入门:DOM的对象属性
    JS应用DOM入门:简单文档DOM结构分析
    httpmodule专题(2)
    Java 算法之快速排序
    HTML与.jsp的融合
    Exception
  • 原文地址:https://www.cnblogs.com/noticeable/p/7850692.html
Copyright © 2020-2023  润新知