From: xiaotaow Date: Mon, 3 Mar 2003 05:45:56 +0000 (+0000) Subject: Initial revision X-Git-Tag: initial~1 X-Git-Url: http://privyetmir.co.uk/gitweb?a=commitdiff_plain;h=8e25190095265fabb56902fc2f55138774d7912e;p=tcludp Initial revision --- 8e25190095265fabb56902fc2f55138774d7912e diff --git a/src/README b/src/README new file mode 100644 index 0000000..d1ab2cd --- /dev/null +++ b/src/README @@ -0,0 +1,77 @@ +Tcl UDP extension + +1. History of the extension +2. Synopsis +3. How to build and test +4. How to use +5. Feedback + +1. History of the extension +---------------------------------------------------------------------- +Some of the code in this extension is copied from Michael Miller's tcludp +package. (http://www.neosoft.com/tcl/ftparchive/sorted/comm/tcludp-1.0/) +Compared with Michael's UDP extension, this extension provides Windows +support and provides the ability of using 'gets/puts' to read/write +the socket. In addition, it provides more configuration ability. + +2. Synopsis +---------------------------------------------------------------------- +udp_open ?port? +udp_conf sock host port +udp_conf sock ?option? (options: -myport -remote -peer) +udp_peek sock ?buffersize? + +udp_open will open a UDP sockets. If 'port' is specified, the UDP socket +will be opened on that port. Otherwise, the system will choose a port and +the user can use 'udp_conf -myport' to get the port number. + +'udp_conf sock host port' will define the remote host and port to send the +UDP packets to. Prior to using 'puts' to write to the socket, please use +udp_conf to set the remote host and port. + +'udp_conf sock ?option?' will get the socket information. +'udp_conf -myport' will get the local port of the socket +'udp_conf -remote' will get the remote host and port that the socket + will send packets to. +'udp_conf -peer' will get the remote host and port information of the + most recently received packet. + +udp_peek will peek a packet without removing it from the buffer. + +3. How to build and test +---------------------------------------------------------------------- +This extension is to add UDP support for Tcl. +This extension is for Tcl8.0 or later. +Before compiling, please modify the Makefile for proper Tcl path. +......................................... +To compile under Unix, +make -f makefile.unix +libudp.so will be in the ../unix directory +.......................................... +To compile under Linux, +make -f makefile.linux +libudp.so will be in the ../linux directory +........................................... +To compile under Windows +nmake -f makefile.vc +udp.dll will be in the ../win directory +........................................... +The ../test contains two test programs. One is for sending and the other +is for receiving UDP packet. + +4. How to use +---------------------------------------------------------------------- +Use Tcl 'load' command to load the library. +Use udp_open to open a socket. Use udp_conf to specify the remote host +and port and get the socket information. Use 'gets/puts' to receive/send +packets. Use udp_peek to peek the packets. + +5. Feedback +---------------------------------------------------------------------- +If you have any problem with this extension, please contact Xiaotao Wu + +Name : Xiaotao Wu +Email : xiaotaow@cs.columbia.edu, xw71@columbia.edu +URL : http://www.cs.columbia.edu/~xiaotaow +Phone : (212)939-7020, (212)939-7133, Fax: (801)751-0217 +SIP : sip:xiaotaow@conductor.cs.columbia.edu diff --git a/src/makefile.linux b/src/makefile.linux new file mode 100755 index 0000000..c7e82b3 --- /dev/null +++ b/src/makefile.linux @@ -0,0 +1,81 @@ +# Generated automatically from Makefile.in by configure. +# +# This file is a Makefile for UDPTcl. +# +# + +include ../../makefile.linux.include + +# Set the CFLAGS variable to -g if you want debug option. You can also +# SPECIFY IT in the command line. E.g., type +# make CFLAGS=-g all +# +CFLAGS = -O -D_$(TCLVERSION) -DLINUX + +# The directory containing the Tcl sources and headers appropriate for +# this version of your shared library ("srcdir" will be replaced or +# has already been replaced by the configure script): +# TCL_INC_DIR = /usr/include +TCL_INC_DIR = $(TCLDIR)/generic + +# The directory containing the Tcl library archive file appropriate +# for this version of Tk: +#TCL_LIB_DIR = /usr/lib +TCL_LIB_DIR = $(TCLDIR)/unix + +# Libraries to use when linking: +# For Linux +LIBS = -L$(TCL_LIB_DIR) -l$(TCLLIB) -ldl -lm -lc + +#---------------------------------------------------------------- +# The information below is modified by the configure script when +# Makefile is generated from Makefile.in. You shouldn't normally +# modify any of this stuff by hand. +#---------------------------------------------------------------- + +CC = gcc +SHLIB_CFLAGS = -fPIC +# Linux ld +SHLIB_LD = /usr/bin/ld -G -z text +SHLIB_SUFFIX = .so +SHLIB_VERSION = +SRC_DIR = . +OBJ_DIR = ../obj +LIB_DIR = ../linux +AC_FLAGS = -DHAVE_UNISTD_H=1 -DHAVE_LIMITS_H=1 + +CC_SWITCHES = $(CFLAGS) -I${TCL_INC_DIR} -I${SRC_DIR} ${SHLIB_CFLAGS} \ + $(AC_FLAGS) + +UDP_LIB_FILE = $(LIB_DIR)/libudp.so + +OBJS = \ + $(OBJ_DIR)/udp_tcl.o + +all: $(UDP_LIB_FILE) + +$(UDP_LIB_FILE): $(OBJ_DIR) $(LIB_DIR) $(OBJS) + rm -f $(UDP_LIB_FILE) + ${SHLIB_LD} -o $(UDP_LIB_FILE) ${OBJS} ${LIBS} + +#---------------------------------------------------------------------- + +$(OBJ_DIR)/udp_tcl.o: $(SRC_DIR)/udp_tcl.c + $(CC) $(CC_SWITCHES) -c $(SRC_DIR)/udp_tcl.c -o \ + $(OBJ_DIR)/udp_tcl.o + +#---------------------------------------------------------------------- + +$(OBJ_DIR): + if [ ! -d $(OBJ_DIR) ]; then \ + mkdir $(OBJ_DIR); \ + fi + +$(LIB_DIR): + if [ ! -d $(LIB_DIR) ]; then \ + mkdir $(LIB_DIR); \ + fi + +clean: + rm -f $(OBJ_DIR)/*.o $(LIB_DIR)/*${SHLIB_SUFFIX} $(LIB_DIR)/*.a + diff --git a/src/makefile.unix b/src/makefile.unix new file mode 100755 index 0000000..61e0256 --- /dev/null +++ b/src/makefile.unix @@ -0,0 +1,94 @@ +# +# This file is a Makefile for TclUDP for Tcl 8.4 under SunOS +# +# + +include ../../makefile.unix.include + +# Set the CFLAGS variable to -g if you want debug option. You can also +# specify it in the command line. E.g., type +# make CFLAGS=-g all +# +# Define SIPC_IPV6 for IPV6 support +# CFLAGS = -O -D_$(TCLVERSION) -DSIPC_IPV6 +CFLAGS = -O -D_$(TCLVERSION) + +# The directory containing the Tcl sources and headers appropriate for +# this version of your shared library ("srcdir" will be replaced or +# has already been replaced by the configure script): + +TCL_INC_DIR = $(TCLDIR)/generic + +# The directory containing the Tcl library archive file appropriate +# for this version of Tk: + +TCL_LIB_DIR = $(TCLDIR)/unix + +# Static library of Tcl + +TCL_STATIC = $(TCLDIR)/unix/lib$(TCLLIB).a + +# Libraries to use when linking: +# For Solaris +# LIBS = -L$(TCL_LIB_DIR) -l$(TCLVERSION) -ldl -lsocket -lnsl -lm -lc + +LIBS = $(TCL_STATIC) -ldl -lsocket -lnsl -lm -lc + +#---------------------------------------------------------------- +# The information below is modified by the configure script when +# Makefile is generated from Makefile.in. You shouldn't normally +# modify any of this stuff by hand. +#---------------------------------------------------------------- + +CC = gcc +SHLIB_CFLAGS = -fPIC +SHLIB_LD = /usr/ccs/bin/ld -G -z text +RANLIB = ranlib +SHLIB_SUFFIX = .so +SHLIB_VERSION = +SRC_DIR = . +OBJ_DIR = ../obj +LIB_DIR = ../unix +AC_FLAGS = -DHAVE_UNISTD_H=1 -DHAVE_LIMITS_H=1 + +CC_SWITCHES = $(CFLAGS) -I${TCL_INC_DIR} -I${SRC_DIR} ${SHLIB_CFLAGS} \ + -D__SOLARIS__ $(AC_FLAGS) + +UDP_LIB_FILE = $(LIB_DIR)/libudp.so +UDP_STATIC_FILE = $(LIB_DIR)/libudp.a + +OBJS = \ + $(OBJ_DIR)/udp_tcl.o + +all: $(UDP_LIB_FILE) $(UTIL_STATIC_FILE) + +$(UDP_LIB_FILE): $(OBJ_DIR) $(LIB_DIR) $(OBJS) + rm -f $(UDP_LIB_FILE) + ${SHLIB_LD} -o $(UDP_LIB_FILE) ${OBJS} ${LIBS} + +$(UDP_STATIC_FILE): $(OBJ_DIR) $(LIB_DIR) $(OBJS) + rm -f $(UDP_STATIC_FILE) + ar cr $(UDP_STATIC_FILE) $(OBJS) + $(RANLIB) $(UDP_STATIC_FILE) + +#---------------------------------------------------------------------- + +$(OBJ_DIR)/udp_tcl.o: $(SRC_DIR)/udp_tcl.c + $(CC) $(CC_SWITCHES) -c $(SRC_DIR)/udp_tcl.c -o \ + $(OBJ_DIR)/udp_tcl.o + +#---------------------------------------------------------------------- + +$(OBJ_DIR): + if [ ! -d $(OBJ_DIR) ]; then \ + mkdir $(OBJ_DIR); \ + fi + +$(LIB_DIR): + if [ ! -d $(LIB_DIR) ]; then \ + mkdir $(LIB_DIR); \ + fi + +clean: + rm -f $(OBJ_DIR)/*.o $(LIB_DIR)/*${SHLIB_SUFFIX} $(LIB_DIR)/*.a + diff --git a/src/makefile.vc b/src/makefile.vc new file mode 100644 index 0000000..525ea6a --- /dev/null +++ b/src/makefile.vc @@ -0,0 +1,38 @@ +include ..\..\makefile.vc.include + +SRC_DIR = . +WIN_DIR = ..\win +OBJ_DIR = ..\obj + +CP = copy +RM = del + +INCLUDES = -I$(TOOLS32)\include -I$(WIN_DIR) -I$(TCL_GENERIC_DIR) -I$(TK_GENERIC_DIR) -I$(MFC_DIR)\include + +#use -DDEBUG to enable debug output. The debug output will be sent to udp.dbg file +DEFINES = -nologo -D_$(TCLVERSION) -DWIN32 + +OBJS = $(OBJ_DIR)\udp_tcl.obj + +UDPDLL = udp.dll + +$(UDPDLL): $(OBJ_DIR) $(OBJS) $(WIN_DIR) + @set LIB=$(TOOLS32)\lib + cl $(OBJS) /LD -o $(WIN_DIR)\$(UDPDLL) /link Wsock32.lib winmm.lib user32.lib $(MFC_DIR)\lib\nafxcw.lib $(guilibsdll) $(TCL_LIB) + +$(OBJ_DIR): + -@if not exist $(OBJ_DIR)\nul mkdir $(OBJ_DIR) +$(WIN_DIR): + -@if not exist $(WIN_DIR)\nul mkdir $(WIN_DIR) + +$(OBJ_DIR)\udp_tcl.obj : $(SRC_DIR)\udp_tcl.c + $(cc32) $(cdebug) -c $(cvarsdll) $(INCLUDES) \ + $(DEFINES) /Fo$(OBJ_DIR)\udp_tcl.obj $(SRC_DIR)\udp_tcl.c + +clean: + -@if exist $(OBJ_DIR)\*.obj $(RM) $(OBJ_DIR)\*.obj + -@if exist $(WIN_DIR)\$(UDPDLL) $(RM) $(WIN_DIR)\$(UDPDLL) + -@if exist $(WIN_DIR)\udp.lib $(RM) $(WIN_DIR)\udp.lib + -@if exist $(WIN_DIR)\udp.exp $(RM) $(WIN_DIR)\udp.exp + -@if exist pkgIndex.tcl $(RM) pkgIndex.tcl + diff --git a/src/udp_tcl.c b/src/udp_tcl.c new file mode 100644 index 0000000..ec22a2a --- /dev/null +++ b/src/udp_tcl.c @@ -0,0 +1,1058 @@ +/****************************************************************************** + * UDP Extension for Tcl 8.4 + * + * Copyright 1999-2000 by Columbia University; all rights reserved + * + * Written by Xiaotao Wu + * Last modified: 11/03/2000 + ******************************************************************************/ +#include "udp_tcl.h" + +#ifdef WIN32 +#include +#include +#include +#include +#else +#ifdef LINUX +#include +#else +#include +#endif +#endif + +#ifdef DEBUG +FILE *dbg; +#endif + +#define MAXBUFFERSIZE 4096 + +static char errBuf[256]; + +EXTERN int Udp_Init(Tcl_Interp *); + +int udpOpen(ClientData , Tcl_Interp *, int , char *[]); +int udpConf(ClientData , Tcl_Interp *, int , char *[]); +int udpPeek(ClientData , Tcl_Interp *, int , char *[]); + +static int udpGet(ClientData instanceData,int direction,ClientData *handlePtr); +static void udpWatch(ClientData instanceData, int mask); +static int udpOutput(ClientData instanceData, char *buf, int toWrite, int *errorCode); +static int udpInput(ClientData instanceData, char *buf, int bufSize, int *errorCode); +static int udpClose(ClientData instanceData, Tcl_Interp *interp); + +#ifdef WIN32 +static HANDLE waitForSock; +static HANDLE waitSockRead; +static HANDLE sockListLock; +static UdpState *sockList; +static UdpState *sockTail; +#endif + +/* + * udpClose + */ +int udpClose(ClientData instanceData, Tcl_Interp *interp) { + int sock; + int errorCode = 0; + UdpState *statePtr = (UdpState *) instanceData; +#ifdef WIN32 + UdpState *statePre; + + WaitForSingleObject(sockListLock, INFINITE); +#endif + + sock = statePtr->sock; + +#ifdef WIN32 + //remove the statePtr from the list + for (statePtr = sockList, statePre = sockList; statePtr != NULL; statePre=statePtr, statePtr=statePtr->next) { + if (statePtr->sock == sock) { +#ifdef DEBUG + fprintf(dbg, "Remove %d from the list\n", sock); + fflush(dbg); +#endif + if (statePtr == sockList) { + sockList = statePtr->next; + } else { + statePre->next = statePtr->next; + if (sockTail == statePtr) + sockTail = statePre; + } + } + } +#endif + + ckfree((char *) statePtr); +#ifndef WIN32 + if (close(sock) < 0) { +#else + if (closesocket(sock) < 0) { +#endif + errorCode = errno; + } + if (errorCode != 0) { +#ifndef WIN32 + sprintf(errBuf, "udpClose: %d, error: %d\n", sock, errorCode); +#else + sprintf(errBuf, "udpClose: %d, error: %d\n", sock, WSAGetLastError()); +#endif +#ifdef DEBUG + fprintf(dbg, "UDP error - close %d", sock); + fflush(dbg); +#endif + } else { +#ifdef DEBUG + fprintf(dbg, "Close socket %d\n", sock); + fflush(dbg); +#endif + } + +#ifdef WIN32 + SetEvent(sockListLock); +#endif + + return errorCode; +} + +/* + * udpWatch + */ +static void udpWatch(ClientData instanceData, int mask) { +#ifndef WIN32 + UdpState *fsPtr = (UdpState *) instanceData; + if (mask) { +#ifdef DEBUG + fprintf(dbg, "Tcl_CreateFileHandler\n"); + fflush(dbg); +#endif + Tcl_CreateFileHandler(fsPtr->sock, mask, + (Tcl_FileProc *) Tcl_NotifyChannel, + (ClientData) fsPtr->channel); + } else { +#ifdef DEBUG + fprintf(dbg, "Tcl_DeleteFileHandler\n"); + fflush(dbg); +#endif + Tcl_DeleteFileHandler(fsPtr->sock); + } +#endif +} + +/* + * udpGet + */ +static int udpGet(ClientData instanceData,int direction,ClientData *handlePtr) { + UdpState *statePtr = (UdpState *) instanceData; +#ifdef DEBUG + fprintf(dbg, "UDP_get %d\n", statePtr->sock); + fflush(dbg); +#endif + return statePtr->sock; +} + +/* + * udpPeek - peek some data and set the peer information + */ +int udpPeek(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { +#ifndef WIN32 + int buffer_size = 16; + int actual_size, socksize, port; + int sock; + char message[17]; + char *remotehost; + struct hostent *name; +#ifdef SIPC_IPV6 + struct sockaddr_in6 recvaddr; +#else + struct sockaddr_in recvaddr; +#endif + Tcl_Channel chan; + UdpState *statePtr; + + chan = Tcl_GetChannel(interp, argv[1], NULL); + if (chan == (Tcl_Channel) NULL) { + return TCL_ERROR; + } + statePtr = (UdpState *) Tcl_GetChannelInstanceData(chan); + sock = Tcl_GetChannelHandle (chan, (TCL_READABLE | TCL_WRITABLE), NULL); + + if (argc > 2) { + buffer_size = atoi(argv[2]); + if (buffer_size > 16) buffer_size = 16; + } + actual_size = recvfrom(sock, message, buffer_size, MSG_PEEK, + (struct sockaddr *)&recvaddr, &socksize); + + if (actual_size < 0) { + sprintf(errBuf, "udppeek error"); + Tcl_AppendResult(interp, errBuf, (char *)NULL); + return TCL_ERROR; + } +#ifdef SIPC_IPV6 + remotehost = (char *)inet_ntop(AF_INET6, &recvaddr.sin_addr, statePtr->peerhost, sizeof(statePtr->peerhost) ); + statePtr->peerport = ntohs(recvaddr.sin_port); +#else + strcpy(statePtr->peerhost, (char *)inet_ntoa(recvaddr.sin_addr)); + statePtr->peerport = ntohs(recvaddr.sin_port); +#endif + message[16]='\0'; + + Tcl_AppendResult(interp, message, (char *)NULL); +#endif + return TCL_OK; +} + +/* + * udpConf + */ +int udpConf(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { + Tcl_Channel chan; + UdpState *statePtr; + char *result; + char buf[128]; + struct hostent *name; + struct ip_mreq mreq; + struct sockaddr_in maddr; + int sock, ret; + + if (argc != 4 && argc != 3) { + result = "udpConf fileId [-mcastadd] [-mcastdrop] groupaddr | udpConf fileId remotehost remoteport | udpConf fileId [-myport] [-remote] [-peer]"; + Tcl_SetResult (interp, result, NULL); + return TCL_ERROR; + } + chan = Tcl_GetChannel(interp, argv[1], NULL); + if (chan == (Tcl_Channel) NULL) { + return TCL_ERROR; + } + statePtr = (UdpState *) Tcl_GetChannelInstanceData(chan); + sock = statePtr->sock; + + if (argc == 3) { + if (!strcmp(argv[2], "-myport")) { + sprintf(buf, "%d", statePtr->localport); + Tcl_AppendResult(interp, buf, (char *)NULL); + } else if (!strcmp(argv[2], "-remote")) { + sprintf(buf, "%s", statePtr->remotehost); + Tcl_AppendResult(interp, buf, (char *)NULL); + sprintf(buf, "%d", statePtr->remoteport); + Tcl_AppendElement(interp, buf); + } else if (!strcmp(argv[2], "-peer")) { + sprintf(buf, "%s", statePtr->peerhost); + Tcl_AppendResult(interp, buf, (char *)NULL); + sprintf(buf, "%d", statePtr->peerport); + Tcl_AppendElement(interp, buf); + } else { + result = "udpConf fileId [-mcastadd] [-mcastdrop] groupaddr | udpConf fileId remotehost remoteport | udpConf fileId [-myport] [-remote] [-peer]"; + Tcl_SetResult (interp, result, NULL); + return TCL_ERROR; + } + return TCL_OK; + } else if (argc == 4) { + if (!strcmp(argv[2], "-mcastadd")) { + if (strlen(argv[3]) >= sizeof(statePtr->remotehost)) { + result = "hostname too long"; + Tcl_SetResult (interp, result, NULL); + return TCL_ERROR; + } + mreq.imr_multiaddr.s_addr = inet_addr(argv[3]); + if (mreq.imr_multiaddr.s_addr == -1) { + name = gethostbyname(argv[3]); + if (name == NULL) { +#ifdef DEBUG + fprintf(dbg, "UDP error - gethostbyname"); + fflush(dbg); +#endif + return TCL_ERROR; + } + memcpy(&mreq.imr_multiaddr.s_addr, name->h_addr, sizeof(mreq.imr_multiaddr.s_addr)); + } + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { +#ifdef DEBUG + fprintf(dbg, "UDP error - setsockopt - IP_ADD_MEMBERSHIP"); + fflush(dbg); +#endif + return TCL_ERROR; + } + maddr.sin_addr.s_addr = htonl(INADDR_ANY); + return TCL_OK; + } else if (!strcmp(argv[2], "-mcastdrop")) { + if (strlen(argv[3]) >= sizeof(statePtr->remotehost)) { + result = "hostname too long"; + Tcl_SetResult (interp, result, NULL); + return TCL_ERROR; + } + mreq.imr_multiaddr.s_addr = inet_addr(argv[3]); + if (mreq.imr_multiaddr.s_addr == -1) { + name = gethostbyname(argv[3]); + if (name == NULL) { +#ifdef DEBUG + fprintf(dbg, "UDP error - gethostbyname"); + fflush(dbg); +#endif + return TCL_ERROR; + } + memcpy(&mreq.imr_multiaddr.s_addr, name->h_addr, sizeof(mreq.imr_multiaddr.s_addr)); + } + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { +#ifdef DEBUG + fprintf(dbg, "UDP error - setsockopt - IP_DROP_MEMBERSHIP"); + fflush(dbg); +#endif + return TCL_ERROR; + } + return TCL_OK; + } else { + if (strlen(argv[2]) >= sizeof(statePtr->remotehost)) { + result = "hostname too long"; + Tcl_SetResult (interp, result, NULL); + return TCL_ERROR; + } + strcpy(statePtr->remotehost, argv[2]); + statePtr->remoteport = atoi(argv[3]); + return TCL_OK; + } + } else { + result = "udpConf fileId [-mcastadd] [-mcastdrop] groupaddr | udpConf fileId remotehost remoteport | udpConf fileId [-myport] [-remote] [-peer]"; + Tcl_SetResult (interp, result, NULL); + return TCL_ERROR; + } +} + +#ifdef WIN32 +/* + * UdpEventProc -- + */ +int UdpEventProc(Tcl_Event *evPtr, int flags) { + UdpEvent *eventPtr = (UdpEvent *) evPtr; + int mask = 0; + + mask |= TCL_READABLE; +#ifdef DEBUG + fprintf(dbg, "UdpEventProc\n"); + fflush(dbg); +#endif + Tcl_NotifyChannel(eventPtr->chan, mask); + return 1; +} + +/* + * UDP_SetupProc - called in Tcl_SetEventSource to do the setup step + */ +static void UDP_SetupProc(ClientData data, int flags) { + UdpState *statePtr; + Tcl_Time blockTime = { 0, 0 }; + +#ifdef DEBUG + fprintf(dbg, "setupProc\n"); + fflush(dbg); +#endif + + if (!(flags & TCL_FILE_EVENTS)) { + return; + } + + WaitForSingleObject(sockListLock, INFINITE); + for (statePtr = sockList; statePtr != NULL; statePtr=statePtr->next) { + if (statePtr->packetNum > 0) { +#ifdef DEBUG + fprintf(dbg, "UDP_SetupProc\n"); + fflush(dbg); +#endif + Tcl_SetMaxBlockTime(&blockTime); + break; + } + } + SetEvent(sockListLock); +} + +/* + * UDP_CheckProc -- + */ +void UDP_CheckProc(ClientData data, int flags) { + UdpState *statePtr; + UdpEvent *evPtr; + int actual_size, socksize, reply; + int buffer_size = MAXBUFFERSIZE; + int port; + struct hostent *name; + char *remotehost, *message; +#ifdef SIPC_IPV6 + char number[128]; + struct sockaddr_in6 recvaddr; +#else + char number[32]; + struct sockaddr_in recvaddr; +#endif + PacketList *p; + +#ifdef DEBUG + fprintf(dbg, "checkProc\n"); + fflush(dbg); +#endif + + //synchronized + WaitForSingleObject(sockListLock, INFINITE); + + for (statePtr = sockList; statePtr != NULL; statePtr=statePtr->next) { + if (statePtr->packetNum > 0) { + +#ifdef DEBUG + fprintf(dbg, "UDP_CheckProc\n"); + fflush(dbg); +#endif + //Read the data from socket and put it into statePtr + socksize = sizeof(recvaddr); +#ifdef SIPC_IPV6 + memset(number, 0, 128); +#else + memset(number, 0, 32); +#endif + memset(&recvaddr, 0, socksize); + + message = (char *)calloc(1, MAXBUFFERSIZE); + if (message == NULL) { +#ifdef DEBUG + fprintf(dbg, "calloc error\n"); + fflush(dbg); +#endif + exit(1); + } + + actual_size = recvfrom(statePtr->sock, message, buffer_size, 0, + (struct sockaddr *)&recvaddr, &socksize); + SetEvent(waitSockRead); + + if (actual_size < 0) { + if (WSAGetLastError() == WSAEMSGSIZE) { + actual_size = buffer_size; + } + } + + if (actual_size < 0) { +#ifdef DEBUG + fprintf(dbg, "UDP error - recvfrom %d\n", statePtr->sock); + fflush(dbg); +#endif + } else { + p = (PacketList *)calloc(1, sizeof(struct PacketList)); + p->message = message; + p->actual_size = actual_size; +#ifdef SIPC_IPV6 + remotehost = (char *)inet_ntoa(AF_INET6, &recvaddr.sin6_addr, p->r_host, sizeof(p->r_host)); + p->r_port = ntohs(recvaddr.sin6_port); +#else + strcpy(p->r_host, (char *)inet_ntoa(recvaddr.sin_addr)); + p->r_port = ntohs(recvaddr.sin_port); +#endif + p->next = NULL; + +#ifdef SIPC_IPV6 + remotehost = (char *)inet_ntoa(AF_INET6, &recvaddr.sin6_addr, statePtr->peerhost, sizeof(statePtr->peerhost) ); + statePtr->peerport = ntohs(recvaddr.sin6_port); +#else + strcpy(statePtr->peerhost, (char *)inet_ntoa(recvaddr.sin_addr)); + statePtr->peerport = ntohs(recvaddr.sin_port); +#endif + + if (statePtr->packets == NULL) { + statePtr->packets = p; + statePtr->packetsTail = p; + } else { + statePtr->packetsTail->next = p; + statePtr->packetsTail = p; + } + +#ifdef DEBUG + fprintf(dbg, "Received %d bytes from %s:%d through %d\n", p->actual_size, p->r_host, p->r_port, statePtr->sock); + fwrite(p->message, 1, p->actual_size, dbg); + fflush(dbg); +#endif + } + + statePtr->packetNum--; + statePtr->doread = 1; +#ifdef DEBUG + fprintf(dbg, "packetNum is %d\n", statePtr->packetNum); + fflush(dbg); +#endif + if (actual_size >= 0) { + evPtr = (UdpEvent *) ckalloc(sizeof(UdpEvent)); + evPtr->header.proc = UdpEventProc; + evPtr->chan = statePtr->channel; + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); +#ifdef DEBUG + fprintf(dbg, "socket %d has data\n", statePtr->sock); + fflush(dbg); +#endif + } + } + } + + SetEvent(sockListLock); +} +#endif + +/* + *---------------------------------------------------------------------- + * udpOpen - opens a UDP socket and addds the file descriptor to the + * tcl interpreter + *---------------------------------------------------------------------- + */ +int udpOpen(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { + int sock; + char channelName[20]; + UdpState *statePtr; + char * result; + uint16_t localport = 0; +#ifdef SIPC_IPV6 + struct sockaddr_in6 addr, sockaddr; +#else + struct sockaddr_in addr, sockaddr; +#endif + unsigned long status = 1; + int len; +#ifdef WIN32 + UdpState *tmp; + static initd = 0; +#endif + + Tcl_ChannelType *Udp_ChannelType; + Udp_ChannelType = (Tcl_ChannelType *) ckalloc((unsigned) sizeof(Tcl_ChannelType)); +#ifdef SIPC_IPV6 + Udp_ChannelType->typeName = strdup("udp6"); +#else + Udp_ChannelType->typeName = strdup("udp"); +#endif + Udp_ChannelType->blockModeProc = NULL; + Udp_ChannelType->closeProc = udpClose; + Udp_ChannelType->inputProc = udpInput; + Udp_ChannelType->outputProc = udpOutput; + Udp_ChannelType->seekProc = NULL; + Udp_ChannelType->setOptionProc = NULL; + Udp_ChannelType->getOptionProc = NULL; + Udp_ChannelType->watchProc = udpWatch; + Udp_ChannelType->getHandleProc = udpGet; + Udp_ChannelType->close2Proc = NULL; + + if (argc >= 2) { + localport = atoi(argv[1]); + } + + memset(channelName, 0, sizeof(channelName)); + +#ifdef SIPC_IPV6 + if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { +#else + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { +#endif + sprintf(errBuf,"%s","udp - socket"); +#ifdef DEBUG + fprintf(dbg, "UDP error - socket\n"); + fflush(dbg); +#endif + Tcl_AppendResult(interp, errBuf, (char *)NULL); + return TCL_ERROR; + } + memset(&addr, 0, sizeof(addr)); +#ifdef SIPC_IPV6 + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(localport); +#else + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = 0; + addr.sin_port = htons(localport); +#endif + if (bind(sock,(struct sockaddr *)&addr, sizeof(addr)) < 0) { + sprintf(errBuf,"%s","udp - bind"); +#ifdef DEBUG + fprintf(dbg, "UDP error - bind\n"); + fflush(dbg); +#endif + Tcl_AppendResult(interp, errBuf, (char *)NULL); + return TCL_ERROR; + } +#ifdef WIN32 + ioctlsocket(sock, FIONBIO, &status); +#else + ioctl(sock, (int) FIONBIO, &status); +#endif + if (localport == 0) { + len = sizeof(sockaddr); + getsockname(sock, (struct sockaddr *)&sockaddr, &len); +#ifdef SIPC_IPV6 + localport = ntohs(sockaddr.sin6_port); +#else + localport = ntohs(sockaddr.sin_port); +#endif + } +#ifdef DEBUG + fprintf(dbg, "Open socket %d. Bind socket to port %d\n", sock, localport); + fflush(dbg); +#endif + + statePtr = (UdpState *) ckalloc((unsigned) sizeof(UdpState)); + statePtr->sock = sock; + sprintf(channelName, "sock%d", statePtr->sock); + statePtr->channel = Tcl_CreateChannel(Udp_ChannelType, channelName, + (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE | TCL_MODE_NONBLOCKING)); + statePtr->doread = 1; + statePtr->localport = localport; + Tcl_RegisterChannel(interp, statePtr->channel); +#ifdef WIN32 + statePtr->threadId = Tcl_GetCurrentThread(); + statePtr->packetNum = 0; + statePtr->next = NULL; + statePtr->packets = NULL; + statePtr->packetsTail = NULL; + Tcl_CreateEventSource(UDP_SetupProc, UDP_CheckProc, NULL); +#endif +// Tcl_SetChannelOption(interp, statePtr->channel, "-blocking", "0"); + Tcl_AppendResult(interp, channelName, (char *)NULL); +#ifdef WIN32 + WaitForSingleObject(sockListLock, INFINITE); + if (sockList == NULL) { + sockList = statePtr; + sockTail = statePtr; + } else { + sockTail->next = statePtr; + sockTail = statePtr; + } +#ifdef DEBUG + fprintf(dbg, "Append %d to sockList\n", statePtr->sock); + fflush(dbg); + for (tmp = sockList; tmp != NULL; tmp = tmp->next) { + fprintf(dbg, "%d --> ", tmp->sock); + fflush(dbg); + } + fprintf(dbg, "NULL\n"); + fflush(dbg); +#endif + SetEvent(sockListLock); + SetEvent(waitForSock); +#endif + return TCL_OK; +} + +#ifdef WIN32 + +/* + * InitSockets + */ +static int InitSockets() { + WSADATA wsaData; + WNDCLASS class; + HINSTANCE handle; + + /* + * Load the socket DLL and initialize the function table. + */ + + if (WSAStartup(0x0101, &wsaData)) + return 0; + + return 1; +} + +/* + * SocketThread + */ +static DWORD WINAPI SocketThread(LPVOID arg) { + fd_set readfds; //variable used for select + struct timeval timeout; + UdpState *statePtr; + int found; + int sockset; + + FD_ZERO(&readfds); + +#ifdef DEBUG + fprintf(dbg, "In socket thread\n"); + fflush(dbg); +#endif + while (1) { + FD_ZERO(&readfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + //synchronized + WaitForSingleObject(sockListLock, INFINITE); + + //no socket, just wait, use event + if (sockList == NULL) { + SetEvent(sockListLock); +#ifdef DEBUG + fprintf(dbg, "Wait for adding socket\n"); + fflush(dbg); +#endif + WaitForSingleObject(waitForSock, INFINITE); + //synchronized + WaitForSingleObject(sockListLock, INFINITE); + } + + //set each socket for select + for (statePtr = sockList; statePtr != NULL; statePtr=statePtr->next) { + FD_SET(statePtr->sock, &readfds); +#ifdef DEBUG + fprintf(dbg, "SET sock %d\n", statePtr->sock); + fflush(dbg); +#endif + } + + SetEvent(sockListLock); + +#ifdef DEBUG + fprintf(dbg, "Wait for select\n"); + fflush(dbg); +#endif + //block here + found = select(0, &readfds, NULL, NULL, &timeout); +#ifdef DEBUG + fprintf(dbg, "select end\n"); + fflush(dbg); +#endif + + if (found <= 0) { + //We closed the socket during select or time out + continue; + } + +#ifdef DEBUG + fprintf(dbg, "Packet comes in\n"); + fflush(dbg); +#endif + + WaitForSingleObject(sockListLock, INFINITE); + sockset = 0; + for (statePtr = sockList; statePtr != NULL; statePtr=statePtr->next) { + if (FD_ISSET(statePtr->sock, &readfds)) { + statePtr->packetNum++; + sockset++; +#ifdef DEBUG + fprintf(dbg, "sock %d is set\n", statePtr->sock); + fflush(dbg); +#endif + break; + } + } + SetEvent(sockListLock); + + //wait for the socket data was read + if (sockset > 0) { +#ifdef DEBUG + fprintf(dbg, "Wait sock read\n"); + fflush(dbg); +#endif + //alert the thread to do event checking + Tcl_ThreadAlert(statePtr->threadId); + WaitForSingleObject(waitSockRead, INFINITE); +#ifdef DEBUG + fprintf(dbg, "Sock read finished\n"); + fflush(dbg); +#endif + } + } +} + +/* + * Udp_WinHasSockets -- + */ +int Udp_WinHasSockets(Tcl_Interp *interp) { + static int initialized = 0; /* 1 if the socket sys has been initialized. */ + static int hasSockets = 0; /* 1 if the system supports sockets. */ + HANDLE socketThread; + DWORD id; + + if (!initialized) { + OSVERSIONINFO info; + + initialized = 1; + + /* + * Find out if we're running on Win32s. + */ + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&info); + + /* + * Check to see if Sockets are supported on this system. Since + * win32s panics if we call WSAStartup on a system that doesn't + * have winsock.dll, we need to look for it on the system first. + * If we find winsock, then load the library and initialize the + * stub table. + */ + + if ((info.dwPlatformId != VER_PLATFORM_WIN32s) + || (SearchPath(NULL, "WINSOCK", ".DLL", 0, NULL, NULL) != 0)) { + hasSockets = InitSockets(); + } + + /* + * Start the socketThread windows and set the thread priority of the + * socketThread as highest + */ + + sockList = NULL; + sockTail = NULL; + waitForSock = CreateEvent(NULL, FALSE, FALSE, NULL); + waitSockRead = CreateEvent(NULL, FALSE, FALSE, NULL); + sockListLock = CreateEvent(NULL, FALSE, TRUE, NULL); + + socketThread = CreateThread(NULL, 8000, SocketThread, NULL, 0, &id); + SetThreadPriority(socketThread, THREAD_PRIORITY_HIGHEST); + +#ifdef DEBUG + fprintf(dbg, "Initialize socket thread\n"); + fflush(dbg); +#endif + + if (socketThread == NULL) { +#ifdef DEBUG + fprintf(dbg, "Failed to create thread\n"); + fflush(dbg); +#endif + } + } + if (hasSockets) { + return TCL_OK; + } + if (interp != NULL) { + Tcl_AppendResult(interp, "sockets are not available on this system", + NULL); + } + return TCL_ERROR; +} + +#endif + +/* + * udpInit + */ +int Udp_Init(Tcl_Interp *interp) { +#ifdef DEBUG + dbg = fopen("udp.dbg", "wb"); +#endif + +#ifdef WIN32 + if (Udp_WinHasSockets(interp) != TCL_OK) { + return TCL_ERROR; + } +#endif + + Tcl_CreateCommand(interp, "udp_open", udpOpen , + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "udp_conf", udpConf , + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "udp_peek", udpPeek , + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + return TCL_OK; +} + +/* + * udpOutput-- + */ +static int udpOutput(ClientData instanceData, char *buf, int toWrite, int *errorCode) { + UdpState *statePtr = (UdpState *) instanceData; + int written; + int socksize; + struct hostent *name; +#ifdef SIPC_IPV6 + struct sockaddr_in6 sendaddr; + struct in6_addr inp; + int n, errnum; +#else + struct sockaddr_in sendaddr; + struct in_addr inp; +#endif + + *errorCode = 0; + errno = 0; + + if (toWrite > MAXBUFFERSIZE) { +#ifdef DEBUG + fprintf(dbg, "UDP error - MAXBUFFERSIZE"); + fflush(dbg); +#endif + return -1; + } + socksize = sizeof(sendaddr); + memset(&sendaddr, 0, socksize); + +#ifdef SIPC_IPV6 + n = inet_pton(AF_INET6, statePtr->remotehost, &sendaddr.sin6_addr); + if (n <= 0) { + name = getipnodebyname(statePtr->remotehost, AF_INET6, AI_DEFAULT, &errnum); +#else + sendaddr.sin_addr.s_addr = inet_addr(statePtr->remotehost); + if (sendaddr.sin_addr.s_addr == -1) { + name = gethostbyname(statePtr->remotehost); +#endif + if (name == NULL) { +#ifdef DEBUG + fprintf(dbg, "UDP error - gethostbyname"); + fflush(dbg); +#endif + return -1; + } +#ifdef SIPC_IPV6 + memcpy(&sendaddr.sin6_addr, name->h_addr, sizeof(sendaddr.sin6_addr)); + } + sendaddr.sin6_family = AF_INET6; + sendaddr.sin6_port = htons(statePtr->remoteport); +#else + memcpy(&sendaddr.sin_addr, name->h_addr, sizeof(sendaddr.sin_addr)); + } + sendaddr.sin_family = AF_INET; + sendaddr.sin_port = htons(statePtr->remoteport); +#endif + written = sendto(statePtr->sock, buf, toWrite, 0, (struct sockaddr *)&sendaddr, socksize); + if (written < 0) { +#ifdef DEBUG + fprintf(dbg, "UDP error - sendto"); + fflush(dbg); +#endif + return -1; + } + +#ifdef DEBUG + fprintf(dbg, "Send %d to %s:%d through %d\n", written, statePtr->remotehost, statePtr->remoteport, statePtr->sock); + fflush(dbg); +#endif + + return written; +} + +/* + * udpInput + */ +static int udpInput(ClientData instanceData, char *buf, int bufSize, int *errorCode) { + UdpState *statePtr = (UdpState *) instanceData; + int bytesRead; + int actual_size, socksize, reply; + int buffer_size = MAXBUFFERSIZE; + int port; + char *remotehost; + struct hostent *name; +#ifdef SIPC_IPV6 + char number[128]; + struct sockaddr_in6 recvaddr; +#else + char number[32]; + struct sockaddr_in recvaddr; +#endif + int sock = statePtr->sock; + int i; +#ifdef WIN32 + PacketList *packets; +#endif + +#ifdef DEBUG + fprintf(dbg, "In udpInput\n"); + fflush(dbg); +#endif + + /* + * The caller of this function is looking for a stream oriented + * system, so it keeps calling the function until no bytes are + * returned, and then appends all the characters together. This + * is not what we want from UDP, so we fake it by returning a + * blank every other call. whenever the doread variable is 1 do + * a normal read, otherwise just return 0. + */ + if (statePtr->doread == 0) { + statePtr->doread = 1; /* next time we want to behave normally */ + *errorCode = EAGAIN; /* pretend that we would block */ +#ifdef DEBUG + fprintf(dbg, "Pretend we would block\n"); + fflush(dbg); +#endif + return 0; + } + + *errorCode = 0; + errno = 0; + + if (bufSize == 0) { + return 0; + } + +#ifdef WIN32 + packets = statePtr->packets; +#ifdef DEBUG + fprintf(dbg, "udp_recv\n"); + fflush(dbg); +#endif + if (packets == NULL) { +#ifdef DEBUG + fprintf(dbg, "packets is NULL\n"); + fflush(dbg); +#endif + return 0; + } + memcpy(buf, packets->message, packets->actual_size); + free((char *) packets->message); +#ifdef DEBUG + fprintf(dbg, "udp_recv message\n"); + fwrite(buf, 1, packets->actual_size, dbg); + fflush(dbg); +#endif + bufSize = packets->actual_size; + strcpy(statePtr->peerhost, packets->r_host); + statePtr->peerport = packets->r_port; + statePtr->packets = packets->next; + free((char *) packets); + bytesRead = bufSize; +#else + // else for Unix/Linux + socksize = sizeof(recvaddr); +#ifdef SIPC_IPV6 + memset(number, 0, 128); +#else + memset(number, 0, 32); +#endif + memset(&recvaddr, 0, socksize); + + bytesRead = recvfrom(sock, buf, buffer_size, 0, (struct sockaddr *)&recvaddr, &socksize); + if (bytesRead < 0) { +#ifdef DEBUG + fprintf(dbg, "UDP error - recvfrom %d\n", sock); + fflush(dbg); +#endif + *errorCode = errno; + return -1; + } + +#ifdef SIPC_IPV6 + remotehost = (char *)inet_ntop(AF_INET6, &recvaddr.sin6_addr, statePtr->peerhost, sizeof(statePtr->peerhost)); + port = ntohs(recvaddr.sin6_port); +#else + remotehost = (char *)inet_ntoa(recvaddr.sin_addr); + port = ntohs(recvaddr.sin_port); + strcpy(statePtr->peerhost, remotehost); +#endif + +#ifdef DEBUG + fprintf(dbg, "remotehost: %s\n", remotehost); + fflush(dbg); +#endif + statePtr->peerport = port; +#endif + + /* we don't want to return anything next time */ + if (bytesRead > 0) { buf[bytesRead] = '\0'; statePtr->doread = 0; } + +#ifdef DEBUG + fprintf(dbg, "udpInput end: %d, %s\n", bytesRead, buf); + fflush(dbg); +#endif + + if (bytesRead > -1) { + return bytesRead; + } + + *errorCode = errno; + return -1; +} + diff --git a/src/udp_tcl.h b/src/udp_tcl.h new file mode 100644 index 0000000..eff1c86 --- /dev/null +++ b/src/udp_tcl.h @@ -0,0 +1,68 @@ +#ifndef UDP_TCL_H +#define UDP_TCL_H +/* + *---------------------------------------------------------------------- + * UDP Extension for Tcl 8.4 + * + * Copyright 1999-2000 by Columbia University; all rights reserved + * + * Written by Xiaotao Wu + * Last modified: 11/03/2000 + *---------------------------------------------------------------------- + */ +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include "tcl.h" + +#ifdef WIN32 + +typedef u_short uint16_t; + +typedef struct { + Tcl_Event header; /* Information that is standard for */ + Tcl_Channel chan; /* Socket descriptor that is ready */ +} UdpEvent; + +typedef struct PacketList { + char *message; + int actual_size; + char r_host[256]; + int r_port; + struct PacketList *next; +} PacketList; + +#endif + +typedef struct UdpState { + Tcl_Channel channel; + int sock; + char remotehost[256]; /* send packets to */ + uint16_t remoteport; + char peerhost[256]; /* receive packets from */ + uint16_t peerport; + uint16_t localport; + int doread; +#ifdef WIN32 + HWND hwnd; + PacketList *packets; + PacketList *packetsTail; + int packetNum; + struct UdpState *next; + Tcl_ThreadId threadId; /* for Tcl_ThreadAlert */ +#endif +} UdpState; + +#endif diff --git a/test/test.tcl b/test/test.tcl new file mode 100755 index 0000000..4fdedd6 --- /dev/null +++ b/test/test.tcl @@ -0,0 +1,27 @@ +#!/home/xiaotaow/tcl83/bin/tclsh + +global gotcha; + +proc handleSock {sock} { + global gotcha; + + set content [gets $sock] + puts "Received $content" + puts "Peer [udp_conf $sock -peer]" + set gotcha 1 +} + +load "../unix/libudp.so" + +set sock [udp_open 5061] +puts "Myport: [udp_conf $sock -myport]" + +if {$sock > 0} { + fileevent $sock readable "handleSock $sock" + udp_conf $sock 204.198.76.59 5061 +# udp_conf $sock minsk.clic.cs.columbia.edu 5060 + puts -nonewline $sock "test" + flush $sock + vwait gotcha + close $sock +} diff --git a/test/test2.tcl b/test/test2.tcl new file mode 100755 index 0000000..aa49087 --- /dev/null +++ b/test/test2.tcl @@ -0,0 +1,25 @@ +#!/home/xiaotaow/tcl83/bin/tclsh + +global gotcha; + +proc handleSock {sock} { + global gotcha; + + set content [udp_read $sock] + puts $content + set gotcha 1 +} + +load "../unix/libudp.so" + +set sock [udp_open 38880] + +if {$sock > 0} { +# udp_conf $sock 204.198.76.55 5061 + udp_conf $sock minsk.clic.cs.columbia.edu 5061 + puts "We will send testtest" + puts $sock "testtest" + flush $sock + close $sock +} + diff --git a/test/testmcast.tcl b/test/testmcast.tcl new file mode 100644 index 0000000..a466028 --- /dev/null +++ b/test/testmcast.tcl @@ -0,0 +1,29 @@ +global gotcha; + +proc handleSock {sock} { + global gotcha; + set content [read $sock] + puts "Received $content" +# set gotcha 1 + puts [udp_conf $sock -peer] +} + +load "../unix/libudp.so" + +# Since the socket is bound to the local +# port when it is created then we must set +# this value to the port number that is used +# by the multicast group. For instance foe SAP +# announcements this should be 9875 +set sock [udp_open 9875] + +# joins the multicast group +udp_conf $sock -mcastadd 224.2.127.254 +if {$sock > 0} { + fileevent $sock readable "handleSock $sock" + vwait gotcha + # leaves the multicast group + udp_conf $sock -mcastdrop 224.2.127.254 + close $sock +} + diff --git a/test/testmcast2.tcl b/test/testmcast2.tcl new file mode 100644 index 0000000..6c7b7f7 --- /dev/null +++ b/test/testmcast2.tcl @@ -0,0 +1,23 @@ +global gotcha; + +load "../unix/libudp.so" + +# Since the socket is bound to the local +# port when it is created then we must set +# this value to the port number that is used +# by the multicast group. For instance foe SAP +# announcements this should be 9875 +set sock [udp_open 9875] + +# joins the multicast group +udp_conf $sock 224.2.127.254 9875 +udp_conf $sock -mcastadd 224.2.127.254 +if {$sock > 0} { + puts "Will send testtest" + puts $sock "testtest" + flush $sock + # leaves the multicast group + udp_conf $sock -mcastdrop 224.2.127.254 + close $sock +} +