c++多线程实现局域网内ip多播

c++基于ip多播实现网络会议

单播多播与广播

普通 IP 通信是在一个发送者和一个接收者之间进行的,我们常把它称为点对点的通信,我们可以称之为单播。

多播我们可以认为是一对多,但并不是在一个局域网里的所有对象发送通讯,而是有选择的,广播我们可以理解为一个人通过广播喇叭对在场的全体说话,这样做的好处是通话效率高,信息一下子就可以传递到全体。

多播较于单播广播的优势之处,首先我们如果相对一个局域网里的特定几个人进行信息发送,如果采用单播方式,逐个节点传输,有多少个目标节点,就会有多少次传送过程,这种方式显然效率极低,是不可取的;如果采用不区分目标、全部发送的广播方式,虽然一次可以传送完数据,但是显然达不到区分特定数据接收对象的目的。

思路

​ 首先为实现收发两个功能,需要分别创建两个project,分别进行收发:通过创建一个 SOCK_DGRAM 类型的 Socket,将此 Socket 绑定到本地的一个端口上,为了接收服务器端发送的多播数据,最后加入多播组,这样便可以接受局域网下的多播数据。这样在同一个局域网下我们可以分别实现收与发的功能。

​ 问题是两种功能并不可以同时进行。因此为实现收发同时进行的功能,特引入线程的概念,将收发数据这两个功能分别写入两个线程。首先在初始化套接字,接入多播组,两个线程里的函数便共享同一个套接字。这里需要说明的是,未引入线程的时候,二者不能同时执行的原因并不因为二者引用的端口冲突。

​ 实际上,收与发可以使用同一个端口,但是send函数以及recv函数是冲突的,不能同时执行,这也是引入线程的原因,通过引入子线程使得send函数以及recv函数可以同时执行,这样便实现了用户可以同时收发的功能。

winsock 版本初始化 建立套接字

 WSADATA wsd;
    struct sockaddr_in remote;
    TCHAR sendbuf[BUFSIZE];
    int len = sizeof(struct sockaddr_in);
    //初始化 WinSock2.2
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    {
        printf("WSAStartup() failed\n");
        return -1;
    }
    if ((sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0,
                          WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF |
                          WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
    {
        printf("socket failed with:%d\n", WSAGetLastError());
        WSACleanup();
        return -1;
    }
    //加入多播组
    remote.sin_family = AF_INET;
    remote.sin_port = htons(MCASTPORT);
    remote.sin_addr.s_addr = inet_addr(MCASTADDR);
    if ((sockM = WSAJoinLeaf(sock, (SOCKADDR *) &remote,
                             sizeof(remote), NULL, NULL, NULL, NULL,
                             JL_BOTH)) == INVALID_SOCKET)
    {
        printf("WSAJoinLeaf() failed:%d\n", WSAGetLastError());
        closesocket(sock);
        WSACleanup();
        return -1;
    }

send 线程

void* sendsocket(void* arg)//向服务端socket发送数据的线程
{
    int st = *(int*)arg;
    char s[1024];
    while (1)
    {
        memset(s, 0, sizeof(s));
        scanf("%s", s);
        if (sendto(sockM, (char *) s, strlen(s), 0, (struct sockaddr *)
                   &remote, sizeof(remote)) == SOCKET_ERROR)
        {
            printf("sendto failed with: %d\n", WSAGetLastError());
            closesocket(sockM);
            closesocket(sock);
            WSACleanup();
            break;
        }
       // if (strcmp(s, "QUIT") == 0)
       //     break;
        Sleep(500);
    }
    return NULL;
}

recv 线程

void* recvsocket(void* arg)//接受来着客户端数据的线程
{
    int st = *(int*)arg;
    char s[1024];
    while (1)
    {
        memset(s, 0, sizeof(s));
        int rc = recv(st, s, sizeof(s), 0);
        if (rc <= 0)//代表socket被关闭(0)或者出错(-1)
        {
            break;
        }
        printf("client receive:%s\n", s);
    }
    return NULL;
}

开启两个线程

    pthread_t thrd1, thrd2;//定义一个线程
    pthread_create(&thrd1, NULL, recvsocket, &sock);
    pthread_create(&thrd2, NULL, sendsocket, &sock);
    pthread_join(thrd1,NULL);
    pthread_join(thrd2,NULL);

完整代码

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define MCASTADDR "233.0.0.1" //本例使用的多播组地址。
#define MCASTPORT 5150 //本地端口号。
#define BUFSIZE 1024 //发送数据缓冲大小。
#pragma comment(lib, "ws2_32")
struct sockaddr_in remote;
SOCKET sock, sockM;
void* recvsocket(void* arg)//接受来着客户端数据的线程
{
    int st = *(int*)arg;
    char s[1024];
    while (1)
    {
        memset(s, 0, sizeof(s));
        int rc = recv(st, s, sizeof(s), 0);
        if (rc <= 0)//代表socket被关闭(0)或者出错(-1)
        {
            break;
        }
        printf("client receive:%s\n", s);
    }
    return NULL;
}
void* sendsocket(void* arg)//向服务端socket发送数据的线程
{
    int st = *(int*)arg;
    char s[1024];
    while (1)
    {
        memset(s, 0, sizeof(s));
        scanf("%s", s);
        if (sendto(sockM, (char *) s, strlen(s), 0, (struct sockaddr *)
                   &remote, sizeof(remote)) == SOCKET_ERROR)
        {
            printf("sendto failed with: %d\n", WSAGetLastError());
            closesocket(sockM);
            closesocket(sock);
            WSACleanup();
            break;
        }
       // if (strcmp(s, "QUIT") == 0)
       //     break;
        Sleep(500);
    }
    return NULL;
}
int main(int argc, char **argv)
{
    WSADATA wsd;
    struct sockaddr_in remote;
    TCHAR sendbuf[BUFSIZE];
    int len = sizeof(struct sockaddr_in);
    //初始化 WinSock2.2
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    {
        printf("WSAStartup() failed\n");
        return -1;
    }
    if ((sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0,
                          WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF |
                          WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
    {
        printf("socket failed with:%d\n", WSAGetLastError());
        WSACleanup();
        return -1;
    }
    //加入多播组
    remote.sin_family = AF_INET;
    remote.sin_port = htons(MCASTPORT);
    remote.sin_addr.s_addr = inet_addr(MCASTADDR);
    if ((sockM = WSAJoinLeaf(sock, (SOCKADDR *) &remote,
                             sizeof(remote), NULL, NULL, NULL, NULL,
                             JL_BOTH)) == INVALID_SOCKET)
    {
        printf("WSAJoinLeaf() failed:%d\n", WSAGetLastError());
        closesocket(sock);
        WSACleanup();
        return -1;
    }

    //发送多播数据,当用户在控制台输入"QUIT"时退出。



    pthread_t thrd1, thrd2;//定义一个线程
    pthread_create(&thrd1, NULL, recvsocket, &sock);
    pthread_create(&thrd2, NULL, sendsocket, &sock);
    pthread_join(thrd1,NULL);
    pthread_join(thrd2,NULL);

    closesocket(sockM);
    closesocket(sock);
    WSACleanup();
    return 0;
}

实验结果截图
在这里插入图片描述

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值