2七、經過visual s'tudio 驗證 SOCKET編程:搭建一個TCP服務器 vSocket模型詳解及select應用詳解

 

 

 

 

   本文就是在windows下進行socket編程,搭建一個TCP客戶端。html

  在visual studio下編程,首先在windows下進行初始化(這點在linux下是不須要的):linux

/*
初始化 Winsock
*/

#include<stdio.h>
#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") 

int main(int argc, char *argv[])
{
    WSADATA wsa;

    printf("\n初始化中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的環境已經搭建完成了。windows

下面就是建立一個套接字:服務器

/*
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("\n初始化Initialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("失敗Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }

    printf("Initialised.\n");


    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.\n");

    return 0;
}

 

好的,此時咱們已經成功地建立了一個套接字。網絡

 

 下面就是經過bind綁定套接字:app

#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("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }

    printf("Initialised.\n");

    //Create a socket
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
    }

    printf("Socket created.\n");

    //配置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;
}

 

進行調試編譯,結果以下:socket

如今,綁定完成了,它的時間讓套接字監聽鏈接。咱們將套接字綁定到特定的IP地址和特定的端口號。經過這樣作,咱們確保全部到這個端口號的傳入數據都被這個應用程序接收。函數

下面進行listen對端口監聽,而後accept接收穫得的數據。post

/*
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("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }

    printf("Initialised.\n");

    //Create a socket
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
    }

    printf("Socket created.\n");

    //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("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }

    printf("Initialised.\n");

    //Create a socket
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
    }

    printf("Socket created.\n");

    //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\n";
    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 \r\n";

    //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("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        exit(EXIT_FAILURE);
    }

    printf("Initialised.\n");

    //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.\n");

    //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 \n", 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 \n", 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 \n", 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 \n", 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] = '\0';
                    printf("%s:%d - %s \n", inet_ntoa(address.sin_addr), ntohs(address.sin_port), buffer);
                    send(s, buffer, valread, 0);
                }
            }
        }
    }

    closesocket(s);
    WSACleanup();

    return 0;
}

運行,程序,打開仿真軟件配置後點擊鏈接,能夠看到輸入以下

輸入數據點擊發送後能夠看到,輸出以下:

至此,一個功能基本完備的服務器已經搭建完成了,可是其實這個服務器仍是不完善的,這個將在下片文章中進行說明。

下篇文章:

 

vSocket模型詳解及select應用詳解

相關文章
相關標籤/搜索