通过UDP从不同的线程进行多播

时间:2012-01-10 13:28:50

标签: c opencv udp pthreads multicast

我正在尝试创建一个应用程序,允许我使用特定的多播地址通过LAN多播我的网络摄像头,并使用sendto()发送帧缓冲区。我尝试构建的应用程序与此站点上的应用程序几乎相同 http://nashruddin.com/Streaming_OpenCV_Videos_Over_the_Network 并使用相同的架构。 我只使用SOCK_DGRAM代替TCP套接字。问题是,当我从另一个线程使用sendto()函数时,它往往会失败,即它返回-1并且errno设置为90(EMSGSIZE),这基本上意味着所形成的数据包太大而无法通过网络。 但即使我尝试将一个简单的字符串(如“hello”)发送到同一个多播地址,也会发生这种情况。如果应用程序是单个线程,这似乎工作正常。也就是说我只是捕获图像并将其全部组播到同一个线程中。这是代码:

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "cv.h"
#include "highgui.h"

#define PORT 12345
#define GROUP "225.0.0.37"
CvCapture*  capture;
IplImage*   img0;
IplImage*   img1;
int         is_data_ready = 0;
int         serversock, clientsock;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* streamServer(void* arg);
void  quit(char* msg, int retval);

int main(int argc, char** argv)
{
      pthread_t     thread_s;
int         key;

if (argc == 2) {
    capture = cvCaptureFromFile(argv[1]);
} else {
    capture = cvCaptureFromCAM(0);
}

if (!capture) {
    quit("cvCapture failed", 1);
}

img0 = cvQueryFrame(capture);
img1 = cvCreateImage(cvGetSize(img0), IPL_DEPTH_8U, 1);

cvZero(img1);
cvNamedWindow("stream_server", CV_WINDOW_AUTOSIZE);

/* print the width and height of the frame, needed by the client */
fprintf(stdout, "width:  %d\nheight: %d\n\n", img0->width, img0->height);
fprintf(stdout, "Press 'q' to quit.\n\n");

/* run the streaming server as a separate thread */
if (pthread_create(&thread_s, NULL, streamServer, NULL)) {
    quit("pthread_create failed.", 1);
}

while(key != 'q') {
    /* get a frame from camera */
    img0 = cvQueryFrame(capture);
    if (!img0) break;

    img0->origin = 0;
    cvFlip(img0, img0, -1);

    /**
     * convert to grayscale 
     * note that the grayscaled image is the image to be sent to the client 
     * so we enclose it with pthread_mutex_lock to make it thread safe 
     */
    pthread_mutex_lock(&mutex);
    cvCvtColor(img0, img1, CV_BGR2GRAY);
    is_data_ready = 1;
    pthread_mutex_unlock(&mutex);

    /* also display the video here on server */
    cvShowImage("stream_server", img0);
    key = cvWaitKey(30);
}

/* user has pressed 'q', terminate the streaming server */
if (pthread_cancel(thread_s)) {
    quit("pthread_cancel failed.", 1);
}

/* free memory */
cvDestroyWindow("stream_server");
quit(NULL, 0);
}

/**
 * This is the streaming server, run as a separate thread
 * This function waits for a client to connect, and send the grayscaled images
 */ 
void* streamServer(void* arg)
{
struct sockaddr_in server;

/* make this thread cancellable using pthread_cancel() */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

/* open socket */
if ((serversock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    quit("socket() failed", 1);
}
memset(&server,0,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr(GROUP);
int opt = 1;
//if(setsockopt(serversock,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(int))==-1){
  //  quit("setsockopt failed",0);
//}
//  /* setup server's IP and port */
//  memset(&server, 0, sizeof(server));
//  server.sin_family = AF_INET;
//  server.sin_port = htons(PORT);
//  server.sin_addr.s_addr = INADDR_ANY;
//   
//  /* bind the socket */
//  if (bind(serversock, (const void*)&server, sizeof(server)) == -1) {
//      quit("bind() failed", 1);
//  }
//
//  /* wait for connection */
//  if (listen(serversock, 10) == -1) {
//      quit("listen() failed.", 1);
//  }
//
//  /* accept a client */
//  if ((clientsock = accept(serversock, NULL, NULL)) == -1) {
//      quit("accept() failed", 1);
//  }

/* the size of the data to be sent */
int imgsize = img1->imageSize;
int bytes=0, i;

/* start sending images */
while(1) 
{
    /* send the grayscaled frame, thread safe */
    pthread_mutex_lock(&mutex);
    if (is_data_ready) {
//          bytes = send(clientsock, img1->imageData, imgsize, 0);
        is_data_ready = 0;
        if((bytes = sendto(serversock,img1->imageData,imgsize,0,(struct sockaddr*)&server,sizeof(server)))==-1){
            quit("sendto FAILED",1);
        }
    }
    pthread_mutex_unlock(&mutex);

//      /* if something went wrong, restart the connection */
//      if (bytes != imgsize) {
//          fprintf(stderr, "Connection closed.\n");
//          close(clientsock);
//
//          if ((clientsock = accept(serversock, NULL, NULL)) == -1) {
//              quit("accept() failed", 1);
//          }
//      }

    /* have we terminated yet? */
    pthread_testcancel();

    /* no, take a rest for a while */
    usleep(1000);
}
}

 /**
  * this function provides a way to exit nicely from the system
  */
void quit(char* msg, int retval)
 {
    if (retval == 0) {
    fprintf(stdout, (msg == NULL ? "" : msg));
    fprintf(stdout, "\n");
} else {
    fprintf(stderr, (msg == NULL ? "" : msg));
    fprintf(stderr, "\n");
}

if (clientsock) close(clientsock);
if (serversock) close(serversock);
if (capture) cvReleaseCapture(&capture);
if (img1) cvReleaseImage(&img1);

pthread_mutex_destroy(&mutex);

    exit(retval);
}

2 个答案:

答案 0 :(得分:3)

sendto()来电中,您引用初始化为imgsize的{​​{1}}。 但我没有看到img1->imageSize设置在哪里,似乎永远不会更新img1->imageSize

首先检查传递给imgsize的{​​{1}}值是否正确。 然后检查它是否太大:

UDP / IP数据报的硬有效负载限制为65,507字节。但是,IPv4网络不需要支持超过548字节的有效负载。 (576是最小的IPv4 MTU大小,少于28字节的UDP / IP开销)。大多数网络的MTU为1500,标称有效载荷为1472字节。

大多数网络允许您通过将数据报分成IP片段来超过MTU,接收操作系统必须重新组装这些片段。这对您的应用程序是不可见的:recvfrom()要么获取整个重组数据包,要么什么也得不到。但是,由于任何碎片的丢失都会导致整个数据包丢失,因此无法获得任何碎片的可能性会增加。此外,一些路由器和操作系统具有模糊的安全规则,这些规则将阻止某些UDP模式或某些大小的片段。

最后,任何给定的网络都可以强制执行最大数据报大小,即使是碎片,这通常远小于65507字节。

由于您正在处理特定网络,因此您需要进行试验,看看您能够可靠地走多远。

UDP/IP at Wikipedia

IPv4 at Wikipedia

答案 1 :(得分:0)

您是否绝对确定不会尝试发送超过65500字节的UDP限制?根据我的经验,您甚至不应发送超过1500字节的以太网数据包限制,以保持最佳的UDP可靠性。

我认为现在您正试图以流的形式发送更多数据。 UDP不是流协议,您不能用它替换TCP。但是当然可以使用UDP在多播上发送视频流,但是在UDP之上需要一些协议来处理UDP的消息大小限制。在现实世界中,UDP之上的RTP协议用于这种任务。