tcp

  1. tcp 设计的初衷是 可靠的数据传输
  2. 数据包发送后 tcp会确定数据是否正确被接收
  3. 如果 数据包在 中途丢失 那么tcp协议会重新发送丢失的数据包
  4. 这一切操作对程序来说是透明的
  5. 一旦tcp连接成功 那么都可以相互 收发信息
  6. 我们一般将 发起连接的 终端成为 客户端
  7. 小心 tcp句柄失效,作为长时间的可靠服务运行时 一定要对tcp句柄进行检查

tcp 服务器/接收端

  1. 作为服务器 一定要有地址 可以让 客户端直接发送请求(服务器的ip 要在客户端之上)
  2. 一个tcp服务器可以同时与多个客户端通信
  3. 每个与服务器建立链接的客户端都有独立的句柄 可以 收发信息

实例

// server.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <Winsock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

/*
windows专属的 初始化api
使用Winsock 之前一定要运行这段代码
*/
void initial_win_socket()
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0)
    {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        return;
    }

}


//回掉函数的函数指针
typedef void(*p_socket_callback)(SOCKET, sockaddr_in);

//每当建立一个链接就调用这个函数
void socket_callback(SOCKET in_sock, sockaddr_in recvName) {

    OutputDebugStringA("我是回掉函数\r\n");


    //接收数据
    char data_rec[512] = { 0 };
    char ip_sender[128] = { 0 };

    inet_ntop(AF_INET, &(recvName.sin_addr), ip_sender, 16); //获取客户端的ip
    recv(in_sock, data_rec, sizeof(data_rec), 0);            //获取接收到的数据
    
    printf("receive from %s:%d : %s\r\n", ip_sender, ntohs(recvName.sin_port), data_rec);

    //向客户端发送数据
    char str_to_clinet[] = "这是一条服务器回复的数据";
    int nRet = send(in_sock, str_to_clinet,sizeof(str_to_clinet),0);
    
    closesocket(in_sock);
}


/*
输入  ip端口 开始绑定
输入  回掉函数,每建立一个socks链接就会调用
最后  别忘记 在回掉函数中关闭 socket接口
closesocket(s);

*/
void bind_port(const char * bind_ip, int bind_port, p_socket_callback fx_cb = NULL)
{
    //1 建立SOCKET(套接字)
    SOCKET s = socket(AF_INET,
                      SOCK_STREAM,//数据流形式
                      IPPROTO_TCP);
    if (s == INVALID_SOCKET)
    {
        return;
    }
    int nRet;

    //2 配置监听端口的结构体
    sockaddr_in name;
    name.sin_family = AF_INET;
    name.sin_port = htons(bind_port);
    inet_pton(AF_INET, bind_ip, &(name.sin_addr.S_un.S_addr));//将ip地址转化为 api使用的比特流形式

    // 3 将socket 绑定到端口
    nRet = bind(s,
        (struct sockaddr*)&name,
                sizeof(name));

    if (nRet == SOCKET_ERROR)
    {
        return;
    }
    //4 开始监听
    nRet = listen(s, SOMAXCONN/* 设置socket队列最大数量*/);
    if (nRet == SOCKET_ERROR)
    {
        return;
    }

    //5 等待新的链接建立
    sockaddr_in recvName = { 0 };
    recvName.sin_family = AF_INET;
    int nLength = sizeof(sockaddr_in);

    while (1) {
        SOCKET sClient = accept(s, (sockaddr*)&recvName, &nLength);//等待链接

        if (sClient == INVALID_SOCKET) {
            closesocket(sClient);
            continue;
        }

        //如果 传入了 回掉函数就调用回掉函数
        if (fx_cb != NULL) {
            fx_cb(sClient, recvName);
        }
    }

    closesocket(s);
}
int main()
{
    initial_win_socket();
    bind_port("127.0.0.1", 10086, socket_callback);
}

tcp 客户端/发送端

实例

// clinet.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <Winsock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

/*
windows专属的 初始化api
使用Winsock 之前一定要运行这段代码
*/
void initial_win_socket()
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if(err != 0)
    {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        return;
    }

}

/*
向指定 服务器:端口 建立tcp链接
返回 建立的 SOCKET 链接
最后  别忘记 关闭 socket接口
*/
SOCKET connect_to_server(const char * dst_ip, int port)
{

    //1 建立SOCKET(套接字)
    SOCKET sClient = socket(AF_INET,
                            SOCK_STREAM,//数据流, 流式
                            IPPROTO_TCP);

    //2 配置 目标服务器的信息
    sockaddr_in name;
    name.sin_family = AF_INET;
    name.sin_port = htons(port);
    inet_pton(AF_INET, dst_ip, &(name.sin_addr.S_un.S_addr));//将ip地址转化为 api使用的比特流形式
    int nLength = sizeof(sockaddr_in);

    //3 建立链接
    int nRet = connect(sClient, (sockaddr*)&name, nLength);
    if(nRet == SOCKET_ERROR)
    {
        closesocket(sClient);
        return NULL;
    }

    //4 返回socket句柄
    return sClient;
    //closesocket(s);
}

int main()
{
    initial_win_socket();
    SOCKET my_socket = connect_to_server("127.0.0.1", 10086);

    //向服务器发送数据
    char str_to_server[] = "这是一条客户端发送的数据";
    int nRet = send(my_socket, str_to_server, sizeof(str_to_server), 0);

    //接收服务器的数据
    char data_rec[512] = { 0 };
    recv(my_socket, data_rec, sizeof(data_rec), 0);//获取接收到的数据
    printf("receive from server: %s\r\n", data_rec);

    system("pause");

}
Last modification:November 1, 2018
如果觉得我的文章对你有用,请随意赞赏