noticeable

博客园 首页 新随笔 联系 订阅 管理
  154 Posts :: 1 Stories :: 19 Comments :: 0 Trackbacks

 

 

 

 

   本文就是在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("\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的环境已经搭建完成了。

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

/*
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绑定套接字:

#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;
}

 

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

现在,绑定完成了,它的时间让套接字监听连接。我们将套接字绑定到特定的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("\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应用详解

posted on 2017-11-17 20:52  noticeable  阅读(...)  评论(... 编辑 收藏