建立TCP的客户端和服务端(Windows)

  • Post author:
  • Post category:其他


一.建立TCP的服务端

1.加载库

2.创建监听套接字

3.绑定IP地址

4.监听

5.接受链接

6.接收数据

7.发送数据

8.关闭套接字,卸载库

#include<iostream>
#include<stdio.h>
#include<winsock2.h>
#include <Ws2tcpip.h> 
using namespace std;
#pragma comment(lib, "ws2_32.lib")
int main()
{
    //1.加载库
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
    {
        printf("The Winsock 2.2 dll was found okay\n");
    }
    //2.创建套接字(该套接字为监听套接字,主要为了监听客户端的链接请求的)
    SOCKET socklis = INVALID_SOCKET;
    socklis = socket(2, SOCK_STREAM, IPPROTO_TCP);//第一个参数af,有ipv4 ipv6//第二个参数type:SOCK_STREAM(tcp),SOCK_DGRAM(udp)
    //第三个参数使用的协议:protocol    IPPROTO_TCP    IPPROTO_UDP
    if (INVALID_SOCKET == socklis)
    {
        cout << "socket error" << WSAGetLastError() << endl;
        WSACleanup();
        return 1;
    }
    else
    {
        cout << "socket success" << endl;
    }
    //3.绑定IP地址    //htons将整形变量从主机字节序转换成网络字节序inet_addr()将Ip地址从字符串转换为u_long
    sockaddr_in soserver;
    soserver.sin_family = AF_INET;
    soserver.sin_addr.S_un.S_addr = INADDR_ANY;
    soserver.sin_port = htons(54321);
    err = bind(socklis, (SOCKADDR*)&soserver, sizeof(soserver));  //作为服务器要进行监听要绑定所有的IP地址inaddr_any
    if (err == 0)
    {
        cout << "bind success" << endl;
    }
    else if (SOCKET_ERROR == err)
    {
        cout << "bind err" << WSAGetLastError() << endl;
        closesocket(socklis);
        WSACleanup();
    }
    //4.监听。服务端要监听来自客户端的链接请求
    int errlis=listen(socklis, SOMAXCONN);
    if (errlis == 0)
    {
        cout << "listen success" << endl;
    }
    else
    {
        cout << "listen error" << WSAGetLastError() << endl;
        closesocket(socklis);
        WSACleanup();
        return 0;
    }
    sockaddr_in soclient;
    int size = sizeof(soclient);
    while (true)
    {
    	//5.接收链接。如果服务端接受了一个客户端的链接,那么将为其单独创建一个套接字与其通信
        SOCKET sock = accept(socklis, (SOCKADDR*)&soclient, &size);
        if (sock != INVALID_SOCKET)
        {
            cout << "accept success" << endl;
        }
        else
        {
            cout << "accept err" << WSAGetLastError() << endl;
            closesocket(socklis);
            WSACleanup();
            return 1;
        }
        int nrecvbuf = 0;
        char recvbuf[1025] = "";
        int nsenbuf = 0;
        char senbuf[1024] = "";
        while (true)
        {
        	//6.接收数据
            int a = recv(sock, recvbuf, sizeof(recvbuf), 0);
            if (a > 0)
            {
                printf("Bytes received: %d\n", a);
                cout << "客户端发送了:" << recvbuf << endl;
            }
            else if (a == 0)
                printf("Connection closed\n");
            else
                printf("recv failed: %d\n", WSAGetLastError());
            gets_s(senbuf);
            //7.发送数据
            int b = send(sock, senbuf, sizeof(senbuf), 0);
            if (b == SOCKET_ERROR) {
                printf("send failed: %d\n", WSAGetLastError());
                closesocket(sock);
                WSACleanup();
                return 1;
            }
        }
        closesocket(sock);
    }
    //8.关闭套接字和卸载库
    closesocket(socklis);
    WSACleanup();
    return 0;
}

关于缓冲区的问题:(下面说的是内核的缓冲区,而代码中的serbuf和clibuf是自己创建的缓冲区,为了方便调用以及显示内核缓冲区的内容)

每个

socket

被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。

TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,比如nagle算法,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。

read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。

二.TCP的客户端建立

1.加载库

2.创建套接字

3.链接

4.发送数据

5.接收数据

6.关闭套接字和卸载库

#include<iostream>
#include<stdio.h>
#include<winsock2.h>
#include <Ws2tcpip.h> 
using namespace std;
#pragma comment(lib, "ws2_32.lib")
int main()
{
    //1.加载库
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
    {
        printf("The Winsock 2.2 dll was found okay\n");
    }
    //2.创建套接字
    SOCKET sock = INVALID_SOCKET;
    sock = socket(2, SOCK_STREAM, IPPROTO_TCP);//第一个参数af,有ipv4 ipv6//第二个参数type:SOCK_STREAM(tcp),SOCK_DGRAM(udp)
    //第三个参数使用的协议:protocol    IPPROTO_TCP    IPPROTO_UDP
    if (INVALID_SOCKET == sock)
    {
        cout << "socket error" << WSAGetLastError() << endl;
        WSACleanup();
        return 1;
    }
    else
    {
        cout << "socket success" << endl;
    }
    sockaddr_in soserver;
    soserver.sin_family = AF_INET;
    soserver.sin_addr.S_un.S_addr = inet_addr("10.56.57.156");
    soserver.sin_port = htons(54321);
    char senbuf[1024] = "";
    char revbuf[1024] = "";
    //3.链接
    int a=connect(sock, (SOCKADDR*)&soserver, sizeof(soserver));
    if (a == 0)
    {
        cout << "connect success" << endl;
    }
    else 
    {
        cout << "connect error" << WSAGetLastError() << endl;
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    while (true)
    {
        //4.发送数据
        gets_s(senbuf);
        send(sock,senbuf,sizeof(senbuf),0);
        if (a == SOCKET_ERROR) {
            printf("send failed: %d\n", WSAGetLastError());
            closesocket(sock);
            WSACleanup();
            return 1;
        }
  
        //5.接收数据
        int b = recv(sock, revbuf, sizeof(revbuf), 0);
        if (b > 0)
        {
            printf("Bytes received: %d\n", b);
            cout << "服务器发送了:" << revbuf << endl;
        }
        else if (b == 0)
            printf("Connection closed\n");
        else
            printf("recv failed: %d\n", WSAGetLastError());
    }
    //6.关闭套接字和卸载库
    closesocket(sock);
    WSACleanup();
    return 0;
}



版权声明:本文为apple_51801179原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。