Transmisie Voip Utilizand Rtp Si Udp cu Aplicatie

Cuprins

+ aplicatia

=== Anexa A(a) ===

Anexa A – Diagrama generală de funcționare a aplicației

NU DA

NU DA

NU DA

=== Anexa A(b) ===

Anexa A – Diagrama de funcționare a clasei RecordSound

NU DA

NU DA

=== Anexa B_Listarea_aplicatiei ===

Anexa B

CerereDlg.h

if !defined(AFX_CEREREDLG_H__5835B7F7_EB48_4A66_BBA6_A2702CFA4CA9__INCLUDED_)

#define AFX_CEREREDLG_H__5835B7F7_EB48_4A66_BBA6_A2702CFA4CA9__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

/////////////////////////////////////////////////////////////////////////////

// CCerereDlg dialog

class CCerereDlg : public CDialog

{

// Construction

public:

char rname[500];

char raddress[500];

CDialog *pdlg;

CCerereDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data

//{{AFX_DATA(CCerereDlg)

enum { IDD = IDD_DIALOG2 };

// NOTE: the ClassWizard will add data members here

//}}AFX_DATA

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CCerereDlg)

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

//}}AFX_VIRTUAL

// Implementation

public:

// Generated message map functions

//{{AFX_MSG(CCerereDlg)

afx_msg BOOL OnInitDialog();

afx_msg void OnAccept();

afx_msg void OnReject();

afx_msg void SetParameter(char*hostname,char*hostaddress,CDialog *dlg) ;

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

//{{AFX_INSERT_LOCATION}}

#endif // !defined(AFX_CEREREDLG_H__5835B7F7_EB48_4A66_BBA6_A2702CFA4CA9__INCLUDED_)

ConectareDlg.h

#if !defined(AFX_CONECTAREDLG_H__DD7E91E6_FBA5_4979_84C0_97B29B280BE1__INCLUDED_)

#define AFX_CONECTAREDLG_H__DD7E91E6_FBA5_4979_84C0_97B29B280BE1__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

/////////////////////////////////////////////////////////////////////////////

// CConectareDlg dialog

class CConectareDlg : public CDialog

{

public:

CDialog *mdlg;

// Construction

public:

CConectareDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data

//{{AFX_DATA(CConectareDlg)

enum { IDD = IDD_DIALOG1 };

//}}AFX_DATA

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CConectareDlg)

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

//}}AFX_VIRTUAL

// Implementation

public:

// Generated message map functions

//{{AFX_MSG(CConectareDlg)

afx_msg void SetParent(CDialog *dlg);

afx_msg void OnConnect();

virtual void OnCancel();

afx_msg void OnScanare();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

//{{AFX_INSERT_LOCATION}}

#endif // !defined(AFX_CONECTAREDLG_H__DD7E91E6_FBA5_4979_84C0_97B29B280BE1__INCLUDED_)

DSocket.h

#if !defined(AFX_DSOCKET_H__B96E2917_9240_452E_9FAB_5904238E70C7__INCLUDED_)

#define AFX_DSOCKET_H__B96E2917_9240_452E_9FAB_5904238E70C7__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#define TYPE_CONTROL 11

#define TYPE_AUDIO 12

#define PORT_CONTROL 30001

#define PORT_AUDIO 30000

// Message Types…

#define MESG_CONNECT 101

#define MESG_DISCONNECT 102

#define MESG_ACCEPT 103

#define MESG_REJECT 104

/////////////////////////////////////////////////////////////////////////////

// DSocket command target

class DSocket : public CSocket

{

public:

CDialog* pdlg;

char localname[200];

static char remotename[500];

static char remoteaddress[500];

int type,x,y;

static unsigned char data[2000],adata[5000];

static unsigned int length,alength;

// Operations

public:

DSocket();

virtual ~DSocket();

void CreateSocket(int port,int type);

void SetParent(CDialog *dlg);

void OnReceive(int errcode);

void SendControlMessage(int type,char *address);

void SendAudioData(unsigned char *data,int length);

void CloseSocket();

};

//{{AFX_INSERT_LOCATION}}

#endif // !defined(AFX_DSOCKET_H__B96E2917_9240_452E_9FAB_5904238E70C7__INCLUDED_)

MibAccess.h

#ifndef _SNMP_ACCESS_H_

#define _SNMP_ACCESS_H_

#include <snmp.h>

//////////////////////////////////////////////////////////////

// Definition of pointers to the four functions in the Mib Dll

//

typedef BOOL (WINAPI *pSnmpExtensionInit)(IN DWORD dwTimeZeroReference, OUT HANDLE *hPollForTrapEvent, OUT AsnObjectIdentifier *supportedView);

typedef BOOL (WINAPI *pSnmpExtensionTrap)(OUT AsnObjectIdentifier *enterprise, OUT AsnInteger *genericTrap, OUT AsnInteger *specificTrap, OUT AsnTimeticks *timeStamp, OUT RFC1157VarBindList *variableBindings);

typedef BOOL (WINAPI *pSnmpExtensionQuery)(IN BYTE requestType, IN OUT RFC1157VarBindList *variableBindings, OUT AsnInteger *errorStatus, OUT AsnInteger *errorIndex);

typedef BOOL (WINAPI *pSnmpExtensionInitEx)(OUT AsnObjectIdentifier *supportedView);

typedef struct

{

long type;

BYTE MACLength;

BYTE MAC[14];

BYTE IP[4];

BYTE SubnetMask[4];

BYTE Description[128];

} tSTRUCTNICINFO;

#define ERROR_MIB_DLL -1

#define ERROR_MIB_WINSOCK -2

#define ERROR_MIB_INIT -3

class MibExtLoad

{

public:

MibExtLoad(LPSTR MibDllName);

~MibExtLoad();

BOOL Init(DWORD dwTimeZeroReference, HANDLE *hPollForTrapEvent, AsnObjectIdentifier *supportedView);

BOOL InitEx(AsnObjectIdentifier *supportedView);

BOOL Query(BYTE requestType, OUT RFC1157VarBindList *variableBindings, AsnInteger *errorStatus, AsnInteger *errorIndex);

BOOL Trap(AsnObjectIdentifier *enterprise, AsnInteger *genericTrap, AsnInteger *specificTrap, AsnTimeticks *timeStamp, RFC1157VarBindList *variableBindings);

BOOL GetDLLStatus();

private:

HINSTANCE m_hInst;

pSnmpExtensionInit m_Init;

pSnmpExtensionInitEx m_InitEx;

pSnmpExtensionQuery m_Query;

pSnmpExtensionTrap m_Trap;

};

class MibII: public MibExtLoad

{

public:

MibII();

~MibII();

int Init();

UINT GetNICCount(BOOL bDialup, BOOL bLoopback);

void GetNICInfo(tSTRUCTNICINFO *pNICInfo);

private:

int m_rvWSA;

UINT m_ifCount;

DWORD *m_ifIndex;

DWORD *m_ifEntryNum;

tSTRUCTNICINFO *m_pNICInfo;

BOOL m_bDialup;

BOOL m_bLoopback;

void MatchNICEntries(UINT NICCount, tSTRUCTNICINFO *pNICInfo);

};

#endif

PlaySound.h

#if !defined(AFX_PlaySound_H__676E24A1_615E_11D6_889A_000B2B0F84B6__INCLUDED_)

#define AFX_PlaySound_H__676E24A1_615E_11D6_889A_000B2B0F84B6__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#define WM_PLAYSOUND_STARTPLAYING WM_USER+600

#define WM_PLAYSOUND_STOPPLAYING WM_USER+601

#define WM_PLAYSOUND_PLAYBLOCK WM_USER+602

#define WM_PLAYSOUND_ENDTHREAD WM_USER+603

#define SOUNDSAMPLES 1000

#define PLAYBUFFER 160

#define SAMPLEPSEC 8000

#include<afxmt.h>

#include<mmsystem.h>

class PlaySound1 : public CWinThread

{

DECLARE_DYNCREATE(PlaySound1)

public:

WAVEFORMATEX m_WaveFormatEx;

BOOL Playing;

HWAVEOUT m_hPlay;

CDialog *dlg;

PlaySound1();

PlaySound1(CDialog *dlg);

virtual ~PlaySound1();

BOOL InitInstance();

int ExitInstance();

void displayError(int code,char []);

void displayHeader(LPWAVEHDR lphdr);

LPWAVEHDR CreateWaveHeader(CString mesg);

void ProcessSoundData(short int *sound, DWORD dwSamples);

void GetDevProperty();

afx_msg LRESULT OnStartPlaying(WPARAM wParam, LPARAM lParam);

afx_msg LRESULT OnStopPlaying(WPARAM wParam, LPARAM lParam);

afx_msg LRESULT OnEndPlaySoundData(WPARAM wParam, LPARAM lParam);

afx_msg LRESULT OnWriteSoundData(WPARAM wParam, LPARAM lParam);

afx_msg LRESULT OnEndThread(WPARAM wParam, LPARAM lParam);

DECLARE_MESSAGE_MAP()

};

#endif // !defined(AFX_PlaySound_H__676E24A1_615E_11D6_889A_000B2B0F84B6__INCLUDED_)

RecordSound.h

if !defined(AFX_RECORDSOUND_H__CB6358C7_4594_47D6_8573_A745760325C1__INCLUDED_)

#define AFX_RECORDSOUND_H__CB6358C7_4594_47D6_8573_A745760325C1__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// RecordSound.h : header file

//

#define WM_RECORDSOUND_STARTRECORDING WM_USER+500

#define WM_RECORDSOUND_STOPRECORDING WM_USER+501

#define WM_RECORDSOUND_ENDTHREAD WM_USER+502

#define SAMPLERSEC 8000

#define MAXRECBUFFER 12

#define RECBUFFER 160

#include<mmsystem.h>

#include<mmreg.h>

/////////////////////////////////////////////////////////////////////////////

// CRecordSound thread

class CRecordSound : public CWinThread

{

DECLARE_DYNCREATE(CRecordSound)

CDialog *dlg;

HWAVEIN m_hRecord;

WAVEFORMATEX m_WaveFormatEx;

BOOL recording;

int isallocated;

LPWAVEHDR rechead[MAXRECBUFFER];

public:

CRecordSound(); // protected constructor used by dynamic creation

CRecordSound(CDialog *dlg);

virtual ~CRecordSound();

void GetDevProperty();

void PreCreateHeader();

void displayError(int errcode,char []);

afx_msg LRESULT OnStartRecording(WPARAM wp,LPARAM lp);

afx_msg LRESULT OnStopRecording(WPARAM wp,LPARAM lp);

afx_msg LRESULT OnEndThread(WPARAM wp,LPARAM lp);

LRESULT OnSoundData(WPARAM wParam, LPARAM lParam);

LPWAVEHDR CreateWaveHeader();

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CRecordSound)

public:

virtual BOOL InitInstance();

virtual int ExitInstance();

DECLARE_MESSAGE_MAP()

};

//{{AFX_INSERT_LOCATION}}

#endif // !defined(AFX_RECORDSOUND_H__CB6358C7_4594_47D6_8573_A745760325C1__INCLUDED_)

rtp1.h

include <sys/types.h>

typedef unsigned char u_int8;

typedef unsigned short u_int16;

typedef unsigned int u_int32;

typedef short int16;

#define RTP_VERSION 2

#define RTP_SEQ_MOD (1<<16)

#define RTP_MAX_SDES 256

typedef enum {

RTCP_SR = 200,

RTCP_RR = 201,

RTCP_SDES = 202,

RTCP_BYE = 203,

RTCP_APP = 204

} rtcp_type_t;

typedef enum {

RTCP_SDES_END = 0,

RTCP_SDES_CNAME = 1,

RTCP_SDES_NAME = 2,

RTCP_SDES_EMAIL = 3,

RTCP_SDES_PHONE = 4,

RTCP_SDES_LOC = 5,

RTCP_SDES_TOOL = 6,

RTCP_SDES_NOTE = 7,

RTCP_SDES_PRIV = 8

} rtcp_sdes_type_t;

typedef struct {

unsigned char cc:4; /*CSRC count*/

unsigned char x:1; /*header extension flag*/

unsigned char p:1; /*padding flag*/

unsigned char version:2; /*RTP version*/

unsigned char pt:7; /*payload type*/

unsigned char m:1; /*marker bit*/

u_int32 ssrc; /* synchronization source */

u_int32 ts; /* timestamp */

u_int16 seq; /* sequence number */

} rtp_hdr_t;

typedef struct {

unsigned int version:2; /* protocol version */

unsigned int p:1; /* padding flag */

unsigned int count:5; /* varies by packet type */

unsigned int pt:8; /* RTCP packet type */

u_int16 length; /* pkt len in words, w/o this word */

} rtcp_common_t;

/*

* Big-endian mask for version, padding bit and packet type pair

*/

#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)

#define RTCP_VALID_VALUE ((RTP_VERSION << 14) | RTCP_SR)

typedef struct {

u_int32 ssrc; /* data source being reported */

unsigned int fraction:8; /* fraction lost since last SR/RR */

int lost:24; /* cumul. no. pkts lost (signed!) */

u_int32 last_seq; /* extended last seq. no. received */

u_int32 jitter; /* interarrival jitter */

u_int32 lsr; /* last SR packet from this source */

u_int32 dlsr; /* delay since last SR packet */

} rtcp_rr_t;

typedef struct {

u_int8 type; /* type of item (rtcp_sdes_type_t) */

u_int8 length; /* length of item (in octets) */

char data[1]; /* text, not null-terminated */

} rtcp_sdes_item_t;

typedef struct {

rtcp_common_t common; /* common header */

union {

/* sender report (SR) */

struct {

u_int32 ssrc; /* sender generating this report */

u_int32 ntp_sec; /* NTP timestamp */

u_int32 ntp_frac;

u_int32 rtp_ts; /* RTP timestamp */

u_int32 psent; /* packets sent */

u_int32 osent; /* octets sent */

rtcp_rr_t rr[1]; /* variable-length list */

} sr;

/* reception report (RR) */

struct {

u_int32 ssrc; /* receiver generating this report */

rtcp_rr_t rr[1]; /* variable-length list */

} rr;

/* source description (SDES) */

struct rtcp_sdes {

u_int32 src; /* first SSRC/CSRC */

rtcp_sdes_item_t item[1]; /* list of SDES items */

} sdes;

/* BYE */

struct {

u_int32 src[1]; /* list of sources */

/* can't express trailing text for reason */

} bye;

} r;

} rtcp_t;

typedef struct rtcp_sdes rtcp_sdes_t;

typedef struct {

u_int16 max_seq; /* highest seq. number seen */

u_int32 cycles; /* shifted count of seq. number cycles */

u_int32 base_seq; /* base seq number */

u_int32 bad_seq; /* last 'bad' seq number + 1 */

u_int32 probation; /* sequ. packets till source is valid */

u_int32 received; /* packets received */

u_int32 expected_prior; /* packet expected at last interval */

u_int32 received_prior; /* packet received at last interval */

u_int32 transit; /* relative trans time for prev pkt */

u_int32 jitter; /* estimated jitter */

/* … */

} source;

typedef struct {

rtp_hdr_t z;

unsigned char date[160];

} rtp_packet;

Transmisie.h

#if !defined(AFX_TRANSMISIE_H__1629A95D_6101_4289_B13A_38FB25D5D971__INCLUDED_)

#define AFX_TRANSMISIE_H__1629A95D_6101_4289_B13A_38FB25D5D971__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#ifndef __AFXWIN_H__

#error include 'stdafx.h' before including this file for PCH

#endif

#include "resource.h" // main symbols

/////////////////////////////////////////////////////////////////////////////

// CTransmisieApp:

// See Transmisie.cpp for the implementation of this class

//

class CTransmisieApp : public CWinApp

{

public:

CTransmisieApp();

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CTransmisieApp)

public:

virtual BOOL InitInstance();

//}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CTransmisieApp)

// NOTE – the ClassWizard will add and remove member functions here.

// DO NOT EDIT what you see in these blocks of generated code !

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TRANSMISIE_H__1629A95D_6101_4289_B13A_38FB25D5D971__INCLUDED_)

TransmisieDlg.h

#if !defined(AFX_TRANSMISIEDLG_H__8CC50B80_F4F2_493B_8E34_3A7D1D799799__INCLUDED_)

#define AFX_TRANSMISIEDLG_H__8CC50B80_F4F2_493B_8E34_3A7D1D799799__INCLUDED_

#include "DSocket.h"

#include "ConectareDlg.h"

#include "PlaySound.h"

#include "RecordSound.h"

#include "MibAccess.h"

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

class CTransmisieDlg : public CDialog

{

public:

DSocket dcontrol,daudio;

BOOL isAudio,isStarted;

BOOL isAudioSend,isAudioReceive;

CConectareDlg *conectaredlg;

PlaySound1 *play;

CRecordSound *record;

// Construction

public:

CTransmisieDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data

//{{AFX_DATA(CTransmisieDlg)

enum { IDD = IDD_TRANSMISIE_DIALOG };

int m_iPort;

int m_iPortCtrl;

CString m_strLocal;

CString m_NICInfoDescription;

CString m_NICInfoMAC;

CString m_NICInfoIP;

UINT m_NICInfoNum;

UINT m_NICInfoType;

BOOL m_bDialup;

BOOL m_bLoopback;

CString m_NICInfoSubnetMask;

//}}AFX_DATA

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CTransmisieDlg)

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

//}}AFX_VIRTUAL

// Implementation

public:

void NICRefresh();

void DisplayNICInfo(UINT NICIndex,tSTRUCTNICINFO *pNICInfo);

int GetNICInfo();

tSTRUCTNICINFO *m_pNICInfo;

UINT m_NICCount;

UINT m_NICIndex;

void OnScanareLocal();

HICON m_hIcon;

// Generated message map functions

//{{AFX_MSG(CTransmisieDlg)

virtual BOOL OnInitDialog();

afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

afx_msg void OnPaint();

afx_msg HCURSOR OnQueryDragIcon();

afx_msg void OnExit();

afx_msg void OnDisconnect();

afx_msg void OnBconnect();

afx_msg void StartConference();

afx_msg void DestroyConference();

virtual void OnCancel();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TRANSMISIEDLG_H__8CC50B80_F4F2_493B_8E34_3A7D1D799799__INCLUDED_)

CerereDlg.cpp

#include "stdafx.h"

#include "Transmisie.h"

#include "CerereDlg.h"

#include "TransmisieDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

CCerereDlg::CCerereDlg(CWnd* pParent /*=NULL*/)

: CDialog(CCerereDlg::IDD, pParent)

{

//{{AFX_DATA_INIT(CCerereDlg)

// NOTE: the ClassWizard will add member initialization here

//}}AFX_DATA_INIT

}

void CCerereDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CCerereDlg)

// NOTE: the ClassWizard will add DDX and DDV calls here

//}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CCerereDlg, CDialog)

//{{AFX_MSG_MAP(CCerereDlg)

ON_BN_CLICKED(IDOK, OnAccept)

ON_BN_CLICKED(IDREJECT, OnReject)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

void CCerereDlg::OnAccept()

{

this->OnCancel();

// Se transmite o notificare spre remote host

((CTransmisieDlg*)pdlg)->dcontrol.SendControlMessage(MESG_ACCEPT,NULL);

// Porneste conferinta…

((CTransmisieDlg*)pdlg)->StartConference();

}

void CCerereDlg::OnReject()

{

((CTransmisieDlg*)pdlg)->dcontrol.SendControlMessage(MESG_REJECT,NULL);

CDialog::OnCancel();

}

BOOL CCerereDlg::OnInitDialog()

{

CDialog::OnInitDialog();

char str[600];

sprintf(str,"Cerere de conectare din partea utilizatorului %s ",rname);

this->SetDlgItemText(IDC_MESG,str);

return TRUE;

}

void CCerereDlg::SetParameter(char*hostname,char*hostaddress,CDialog *dlg)

{

strcpy(rname,hostname);

strcpy(raddress,hostaddress);

pdlg=dlg;

}

ConectareDlg.h

#include "stdafx.h"

#include "Transmisie.h"

#include "ConectareDlg.h"

#include "TransmisieDlg.h"

#include "winsock2.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

CConectareDlg::CConectareDlg(CWnd* pParent /*=NULL*/)

: CDialog(CConectareDlg::IDD, pParent)

{

//{{AFX_DATA_INIT(CConectareDlg)

//}}AFX_DATA_INIT

}

void CConectareDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CConectareDlg)

//}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CConectareDlg, CDialog)

//{{AFX_MSG_MAP(CConectareDlg)

ON_BN_CLICKED(IDC_BUTTON1, OnConnect)

ON_BN_CLICKED(IDOK, OnScanare)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

void CConectareDlg::OnConnect()

{

char hostname[200];

this->GetDlgItemText(IDC_EDIT1,hostname,200);

this->OnCancel();

((CTransmisieDlg*)mdlg)->dcontrol.SendControlMessage(MESG_CONNECT,hostname);

}

void CConectareDlg::SetParent(CDialog *dlg)

{

mdlg=dlg;

}

void CConectareDlg::OnCancel()

{

this->SetDlgItemText(IDC_EDIT1,"");

CDialog::OnCancel();

}

void CConectareDlg::OnScanare()

{

CString strTemp;

CListBox *pList = (CListBox*) GetDlgItem(IDC_LIST1);

pList->ResetContent();

struct hostent *host;

struct in_addr *ptr; // Ptr adresa IP

DWORD dwScope = RESOURCE_CONTEXT;

NETRESOURCE *NetResource = NULL;

HANDLE hEnum;

WNetOpenEnum( dwScope, NULL, NULL, NULL, &hEnum );

WSADATA wsaData;

WSAStartup(MAKEWORD(2,0),&wsaData);

if ( hEnum )

{

DWORD Count = 0xFFFFFFFF;

DWORD BufferSize = 50000;

LPVOID Buffer = new char[50000];

WNetEnumResource( hEnum, &Count, Buffer, &BufferSize );

NetResource = (NETRESOURCE*)Buffer;

char szHostName[200];

for ( unsigned int i = 0; i < Count; i++, NetResource++ )

{

if (NetResource->dwUsage == RESOURCEUSAGE_CONTAINER && NetResource-> dwType == RESOURCETYPE_ANY )

{

if ( NetResource->lpRemoteName )

{

CString strFullName = NetResource->lpRemoteName;

if ( 0 == strFullName.Left(2).Compare("\\\\") )

strFullName = strFullName.Right(strFullName.GetLength()-2);

gethostname( szHostName, strlen( szHostName ) );

host = gethostbyname(strFullName);

if (!host)

continue;

ptr = (struct in_addr *) host->h_addr_list[0];

// Ex. 211.40.35.76 se imparte astfel

int a = ptr->S_un.S_un_b.s_b1; // 211

int b = ptr->S_un.S_un_b.s_b2; // 40

int c = ptr->S_un.S_un_b.s_b3; // 35

int d = ptr->S_un.S_un_b.s_b4; // 76

strTemp.Format("%s –> %d.%d.%d.%d",strFullName,a,b,c,d);

pList->AddString(strTemp);

}

}

}

delete Buffer;

WNetCloseEnum( hEnum );

}

WSACleanup();

}

DSocket.cpp

#include "stdafx.h"

#include "Transmisie.h"

#include "DSocket.h"

#include "TransmisieDlg.h"

#include "CerereDlg.h"

#include "rtp1.h"

#include "time.h"

#include "stdlib.h"

#include "malloc.h"

#include "memory.h"

#include "Winbase.h"

#include "Windows.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

char DSocket::remoteaddress[500]="";

char DSocket::remotename[500]="";

unsigned char DSocket::data[2000];

unsigned char DSocket::adata[5000];

unsigned int DSocket::length=2000;

unsigned int DSocket::alength=5000;

rtp_packet r;

DSocket::DSocket()

{

}

DSocket::~DSocket()

{

}

// Do not edit the following lines, which are needed by ClassWizard.

#if 0

BEGIN_MESSAGE_MAP(DSocket, CSocket)

//{{AFX_MSG_MAP(DSocket)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

#endif // 0

void DSocket::CreateSocket(int port,int dtype)

{

x=0;

y=0;

r.z.ts=time(0);

r.z.ssrc=2*time(0);

r.z.cc=0;

r.z.m=0;

r.z.p=0;

r.z.pt=0;

r.z.seq=0;

r.z.version=RTP_VERSION;

r.z.x=0;

this->Create(port,SOCK_DGRAM);

type=dtype;

// Get host name…

gethostname(localname,300);

}

void DSocket::SetParent(CDialog *dlg)

{

pdlg=dlg;

}

void DSocket::OnReceive(int errcode)

{

CString address;

rtp_packet *b;

char hname[400],str[1000];

unsigned int port,retvalue;

CCerereDlg rdlg(NULL);

if(type==TYPE_CONTROL)

{

retvalue=this->ReceiveFrom(data,length,address,port);

((CTransmisieDlg*)pdlg)->SetDlgItemText(IDC_IP_REMOTE,address);

if(retvalue==SOCKET_ERROR)

return;

for(int i=0;i<data[1];i++)

hname[i]=data[i+2];

hname[i]=0;

strcpy(remotename,hname);

strcpy(remoteaddress,(LPCTSTR)address);

switch(data[0])

{

case MESG_CONNECT:

rdlg.SetParameter(remotename,remoteaddress,pdlg);

rdlg.DoModal();

return;

case MESG_DISCONNECT:

((CTransmisieDlg*)pdlg)->DestroyConference();

sprintf(str,"Utilizatorul %s s-a deconectat",hname);

AfxMessageBox(str);

((CTransmisieDlg*)pdlg)->SetDlgItemText(IDC_IP_REMOTE,"");

return;

case MESG_ACCEPT:

AfxMessageBox("Utilizatorul a acceptat conexiunea");

((CTransmisieDlg*)pdlg)->StartConference();

return;

case MESG_REJECT:

sprintf(str,"Utilizatorul %s a refuzat invitatia",hname);

AfxMessageBox(str);

((CTransmisieDlg*)pdlg)->SetDlgItemText(IDC_IP_REMOTE,"");

return;

}

return;

}

if(type==TYPE_AUDIO)

{

retvalue=this->ReceiveFrom(adata,alength,address,port);

if(retvalue==SOCKET_ERROR)

return;

b=(rtp_packet*)malloc(retvalue);

memset(b,0,retvalue);

y=y+1;

((CTransmisieDlg*)pdlg)->SetDlgItemInt(IDC_RECEPTION,y);

memcpy(b,adata,retvalue);

((CTransmisieDlg*)pdlg)->play->PostThreadMessage (WM_PLAYSOUND_PLAYBLOCK, sizeof(b->date),(LPARAM)b->date);

return;

}

}

void DSocket::SendControlMessage(int type,char *address)

{

char data[1000];

int n;

data[0]=type;

n=strlen(localname);

data[1]=n;

memcpy(&data[2],localname,n);

if(address==NULL)

{

SendTo(data,n+2,PORT_CONTROL,remoteaddress);

}

else

{

SendTo(data,n+2,PORT_CONTROL,address);

}

}

void DSocket::SendAudioData(unsigned char *data,int length)

{

memcpy(&(r.date),data,length);

SendTo(&r,sizeof(r),PORT_AUDIO,remoteaddress);

x=x+1;

((CTransmisieDlg*)pdlg)->SetDlgItemInt(IDC_TRANSMISION,x);

r.z.ts+=160;

r.z.seq+=1;

}

void DSocket::CloseSocket()

{

DSocket::Close();

}

MibAccess.cpp

#include <stdafx.h>

#include <winsock2.h>

#include "MibAccess.h"

MibExtLoad::MibExtLoad(LPSTR MibDllName)

{

m_Init = NULL;

m_InitEx = NULL;

m_Query = NULL;

m_Trap = NULL;

m_hInst = LoadLibrary(MibDllName);

if (m_hInst < (HINSTANCE) HINSTANCE_ERROR)

{

m_hInst = NULL;

return;

}

m_Init=(pSnmpExtensionInit)GetProcAddress(m_hInst,"SnmpExtensionInit");

m_InitEx=(pSnmpExtensionInitEx)GetProcAddress(m_hInst, "SnmpExtensionInitEx");

m_Query=(pSnmpExtensionQuery)GetProcAddress(m_hInst,

"SnmpExtensionQuery");

m_Trap=(pSnmpExtensionTrap) GetProcAddress(m_hInst,"SnmpExtensionTrap");

}

MibExtLoad::~MibExtLoad()

{

if (m_hInst)

FreeLibrary(m_hInst);

m_hInst = NULL;

}

BOOL MibExtLoad::Init(DWORD dwTimeZeroReference, HANDLE *hPollForTrapEvent, AsnObjectIdentifier *supportedView)

{

if (m_hInst && m_Init)

return m_Init(dwTimeZeroReference, hPollForTrapEvent, supportedView);

return FALSE;

}

BOOL MibExtLoad::InitEx(AsnObjectIdentifier *supportedView)

{

if (m_hInst && m_InitEx)

return m_InitEx(supportedView);

return FALSE;

}

BOOL MibExtLoad::Query(BYTE requestType, OUT RFC1157VarBindList *variableBindings, AsnInteger *errorStatus, AsnInteger *errorIndex)

{

if (m_hInst && m_Query)

return m_Query(requestType, variableBindings, errorStatus, errorIndex);

return FALSE;

}

BOOL MibExtLoad::Trap(AsnObjectIdentifier *enterprise, AsnInteger *genericTrap, AsnInteger *specificTrap, AsnTimeticks *timeStamp, RFC1157VarBindList *variableBindings)

{

if (m_hInst && m_Trap)

return m_Trap(enterprise, genericTrap, specificTrap, timeStamp, variableBindings);

return FALSE;

}

BOOL MibExtLoad::GetDLLStatus()

{

if (m_hInst == NULL)

return FALSE;

else

return TRUE;

}

MibII::MibII():MibExtLoad("inetmib1.dll")

{

WSADATA wsa;

m_rvWSA = WSAStartup(MAKEWORD(2, 0), &wsa);

}

MibII::~MibII()

{

WSACleanup();

if (m_ifCount > 0)

{

delete m_pNICInfo;

delete m_ifIndex;

delete m_ifEntryNum;

}

}

int MibII::Init()

{

if (!GetDLLStatus())

return ERROR_MIB_DLL;

if (m_rvWSA)

return ERROR_MIB_WINSOCK;

HANDLE PollForTrapEvent;

AsnObjectIdentifier SupportedView;

if (!MibExtLoad::Init(GetTickCount(),&PollForTrapEvent,&SupportedView))

return ERROR_MIB_INIT;

return 0;

}

UINT MibII::GetNICCount(BOOL bDialup, BOOL bLoopback)

{

#define NUM_VARBIND_LIST 7

UINT OID_ifNumEntries[] = {1, 3, 6, 1, 2, 1, 2, 1};

AsnObjectIdentifier MIB_ifNumEntries = {sizeof(OID_ifNumEntries) / sizeof(UINT), OID_ifNumEntries};

UINT OID_ifEntryType[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 3};

AsnObjectIdentifier MIB_ifEntryType = {sizeof(OID_ifEntryType) / sizeof(UINT), OID_ifEntryType};

UINT OID_ifMAC[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 6};

AsnObjectIdentifier MIB_ifMAC = {sizeof(OID_ifMAC) / sizeof(UINT), OID_ifMAC};

UINT OID_ifIPAddr[] = {1, 3, 6, 1, 2, 1, 4, 20, 1, 1};

AsnObjectIdentifier MIB_ifIPAddr = {sizeof(OID_ifIPAddr) / sizeof(UINT), OID_ifIPAddr};

UINT OID_ifSubnetMask[] = {1, 3, 6, 1, 2, 1, 4, 20, 1, 3};

AsnObjectIdentifier MIB_ifSubnetMask = {sizeof(OID_ifSubnetMask) / sizeof(UINT), OID_ifSubnetMask};

UINT OID_ifDesc[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 2};

AsnObjectIdentifier MIB_ifDesc = {sizeof(OID_ifDesc) / sizeof(UINT), OID_ifDesc};

UINT OID_ifIndex[] = {1, 3, 6, 1, 2, 1, 4, 20, 1, 2};

AsnObjectIdentifier MIB_ifIndex = {sizeof(OID_ifIndex) / sizeof(UINT), OID_ifIndex};

UINT OID_ifIPRouteTable[] = {1, 3, 6, 1, 2, 1, 4, 21, 1};

AsnObjectIdentifier MIB_ifIPRouteTable = {sizeof(OID_ifIPRouteTable) / sizeof(UINT), OID_ifIPRouteTable};

UINT OID_ifEntryNum[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 1};

AsnObjectIdentifier MIB_ifEntryNum = {sizeof(OID_ifEntryNum) / sizeof(UINT), OID_ifEntryNum};

RFC1157VarBindList varBindList;

RFC1157VarBind varBind[NUM_VARBIND_LIST];

AsnInteger errorStatus;

AsnInteger errorIndex;

AsnObjectIdentifier MIB_NULL = {0, 0};

int ret;

UINT NICCount = 0, ifIndex = 0, i;

varBindList.list = varBind;

varBindList.len = 1;

SNMP_oidcpy(&varBind[0].name, &MIB_ifNumEntries);

ret = Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus, &errorIndex);

m_ifCount = varBind[0].value.asnValue.number;

if (m_ifCount > 0)

{

m_pNICInfo = new tSTRUCTNICINFO [m_ifCount];

m_ifIndex = new DWORD [m_ifCount];

m_ifEntryNum = new DWORD [m_ifCount];

m_bDialup = bDialup;

m_bLoopback = bLoopback;

}

else

return 0;

SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryType);

SNMP_oidcpy(&varBind[1].name, &MIB_ifMAC);

varBindList.len = NUM_VARBIND_LIST;

SNMP_oidcpy(&varBind[2].name, &MIB_ifIPAddr);

SNMP_oidcpy(&varBind[3].name, &MIB_ifSubnetMask);

SNMP_oidcpy(&varBind[4].name, &MIB_ifDesc);

SNMP_oidcpy(&varBind[5].name, &MIB_ifIndex);

SNMP_oidcpy(&varBind[6].name, &MIB_ifEntryNum);

memset(m_pNICInfo, 0, sizeof(tSTRUCTNICINFO) * m_ifCount);

do

{

ret = Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus, &errorIndex);

if (!ret)

ret = 1;

else

ret=SNMP_oidncmp(&varBind[0].name,&MIB_ifEntryType,

MIB_ifEntryType.idLength);

if (!ret)

{

ret = SNMP_oidncmp(&varBind[1].name, &MIB_ifMAC, MIB_ifMAC.idLength);

if (!ret)

{

NICCount++;

if ((varBind[1].value.asnValue.address.length == 0 && !m_bLoopback) || (varBind[1].value.asnValue.address.length > 0 && varBind[1].value.asnValue.address.stream[0] == 0x44 && varBind[1].value.asnValue.address.stream[1] == 0x45 && varBind[1].value.asnValue.address.stream[2] == 0x53 && varBind[1].value.asnValue.address.stream[3] == 0x54 && !m_bDialup) || (varBind[1].value.asnValue.address.length > 0 && varBind[1].value.asnValue.address.stream[0] == 0x00 && varBind[1].value.asnValue.address.stream[1] == 0x00 && varBind[1].value.asnValue.address.stream[2] == 0x00 && varBind[1].value.asnValue.address.stream[3] == 0x00 && varBind[1].value.asnValue.address.stream[4] == 0x00 && varBind[1].value.asnValue.address.stream[5] == 0x00))

NICCount–;

m_ifIndex[ifIndex] = varBind[5].value.asnValue.number;

m_ifEntryNum[ifIndex] = varBind[6].value.asnValue.number;

m_pNICInfo[ifIndex].type = varBind[0].value.asnValue.number;

m_pNICInfo[ifIndex].MACLength = varBind[1].value.asnValue.address.length;

for (i = 0; i < varBind[1].value.asnValue.address.length; i++)

m_pNICInfo[ifIndex].MAC[i] = varBind[1].value.asnValue.address.stream[i];

if (!SNMP_oidncmp(&varBind[2].name, &MIB_ifIPAddr, MIB_ifIPAddr.idLength))

{

for (i = 0; i < 4; i++)

m_pNICInfo[ifIndex].IP[i]=varBind[2].value.asnValue.address.stream[i];

}

if (!SNMP_oidncmp(&varBind[3].name, &MIB_ifSubnetMask, MIB_ifSubnetMask.idLength))

{

for (i = 0; i < 4; i++)

m_pNICInfo[ifIndex].SubnetMask[i] = varBind[3].value.asnValue.address.stream[i];

}

i = sizeof(m_pNICInfo[ifIndex].Description) – 1;

if (varBind[4].value.asnValue.address.length < i)

i = varBind[4].value.asnValue.address.length;

memcpy(m_pNICInfo[ifIndex].Description, varBind[4].value.asnValue.address.stream, i);

ifIndex++;

}

}

}

while (!ret);

for (i = 0; i < varBindList.len; i++)

SNMP_FreeVarBind(&varBind[i]);

return NICCount;

}

void MibII::GetNICInfo(tSTRUCTNICINFO *pNICInfo)

{

tSTRUCTNICINFO tempStruct;

UINT i, j,validNICIndex = 0;

for (i = 0; i < m_ifCount; i++)

{

memcpy(tempStruct.IP, m_pNICInfo[i].IP, sizeof(tempStruct.IP));

memcpy(tempStruct.SubnetMask, m_pNICInfo[i].SubnetMask, sizeof(tempStruct.SubnetMask));

for (j = 0; j < m_ifCount; j++)

{

if (m_ifIndex[i] == m_ifEntryNum[j])

break;

}

tempStruct.type = m_pNICInfo[j].type;

memcpy(tempStruct.Description, m_pNICInfo[j].Description, sizeof(tempStruct.Description));

tempStruct.MACLength = m_pNICInfo[j].MACLength;

memcpy(tempStruct.MAC, m_pNICInfo[j].MAC, tempStruct.MACLength);

if ((tempStruct.MACLength == 0 && !m_bLoopback) || (tempStruct.MAC[0] == 0x44 && tempStruct.MAC[1] == 0x45 && tempStruct.MAC[2] == 0x53 && tempStruct.MAC[3] == 0x54 && !m_bDialup) || (tempStruct.MAC[0] == 0x00 && tempStruct.MAC[1] == 0x00 && tempStruct.MAC[2] == 0x00 && tempStruct.MAC[3] == 0x00 && tempStruct.MAC[4] == 0x00 && tempStruct.MAC[5] == 0x00))

{

}

else

{

memcpy(&pNICInfo[validNICIndex],&tempStruct,sizeof(tSTRUCTNICINFO));

validNICIndex++;

}

}

}

PlaySound.cpp

#include "stdafx.h"

#include "Transmisie.h"

#include "TransmisieDlg.h"

#include "PlaySound.h"

#include <mmsystem.h>

#include <mmreg.h>

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

IMPLEMENT_DYNCREATE(PlaySound1, CWinThread)

BEGIN_MESSAGE_MAP(PlaySound1, CWinThread)

ON_THREAD_MESSAGE(WM_PLAYSOUND_STARTPLAYING, OnStartPlaying)

ON_THREAD_MESSAGE(WM_PLAYSOUND_STOPPLAYING, OnStopPlaying)

ON_THREAD_MESSAGE(WM_PLAYSOUND_PLAYBLOCK, OnWriteSoundData)

ON_THREAD_MESSAGE(MM_WOM_DONE, OnEndPlaySoundData)

ON_THREAD_MESSAGE(WM_PLAYSOUND_ENDTHREAD,OnEndThread)

END_MESSAGE_MAP()

PlaySound1::PlaySound1()

{

}

PlaySound1::PlaySound1(CDialog *dialog)

{

dlg=dialog;

memset(&m_WaveFormatEx,0x00,sizeof(m_WaveFormatEx));

m_WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;

m_WaveFormatEx.nChannels = 1;

m_WaveFormatEx.wBitsPerSample = 8;

m_WaveFormatEx.cbSize = 0;

m_WaveFormatEx.nSamplesPerSec = SAMPLEPSEC;

m_WaveFormatEx.nAvgBytesPerSec = SAMPLEPSEC ;

m_WaveFormatEx.nBlockAlign = 1;

Playing = FALSE;

}

PlaySound1::~PlaySound1()

{

}

void PlaySound1::GetDevProperty()

{

CString format;

WAVEOUTCAPS wavecap;

int propno[]= {

WAVECAPS_LRVOLUME ,

WAVECAPS_PITCH ,

WAVECAPS_PLAYBACKRATE,

WAVECAPS_SYNC ,

WAVECAPS_VOLUME,

WAVECAPS_SAMPLEACCURATE ,

};

CString propstr[]={

"WAVECAPS_LRVOLUME ",

"WAVECAPS_PITCH ",

"WAVECAPS_PLAYBACKRATE",

"WAVECAPS_SYNC" ,

"WAVECAPS_VOLUME",

"WAVECAPS_SAMPLEACCURATE" ,

};

format.Empty();

format="\nSpecial properties… \n";

for(int j=0;j<6;j++)

{

if( (wavecap.dwSupport & (unsigned)propno[j]) ==(unsigned) propno[j])

{

format+=propstr[j]+"\n";

}

}

}

BOOL PlaySound1::InitInstance()

{

return TRUE;

}

int PlaySound1::ExitInstance()

{

return CWinThread::ExitInstance();

}

LRESULT PlaySound1::OnStartPlaying(WPARAM wParam, LPARAM lParam)

{

MMRESULT mmReturn = 0;

if(Playing)

return FALSE;

mmReturn = ::waveOutOpen( &m_hPlay, WAVE_MAPPER, &m_WaveFormatEx, ::GetCurrentThreadId(), 0, CALLBACK_THREAD);

if(mmReturn )

displayError(mmReturn,"PlayStart");

else

{

Playing = TRUE;

DWORD volume=0xffffffff;

waveOutSetVolume(m_hPlay,volume);

}

return TRUE;

}

void PlaySound1::displayError(int code,char mesg[])

{

char errorbuffer[MAX_PATH];

char errorbuffer1[MAX_PATH];

waveOutGetErrorText( code,errorbuffer,MAX_PATH);

sprintf(errorbuffer1,"PLAY : %s :%x:%s",mesg,code,errorbuffer);

AfxMessageBox(errorbuffer1);

}

LRESULT PlaySound1::OnStopPlaying(WPARAM wParam, LPARAM lParam)

{

MMRESULT mmReturn = 0;

if(Playing==FALSE)

return FALSE;

mmReturn = ::waveOutReset(m_hPlay);

if(!mmReturn)

{

Playing = FALSE;

Sleep(500);

mmReturn = ::waveOutClose(m_hPlay);

}

return mmReturn;

}

LRESULT PlaySound1::OnEndPlaySoundData(WPARAM wParam, LPARAM lParam)

{

LPWAVEHDR lpHdr = (LPWAVEHDR) lParam;

if(lpHdr)

{

::waveOutUnprepareHeader(m_hPlay, lpHdr, sizeof(WAVEHDR));

}

return ERROR_SUCCESS;

}

LRESULT PlaySound1::OnWriteSoundData(WPARAM wParam, LPARAM lParam)

{

MMRESULT mmResult = 0;

int length=(int) wParam;

if(((CTransmisieDlg*)dlg)->isAudioReceive==FALSE)

{

return FALSE;

}

if(Playing==FALSE)

return FALSE;

WAVEHDR *lpHdr=new WAVEHDR;

memset(lpHdr,0,sizeof(WAVEHDR));

lpHdr->lpData=(char *)lParam;

lpHdr->dwBufferLength=length;

mmResult = ::waveOutPrepareHeader(m_hPlay, lpHdr, sizeof(WAVEHDR));

if(mmResult)

{

return ERROR_SUCCESS;

}

mmResult = ::waveOutWrite(m_hPlay, lpHdr, sizeof(WAVEHDR));

if(mmResult)

{

return ERROR_SUCCESS;

}

return ERROR_SUCCESS;

}

LRESULT PlaySound1::OnEndThread(WPARAM wParam, LPARAM lParam)

{

if(Playing==TRUE)

OnStopPlaying(0,0);

::PostQuitMessage(0);

return TRUE;

}

RecordSound.cpp

#include "stdafx.h"

#include "Transmisie.h"

#include "TransmisieDlg.h"

#include "RecordSound.h"

#include<mmsystem.h>

#include<mmreg.h>

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

IMPLEMENT_DYNCREATE(CRecordSound, CWinThread)

BEGIN_MESSAGE_MAP(CRecordSound,CWinThread)

ON_THREAD_MESSAGE(MM_WIM_DATA, OnSoundData)

ON_THREAD_MESSAGE(WM_RECORDSOUND_STARTRECORDING,OnStartRecording)

ON_THREAD_MESSAGE(WM_RECORDSOUND_STOPRECORDING,OnStopRecording)

ON_THREAD_MESSAGE(WM_RECORDSOUND_ENDTHREAD,OnEndThread)

END_MESSAGE_MAP()

CRecordSound::CRecordSound()

{

}

CRecordSound::CRecordSound(CDialog *dialog)

{

dlg=dialog;

recording=FALSE;

isallocated=0; //se aloca memorie ptr wavebuffer

PreCreateHeader();

memset(&m_WaveFormatEx,0x00,sizeof(m_WaveFormatEx));

m_WaveFormatEx.wFormatTag=WAVE_FORMAT_PCM;

m_WaveFormatEx.nChannels=1;

m_WaveFormatEx.wBitsPerSample=8;

m_WaveFormatEx.cbSize=0;

m_WaveFormatEx.nSamplesPerSec=SAMPLERSEC; //22.05 KHz

m_WaveFormatEx.nBlockAlign=1;

m_WaveFormatEx.nAvgBytesPerSec=SAMPLERSEC ; //m_WaveFormatEx.nBlockAlign;

}

CRecordSound::~CRecordSound()

{

if(!isallocated)

return;

for(int i=0;i<MAXRECBUFFER;i++)

{

if(rechead[i])

delete rechead[i];

}

}

BOOL CRecordSound::InitInstance()

{

return TRUE;

}

int CRecordSound::ExitInstance()

{

return CWinThread::ExitInstance();

}

void CRecordSound::PreCreateHeader()

{

for(int i=0;i<MAXRECBUFFER;i++)

rechead[i]=CreateWaveHeader();

isallocated=1;

}

LRESULT CRecordSound::OnStartRecording(WPARAM wp,LPARAM lp)

{

if(recording)

return FALSE;

MMRESULT mmReturn = ::waveInOpen( &m_hRecord, WAVE_MAPPER, &m_WaveFormatEx, ::GetCurrentThreadId(), 0, CALLBACK_THREAD);

if(mmReturn!=MMSYSERR_NOERROR )

{

displayError(mmReturn,"Open");

return FALSE;

}

if(mmReturn==MMSYSERR_NOERROR )

{

for(int i=0; i < MAXRECBUFFER ; i++)

{

mmReturn = ::waveInPrepareHeader(m_hRecord,rechead[i], sizeof(WAVEHDR));

mmReturn = ::waveInAddBuffer(m_hRecord, rechead[i], sizeof(WAVEHDR));

}

mmReturn = ::waveInStart(m_hRecord);

if(mmReturn!=MMSYSERR_NOERROR )

displayError(mmReturn,"Start");

else

recording=TRUE;

}

return TRUE;

}

void CRecordSound::displayError(int mmReturn,char errmsg[])

{

char errorbuffer[MAX_PATH];

char errorbuffer1[MAX_PATH];

waveInGetErrorText( mmReturn,errorbuffer,MAX_PATH);

sprintf(errorbuffer1,"RECORD:%s :%x :%s",errmsg,mmReturn,errorbuffer);

AfxMessageBox(errorbuffer1);

}

LRESULT CRecordSound::OnStopRecording(WPARAM wp,LPARAM lp)

{

MMRESULT mmReturn = 0;

if(!recording)

return FALSE;

mmReturn = ::waveInStop(m_hRecord);

if(!mmReturn)

{

recording = FALSE;

mmReturn = ::waveInReset(m_hRecord);

}

Sleep(500);

if(!mmReturn)

mmReturn = ::waveInClose(m_hRecord);

return mmReturn;

}

LRESULT CRecordSound::OnSoundData(WPARAM wParam, LPARAM lParam)

{

if(recording==FALSE)

return FALSE;

LPWAVEHDR lpHdr = (LPWAVEHDR) lParam;

if(lpHdr->dwBytesRecorded==0 || lpHdr==NULL)

return ERROR_SUCCESS;

::waveInUnprepareHeader(m_hRecord, lpHdr, sizeof(WAVEHDR));

if(lpHdr->lpData!=NULL )

{

((CTransmisieDlg*)dlg )->daudio.SendAudioData((unsigned char*)lpHdr-> lpData,lpHdr-> dwBytesRecorded);

}

if(recording)

{

::waveInPrepareHeader(m_hRecord,lpHdr, sizeof(WAVEHDR));

::waveInAddBuffer(m_hRecord, lpHdr, sizeof(WAVEHDR));

}

return ERROR_SUCCESS;

}

LRESULT CRecordSound::OnEndThread(WPARAM wp,LPARAM lp)

{

if(recording)

OnStopRecording(0,0);

::PostQuitMessage(0);

return TRUE;

}

LPWAVEHDR CRecordSound::CreateWaveHeader()

{

LPWAVEHDR lpHdr = new WAVEHDR;

if(lpHdr==NULL)

{

return NULL;

}

ZeroMemory(lpHdr, sizeof(WAVEHDR));

char* lpByte = new char[RECBUFFER];

if(lpByte==NULL)

{

return NULL;

}

lpHdr->lpData = lpByte;

lpHdr->dwBufferLength =RECBUFFER;

return lpHdr;

}

void CRecordSound::GetDevProperty()

{

CString format;

WAVEOUTCAPS wavecap;

int propno[]= {

WAVECAPS_LRVOLUME ,

WAVECAPS_PITCH ,

WAVECAPS_PLAYBACKRATE,

WAVECAPS_SYNC ,

WAVECAPS_VOLUME,

WAVECAPS_SAMPLEACCURATE ,

};

CString propstr[]={

"WAVECAPS_LRVOLUME ",

"WAVECAPS_PITCH ",

"WAVECAPS_PLAYBACKRATE",

"WAVECAPS_SYNC" ,

"WAVECAPS_VOLUME",

"WAVECAPS_SAMPLEACCURATE" ,

};

format.Empty();

format="\nSpecial properties… \n";

for(int j=0;j<6;j++)

{

if( (wavecap.dwSupport & (unsigned)propno[j]) ==(unsigned) propno[j])

{

format+=propstr[j]+"\n";

}

}

}

Snmpapi.cpp

#include <stdafx.h>

#include <snmp.h>

SNMPAPI

SNMP_FUNC_TYPE

SnmpUtilOidCpy(OUT AsnObjectIdentifier *DstObjId, IN AsnObjectIdentifier *SrcObjId)

{

DstObjId->ids = (UINT *)GlobalAlloc(GMEM_ZEROINIT,SrcObjId->idLength *sizeof(UINT));

if(!DstObjId->ids)

{

SetLastError(1);

return 0;

}

memcpy(DstObjId->ids,SrcObjId->ids,SrcObjId->idLength*sizeof(UINT));

DstObjId->idLength = SrcObjId->idLength;

return 1;

}

VOID

SNMP_FUNC_TYPE

SnmpUtilOidFree(IN OUT AsnObjectIdentifier *ObjId)

{

GlobalFree(ObjId->ids);

ObjId->ids = 0;

ObjId->idLength = 0;

}

SNMPAPI

SNMP_FUNC_TYPE

SnmpUtilOidNCmp(IN AsnObjectIdentifier *ObjIdA, IN AsnObjectIdentifier *ObjIdB, IN UINT Len)

{

UINT CmpLen;

UINT i;

int res;

CmpLen = Len;

if(ObjIdA->idLength < CmpLen)

CmpLen = ObjIdA->idLength;

if(ObjIdB->idLength < CmpLen)

CmpLen = ObjIdB->idLength;

for(i=0;i<CmpLen;i++)

{

res = ObjIdA->ids[i] – ObjIdB->ids[i];

if(res!=0)

return res;

}

return 0;

}

VOID

SNMP_FUNC_TYPE

SnmpUtilVarBindFree( IN OUT RFC1157VarBind *VarBind )

{

BYTE asnType;

// Elibereaza numele obiectului

SnmpUtilOidFree(&VarBind->name);

asnType = VarBind->value.asnType;

if(asnType==ASN_OBJECTIDENTIFIER)

{

SnmpUtilOidFree(&VarBind->value.asnValue.object);

}

else

if((asnType==ASN_OCTETSTRING) || (asnType==ASN_RFC1155_IPADDRESS) || (asnType==ASN_RFC1155_OPAQUE) || (asnType==ASN_SEQUENCE))

{

if(VarBind->value.asnValue.string.dynamic){

GlobalFree(VarBind->value.asnValue.string.stream);

}

}

VarBind->value.asnType = ASN_NULL;

}

Transmisie.cpp

#include "stdafx.h"

#include "Transmisie.h"

#include "TransmisieDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

BEGIN_MESSAGE_MAP(CTransmisieApp, CWinApp)

//{{AFX_MSG_MAP(CTransmisieApp)

// NOTE – the ClassWizard will add and remove mapping macros here.

// DO NOT EDIT what you see in these blocks of generated code!

//}}AFX_MSG

ON_COMMAND(ID_HELP, CWinApp::OnHelp)

END_MESSAGE_MAP()

CTransmisieApp::CTransmisieApp()

{

// TODO: add construction code here,

// Place all significant initialization in InitInstance

}

/////////////////////////////////////////////////////////////////////////////

// The one and only CTransmisieApp object

CTransmisieApp theApp;

/////////////////////////////////////////////////////////////////////////////

// CTransmisieApp initialization

BOOL CTransmisieApp::InitInstance()

{

if (!AfxSocketInit())

{

AfxMessageBox(IDP_SOCKETS_INIT_FAILED);

return FALSE;

}

AfxEnableControlContainer();

// Standard initialization

// If you are not using these features and wish to reduce the size

// of your final executable, you should remove from the following

// the specific initialization routines you do not need.

#ifdef _AFXDLL

Enable3dControls(); // Call this when using MFC in a shared DLL

#else

Enable3dControlsStatic(); // Call this when linking to MFC statically

#endif

CTransmisieDlg dlg;

m_pMainWnd = &dlg;

int nResponse = dlg.DoModal();

if (nResponse == IDOK)

{

// TODO: Place code here to handle when the dialog is

// dismissed with OK

}

else if (nResponse == IDCANCEL)

{

// TODO: Place code here to handle when the dialog is

// dismissed with Cancel

}

// Since the dialog has been closed, return FALSE so that we exit the

// application, rather than start the application's message pump.

return FALSE;

}

TransmisieDlg.cpp

include "stdafx.h"

#include "Transmisie.h"

#include "TransmisieDlg.h"

#include "Resource.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

class CAboutDlg : public CDialog

{

public:

CAboutDlg();

// Dialog Data

//{{AFX_DATA(CAboutDlg)

enum { IDD = IDD_ABOUTBOX };

//}}AFX_DATA

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CAboutDlg)

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

//}}AFX_VIRTUAL

// Implementation

protected:

//{{AFX_MSG(CAboutDlg)

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)

{

//{{AFX_DATA_INIT(CAboutDlg)

//}}AFX_DATA_INIT

}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CAboutDlg)

//}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)

//{{AFX_MSG_MAP(CAboutDlg)

// No message handlers

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

CTransmisieDlg::CTransmisieDlg(CWnd* pParent /*=NULL*/)

: CDialog(CTransmisieDlg::IDD, pParent)

{

//{{AFX_DATA_INIT(CTransmisieDlg)

m_iPort = 0;

m_iPortCtrl = 0;

m_strLocal = _T("");

m_NICInfoDescription = _T("");

m_NICInfoMAC = _T("");

m_NICInfoSubnetMask = _T("");

//}}AFX_DATA_INIT

// Note that LoadIcon does not require a subsequent DestroyIcon in Win32

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

void CTransmisieDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CTransmisieDlg)

DDX_Text(pDX, IDC_ESERVPORT, m_iPort);

DDX_Text(pDX, IDC_PORTCTRL, m_iPortCtrl);

DDX_Text(pDX, IDC_LOCAL, m_strLocal);

DDX_Text(pDX, IDC_NIC_INFO_DESCRIPTION, m_NICInfoDescription);

DDX_Text(pDX, IDC_NIC_INFO_MAC, m_NICInfoMAC);

DDX_Text(pDX, IDC_NIC_INFO_SUBNET_MASK, m_NICInfoSubnetMask);

//}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CTransmisieDlg, CDialog)

//{{AFX_MSG_MAP(CTransmisieDlg)

ON_WM_SYSCOMMAND()

ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

ON_BN_CLICKED(IDC_EXIT, OnExit)

ON_BN_CLICKED(IDC_DISCONNECT, OnDisconnect)

ON_BN_CLICKED(IDC_BCONNECT, OnBconnect)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

BOOL CTransmisieDlg::OnInitDialog()

{

CDialog::OnInitDialog();

// Add "About…" menu item to system menu.

// IDM_ABOUTBOX must be in the system command range.

ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);

if (pSysMenu != NULL)

{

CString strAboutMenu;

strAboutMenu.LoadString(IDS_ABOUTBOX);

if (!strAboutMenu.IsEmpty())

{

pSysMenu->AppendMenu(MF_SEPARATOR);

pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

}

}

// Set the icon for this dialog. The framework does this automatically

// when the application's main window is not a dialog

SetIcon(m_hIcon, TRUE); // Set big icon

SetIcon(m_hIcon, FALSE); // Set small icon

// TODO: Add extra initialization here

m_NICIndex = 0;

m_pNICInfo = NULL;

NICRefresh();

OnScanareLocal();

GetDlgItem(IDC_BCONNECT)->EnableWindow(TRUE);

GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);

m_iPort=PORT_AUDIO;

m_iPortCtrl=PORT_CONTROL;

UpdateData(FALSE);

record=new CRecordSound(this);

record->CreateThread();

play=new PlaySound1(this);

play->CreateThread();

isAudioSend=TRUE;

isAudioReceive=TRUE;

conectaredlg=new CConectareDlg(NULL);

conectaredlg->SetParent(this);

dcontrol.SetParent(this);

daudio.SetParent(this);

dcontrol.CreateSocket(PORT_CONTROL,TYPE_CONTROL);

isStarted=FALSE;

return TRUE; // return TRUE unless you set the focus to a control

}

void CTransmisieDlg::OnSysCommand(UINT nID, LPARAM lParam)

{

if ((nID & 0xFFF0) == IDM_ABOUTBOX)

{

CAboutDlg dlgAbout;

dlgAbout.DoModal();

}

else

{

CDialog::OnSysCommand(nID, lParam);

}

}

void CTransmisieDlg::OnPaint()

{

if (IsIconic())

{

CPaintDC dc(this); // device context for painting

SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

int cxIcon = GetSystemMetrics(SM_CXICON);

int cyIcon = GetSystemMetrics(SM_CYICON);

CRect rect;

GetClientRect(&rect);

int x = (rect.Width() – cxIcon + 1) / 2;

int y = (rect.Height() – cyIcon + 1) / 2;

dc.DrawIcon(x, y, m_hIcon);

}

else

{

CDialog::OnPaint();

}

}

HCURSOR CTransmisieDlg::OnQueryDragIcon()

{

return (HCURSOR) m_hIcon;

}

void CTransmisieDlg::OnExit()

{

int iResults;

iResults=MessageBox("Doriti sa iesiti?","IESIRE",MB_YESNOCANCEL | MB_ICONINFORMATION);

switch (iResults)

{

case IDYES:

{

if (isStarted)

{

dcontrol.SendControlMessage(MESG_DISCONNECT,NULL);

DestroyConference();

if (m_pNICInfo)

delete m_pNICInfo;

OnOK();

}

else

{

if (isStarted)

dcontrol.SendControlMessage(MESG_DISCONNECT,NULL);

OnOK();

}

}

break;

}

UpdateData(FALSE);

}

void CTransmisieDlg::OnDisconnect()

{

dcontrol.SendControlMessage(MESG_DISCONNECT,NULL);

DestroyConference();

SetDlgItemText(IDC_IP_REMOTE,"");

}

void CTransmisieDlg::OnBconnect()

{

conectaredlg->DoModal();

}

void CTransmisieDlg::StartConference()

{

if (isStarted)

return;

isStarted=TRUE;

isAudioSend=TRUE;

isAudioReceive=TRUE;

GetDlgItem(IDC_BCONNECT)->EnableWindow(FALSE);

GetDlgItem(IDC_DISCONNECT)->EnableWindow(TRUE);

UpdateData(FALSE);

daudio.CreateSocket(PORT_AUDIO,TYPE_AUDIO);

record->PostThreadMessage(WM_RECORDSOUND_STARTRECORDING,0,0);

play->PostThreadMessage(WM_PLAYSOUND_STARTPLAYING,0,0);

}

void CTransmisieDlg::DestroyConference()

{

if (isStarted==FALSE)

return;

isStarted=FALSE;

GetDlgItem(IDC_BCONNECT)->EnableWindow(TRUE);

GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);

SetDlgItemText(IDC_EDIT1,"");

if(isAudioSend)

record->SuspendThread();

record->PostThreadMessage(WM_RECORDSOUND_STOPRECORDING,0,0);

daudio.CloseSocket();

play->PostThreadMessage(WM_PLAYSOUND_STOPPLAYING,0,0);

record->ResumeThread();

isAudioSend=TRUE;

isAudioReceive=TRUE;

}

void CTransmisieDlg::OnCancel()

{

if(isStarted)

DestroyConference();

dcontrol.CloseSocket();

record->PostThreadMessage(WM_RECORDSOUND_ENDTHREAD,0,0);

play->PostThreadMessage(WM_PLAYSOUND_ENDTHREAD,0,0);

CDialog::OnCancel();

}

void CTransmisieDlg::OnScanareLocal()

{

WORD wVersionRequested;

WSADATA wsaData;

char name[255];

CString ip;

PHOSTENT hostinfo;

wVersionRequested = MAKEWORD( 2, 0 );

if (WSAStartup( wVersionRequested, &wsaData ) == 0 )

{

if( gethostname ( name, sizeof(name)) == 0)

{

if((hostinfo = gethostbyname(name)) != NULL)

{

ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);

m_strLocal=ip;

}

}

WSACleanup( );

}

}

int CTransmisieDlg::GetNICInfo()

{

MibII m;

BOOL m_bDialup = FALSE;

BOOL m_bLoopback = FALSE;

int rv = m.Init();

if (rv < 0)

return rv;

UpdateData();

m_NICCount = m.GetNICCount(m_bDialup, m_bLoopback);

if (m_NICCount > 0)

{

if (m_pNICInfo)

delete m_pNICInfo;

m_pNICInfo = new tSTRUCTNICINFO [m_NICCount];

m.GetNICInfo(m_pNICInfo);

}

return m_NICCount;

}

void CTransmisieDlg::DisplayNICInfo(UINT NICIndex, tSTRUCTNICINFO *pNICInfo)

{

BYTE *p, buf[128];

CString m_NICInfoIP = _T("");

m_NICInfoNum = NICIndex;

m_NICInfoDescription = pNICInfo->Description;

m_NICInfoSubnetMask.Format("%d.%d.%d.%d", pNICInfo->SubnetMask[0], pNICInfo-> SubnetMask[1], pNICInfo->SubnetMask[2], pNICInfo-> SubnetMask[3]);

memset(buf, 0, sizeof(buf));

p = buf;

for (UINT i = 0; i < pNICInfo->MACLength; i++)

{

sprintf((char *) p, "%02X-", pNICInfo->MAC[i]);

p += 3;

}

if (pNICInfo->MACLength > 0)

*(p – 1) = 0;

m_NICInfoMAC.Format("%s", buf);

m_NICInfoType = pNICInfo->type;

UpdateData(FALSE);

}

void CTransmisieDlg::NICRefresh()

{

int rv = GetNICInfo();

if (rv > 0)

{

m_NICIndex = 0;

DisplayNICInfo(m_NICIndex, &m_pNICInfo[m_NICIndex]);

}

}

=== Capitolul 1 ===

Capitolul 1

Principiul de transmisie a semnalelor audio prin TCP/IP

1.1. Modelul de referință TCP/IP

TCP/IP este un protocol de comunicații de tip software folosit în rețelele de calculatoare și Internet. Deși numele, TCP/IP, implică faptul că acesta este doar o combinație de două protocoale, TCP (Transmission Control Protocol) și IP (Internet Protocol), termenul TCP/IP nu se referă la o singură entitate ce combină două protocoale, ci la un set mai mare de programe software ce furnizează servicii pentru rețea precum transferul de fișiere la distanță și poșta electronică. Ca orice protocol de comunicații, TCP/IP tratează erorile de transmisie, administrează calea de transmisie și furnizarea datelor, controlând transmisia propriu-zisă prin semnale de stare predefinite.

Arhitectura modelului TCP/IP derivă din arhitectura modelului de referință OSI (Open Systems Interconnect Reference Model), dezvoltat de International Standards Organization (ISO). Modelul OSI este structurat pe șapte nivele numite layere: Aplicație, Prezentare, Sesiune, Transport, Rețea, Legătură de date și Fizic. Informația parcurgea aceste nivele în ordine, suportând transformări de la un nivel la altul, după care este transmisă prin mediul de transmisie. Atunci când au fost adăugate, mai târziu, rețelele prin satelit și radio, interconectarea acestora prin protocoalele existente a pus diferite probleme.

O cerință pentru TCP/IP a fost aceea ca atât timp cât funcționează calculatorul sursă și calculatorul destinație, conexiunile să rămână intacte, indiferent dacă o parte din echipamente sau liniile de transmisiune erau scoase brusc din funcțiune. Mai mult, era nevoie de o arhitectură flexibilă, deoarece se aveau în vedere aplicații cu cerințe divergente, mergând de la transferul de fișiere până la transmiterea vorbirii în timp real.

Pentru o mai mare flexibilitate, modelul de referință TCP/IP conține doar patru nivele, în loc de șapte nivele ca în modelul OSI. Acestea sunt: Aplicație, Transport, Internet și Acces la rețea.

Modelul OSI Modelul TCP/IP

Figura 1.1 – Comparație între modelele OSI și TCP/IP

1.1.1 Nivelul Acces la rețea

Nivelul Acces la rețea este nivelul cel mai de jos în ierarhia protocolului TCP/IP. Protocoalele din acest nivel furnizează mijloacele necesare ca un sistem să transmită date spre celelalte echipamente dintr-o rețea legată direct. Definește, de asemenea, cum se folosește rețeaua pentru a transmite o datagrama IP. Spre deosebire de protocoalele de nivel superior, protocoalele nivelului Acces la rețea trebuie să știe toate detaliile rețelei (structura rețelei, adresarea, etc.) pentru a formata corect datele ce urmează a fi transmise în funcție de cerințele rețelei. Nivelul Acces la rețea din modelul TCP/IP poate cuprinde funcțiile tuturor celor trei nivele inferioare din modelul OSI (Retea, Legatură de date și Fizic).

Nivelul Acces la rețea este adesea ignorat de utilizatori. Arhitectura TCP/IP ascunde funcțiile nivelelor inferioare, iar protocoalele cele mai cunoscute (IP, TCP, UDP, etc.) sunt toate de nivel înalt. Datorită noilor tehnologii hardware care apar, noi protocoale ale nivelului Acces la rețea trebuiesc dezvoltate pentru ca noile tehnologii să poată fi folosite în rețelele ce au la bază TCP/IP. În consecință, există multe protocoale de acces – câte unu pentru fiecare standard de rețea fizică.

Funcțiile realizate la acest nivel includ încapsularea datagramelor IP în frame-urile transmise de rețea și maparea adreselor IP în adrese fizice folosite de rețea. Unul din punctele forte ale TCP/IP este schema de adresare universală. Adresa IP trebuie convertită într-o adresă corespunzătoare rețelei fizice prin care se transmite datagrama.

Două exemple de RFC (Remote For Comment) care definesc protocoalele nivelului Acces la rețea sunt:

RFC 826, Adress Resolution Protocol (ARP), mapează adrese IP în adrese Ethernet;

RFC 894, A Standard for the Transmission of IP Datagrams over Ethernet Networks, care specifică modul în care datagramele IP sunt încapsulate și transmise în rețelele Ethernet.

1.1.2. Nivelul Internet

Nivelul Internet este nivelul următor în ierarhia modelului TCP/IP. Protocolul Internet (IP), definit de RFC 791, este “inima” modelului TCP/IP și cel mai important protocol din nivelul Internet. IP-ul furnizează serviciul fundamental pentru livrarea de pachete pe care rețelele bazate pe TCP/IP sunt proiectate. Toate protocoalele, din nivelele superioare și inferioare IP-ului, folosesc protocolul Internet pentru livrarea datelor. Toate fluxurile de date, de intrare sau de ieșire, trec prin IP, indiferent de destinația finală.

1.1.2.1. Protocolul Internet (IP)

Protocolul Internet este blocul de bază al Internetului. Funcțiile sale includ:

Definirea datagramei, care este unitatea fundamentală de transmisie pe Internet;

Definirea modului de adresare a Internetului;

Pasarea datelor între nivelul Acces la rețea și nivelul Transport;

Rutarea datagramelor spre gazde specifice din rețea;

Realizarea fragmentării și reasamblării datagramelor.

Protocolul Internet este un protocol de tip fără conexiune (connectionless protocol), adică nu schimbă informații de control (numite handshake) pentru a stabili o conexiune de tip end-to-end înainte de a transmite informațiile. Acest protocol se bazează pe alte portocoale din celelalte nivele pentru a stabili conexiuni dacă sunt cerute servicii orientate pe conexiune. IP-ul se bazează, de asemenea, pe protocoalele din celelalte nivele pentru furnizarea detecției erorilor și recuperarea datelor în cazul apariției de erori.

Modelul TCP/IP este folosit cu precădere în rețele ce folosesc comutația de pachete. Aceste rețele folosesc informația de adresare din pachete pentru a comuta pachete dintr-o rețea fizică în alta spre destinația finală. Formatul pachetelor din IP este numit datagramă. Formatul datagramelor este prezentat mai jos:

32 de biți

Figura 1.2 –Formatul datagramei IP

Câmpul Versiune memorează cărei versiuni de protocol îi aparține datagrama. Prin includerea unui câmp versiune în fiecare datagramă, devine posibil ca tranziția dintre versiuni să țină luni, sau chiar ani, cu unele calculatoare rulând vechea versiune, iar altele pe cea nouă.

Din moment ce lungimea antetului nu este constantă, un câmp din antet, IHL (Internet Header Length), este pus la dispoziție pentru a spune cât de lung este antetul, în cuvinte de 32 de octeți. Valoarea minimă este 5, care se aplică atunci când nu sunt prezente opțiuni. Valoarea maximă a acestui câmp este 15, ceea ce limitează antetul la 60 de octeți și, astfel, câmpul de opțiuni la 40 de octeți. Pentru unele opțiuni, cum ar fi cea care înregistrează calea pe care a mers un pachet, 40 de octeți sunt mult prea puțini, făcând această opțiune nefolositoare.

Câmpul Tip serviciu permite gazdei să comunice subrețelei ce tip de serviciu dorește. Sunt posibile diferite combinații de fiabilitate și viteză. Pentru vocea digitizată, livrarea rapidă are prioritate față de transmisia corectă. Pentru transferul de fișiere, transmisia fără erori este mult mai importantă decât transmisia rapidă.

Lungimea totală include totul din datagramă – atât antet cât și date. Lungimea maximă este de 65535 octeți. În prezent, această limită superioară este tolerabilă, dar în viitoarele rețele cu capacități de gigaocteți vor fi necesare datagrame mai mari.

Câmpul Identificare este necesar pentru a permite gazdei destinație să determine cărei datagrame aparține un nou pachet primit. Toate fragmentele unei datagrame conțin aceeași valoare de identificare.

Urmează un bit nefolosit și apoi două câmpuri de 1 bit. DF înseamnă Don’t Fragment (A nu se fragmenta). Acesta este un ordin dat ruterelor să nu fragmenteze datagrama pentru că destinația nu este capabilă să asambleze piesele la loc. MF vine de la More Fragments (fragmente adiționale). Toate fragmentele, cu excepția ultimului, au acest bit activat. El este necesar pentru a ști când au ajuns toate fragmentele unei datagrame.

Deplasamentul fragmentului spune unde este locul fragmentului curent în cadrul datagramei. Toate fragmentele dintr-o datagramă, cu excepția ultimului, trebuie să fie un multiplu de 8 octeți – unitatea de fragmentare elementară. Din moment ce sunt prevăzuți 13 biți, există un maxim de 8192 de fragmente pe datagramă, obținându-se o lungime maximă a datagramei de 65536 octeți, cu unul mai mult decât câmpul Lungime totală.

Câmpul Timp de viață este un contor folosit pentru a limita timpul de viață a pachetelor. Este prevăzut să contorizeze timpul în secunde, permițând un timp maxim de viață de 255 de secunde. El trebuie decrementat la fiecare salt (hop – trecere dintr-o rețea în alta) și se presupune că este decrementat de mai multe ori când stă la coadă un timp îndelungat într-un ruter. În practică, el contorizează doar salturile. Când se ajunge la valoarea zero, pachetul este eliminat și se trimite înapoi la gazda sursă un pachet de avertisment. Acestă facilitate previne circulația la infinit a datagramelor, ceea ce se poate întâmpla dacă tabelele de dirijare devin incoerente. Pentru Windows XP, valoarea predefinită este 128.

Când nivelul rețea a asamblat o datagramă completă, are nevoie să știe ce să facă cu ea. Câmpul Protocol spune cărui proces de transport trebuie să-l predea. TCP este o posibilitate, dar tot așa sunt UDP și alte câteva. Numerotarea protocoalelor este globală la nivelul întregului Internet și este definită în RFC 1700.

Suma de control a antetului verifică numai antetul. O astfel de sumă de control este utilă pentru detectarea erorilor generate de locații de memorie proaste din interiorul unui ruter. Algoritmul este de a aduna toate jumătățile de cuvinte, de 16 biți, atunci când acestea sosesc, folosind aritmetică în complement față de 1 și păstrarea complementului față de 1 al rezultatului. Pentru scopul acestui algoritm, suma de control a antetului este presupusă a fi zero după sosire. Acest algoritm este mai robust decât folosirea unei adunări normale. Suma de control a antetului trebuie recalculată la fiecare salt, pentru că cel puțin un câmp se schimbă întotdeauna (câmpul timp de viață), dar se pot folosi trucuri pentru a accelera calculul.

Adresa sursei și Adresa destinației indică numărul de rețea și numărul de gazdă. Despre adresele Internet voi discuta în secțiunea următoare.

Câmpul Opțiuni a fost proiectat pentru a oferi un subterfugiu care să permită versiunilor viitoare ale protocolului să includă informații care nu sunt prezente în proiectul original, pentru a permite cercetătorilor să încerce noi idei și pentru a evita alocarea unor biți din antet pentru informații folosite rar. Opțiunile sunt de lungime variabilă. Fiecare începe cu un cod de octet care identifică opțiunea. Unele opțiuni sunt urmate de un câmp de un octet reprezentând lungimea opțiunii, urmat de unul sau mai mulți octeți de date. Câmpul Opțiuni este completat până la un multiplu de 4 octeți. În acest moment sunt definite cinci opțiuni, dar nu toate ruterele le suportă pe toate.

1.1.2.2. Adrese IP

Fiecare gazdă și ruter din Internet are o adresă IP, care codifică adresa sa de rețea și de gazdă. Combinația este unică: nu există două echipamente cu aceeași adresă IP. Toate adresele IP sunt de 32 de biți lungime și sunt folosite în câmpurile Adresă sursă și Adresă destinație ale pachetelor IP. Formatele folosite pentru adrese IP sunt ilustrate în figura 1.3. Acele mașini care sunt conectate la mai multe rețele au adrese diferite în fiecare rețea.

32 de biți

Clasa A

Clasa B

Clasa C

Clasa D

Clasa E

Figura 1.3 – Formatul adreselor IP

Formatele de clasă A, B, C și D permit până la 126 de rețele cu până la 16 milioane de gazde fiecare, 16382 de rețele cu până la 64K gazde, 2 milioane de rețele (de exemplu LAN-uri) cu până la 254 de gazde fiecare și multicast (trimitere multiplă), în care fiecare datagramă este direcționată mai multor gazde. Adresele care încep cu 11110 sunt rezervate pentru o utilizare ulterioară. Numerele de rețea sunt atribuite de NIC (Network Information Center – Centrul de Informații de Rețea) pentru a evita conflictele.

Adresele de rețea, care sunt numere de 32 de biți, sunt scrise în mod uzual în notația zecimală cu punct. În acest format, fiecare din cei 4 octeți este scris în zecimal, de la 0 la 255. De exemplu, adresa hexazecimală C0290614 este scrisă ca 192.41.6.20. Cea mai mică adresă este 0.0.0.0 și cea mai mare este 255.255.255.255.

Adresa IP 0.0.0.0 este folosită de gazde atunci când sunt pornite, dar nu mai este folosită ulterior. Adresele IP cu 0 ca număr de rețea se referă la rețeaua curentă. Aceste adrese permit ca mașinile să refere propria rețea fără a cunoaște numărul de rețea (dar le trebuie să cunoască clasa adresei pentru a ști câte zerouri să includă). Adresele care constau numai din 1-uri permit difuzarea în rețeaua curentă, în mod uzual un LAN (Local Area Network). Adresele cu un număr exact de rețea și numai 1-uri în câmpul gazdă permit mașinilor să trimită pachete de difuzare în LAN-uri la distanță, aflate oriunde în Internet. În final, toate adresele de forma 127.xx.yy.zz sunt rezervate pentru testări în bucla locală (loopback). Pachetele trimise spre această adresă nu sunt trimise prin cablu; ele sunt prelucrate local și tratate ca pachete sosite. Aceasta permite pachetelor să fie trimise către rețeaua fără ca emițătorul să cunoască numărul său. Această facilitate este folosită de asemenea pentru depanarea programelor de rețea.

1.1.3. Nivelul Transport

Nivelul următor nivelului Internet este nivelul Transport. Există două protocoale mai importante în acest nivel: Transmission Control Protocol (TCP) și User Datagram Protocol (UDP). TCP furnizează servicii de livrare a datelor precise cu detectarea erorilor de tip end-to-end precum și corectarea acestora. UDP furnizează un serviciu de livrare a datagramelor fără a stabili o conexiune și adăugând un antet foarte mic. Ambele protocoale livrează date între nivelul Aplicație și nivelul Internet. Programatorii de aplicații pot alege care serviciu este mai folositor aplicațiilor dezvoltate de ei.

1.1.3.1. UDP (User Datagram Protocol)

UDP (User Datagram Protocol – protocol pentru informația utilizator) oferă aplicațiilor o modalitate de a transmite datagrame IP neprelucrate încapsulate și pe care le transmite fără a stabili o conexiune (prin stabilirea unei conexiuni se înțelege schimbul de mesaje de control prin care se poate accepta sau respinge o conexiune). Multe aplicații de tip client-server în care o parte emite cereri și cealaltă emite răspunsuri folosesc, mai curând, UDP decât să se complice să stabilească și apoi să elibereze o conexiune. UDP este descris în RFC 768. Antetul mesajului UDP este prezentat în figura 1.4.

32 de biți

Figura 1.4 – Antetul pachetelor UDP

Un segment UDP constă dintr-un antet de 8 octeți urmat de date. Cele două porturi au rolul de a identifica punctele terminale ale echipamentelor sursă și destinație. Câmpul lungime UDP include cei 8 octeți ai antetului plus datele. Câmpul sumă de control UDP are un format de pseudo-antet, prezentat în figura 1.5, antetul UDP și datele UDP, completate, după caz, până la un număr par de octeți. El este opțional și în cazul în care nu este calculat, este memorat ca 0 (o valoare 0 memorată în urma calculelor este prezentată ca un șir de biți 1, ceea ce este același lucru în complement față de 1). Dezactivarea acestuia este un nonsens, excepție făcând cazul în care calitatea informației chiar nu contează (de exemplu, transmisia vocală digitizată).

32 de biți

Figura 1.5 – Pseudo-antetul inclus în suma de control UDP

1.1.3.2. TCP (Transmission Control Protocol)

Aplicațiile care necesită un protocol de transport precis folosesc TCP, deoarece acesta verifică dacă datele sunt transmise exact prin rețea și în ordinea corectă. Acest protocol este orientat pe conexiune (adică întâi stabilește o conexiune și apoi trimite datele), iar transmisia se face prin flux de octeți.

Exactitatea acestui protocol se datorează unui mechanism numit PAR (Positive Acknowledgement with Re-transmission – confirmare pozitiva cu retransmisie). Când este pornit, sistemul pe care rulează acest mecanism retrimite datele până primește confirmarea de la destinație că datele au ajuns corect. Unitatea de transmisie între două module TCP se numește segment. Fiecare segment conține o sumă de control pe care destinatarul o folosește să verifice integritatea segmentului. Un segment constă dintr-un antet de exact 20 de octeți (plus o parte opțională) urmat de zero sau mai mulți octeți de date. Programul TCP decide cât de mari trebuie să fie aceste segmente. El poate acumula informația provenită din mai multe scrieri într-un singur segment sau poate fragmenta informația provenită dintr-o singură scriere în mai multe segmente. Există două limite care restricționează dimensiunea unui segment. În primul rând, fiecare segment, inclusiv antetul TCP, trebuie să încapă în cei 65535 de octeți de informație utilă IP. În al doilea rând, fiecare rețea are o unitate maximă de transfer sau MTU (Maximum Transfer Unit), deci fiecare segment trebuie să încapă în acest MTU.

Formatul unui segment TCP este prezentat în figura 1.6.

32 de biți

Figura 1.6 – Antetul TCP

Câmpurile Port sursă și Port destinație identifică punctele finale ale conexiunii. Fiecare echipament poate decide pentru sine modalitatea de alocare a porturilor sale mai mari de 256. Un port formează împreună cu adresa IP a echipamentului un unic TSAP (Transport Service Access Point) de 48 de biți. Conexiunea este identificată de ambele numere ale soclurilor (sockets) sursă și destinație.

Câmpurile Număr de secvență și Număr de confirmare au semnificația funcțiilor lor uzuale. Trebuie notat că cel din urmă indică octetul următor așteptat și nu ultimul octet recepționat în mod corect. Ambele câmpuri au lungimea de 32 de biți, deoarece într-un flux TCP fiecare bit de informație este numerotat.

Lungimea antetului TCP indică numărul de cuvinte de 32 de biți care sunt conținute în antetul TCP. Această informație este utilă, deoarece câmpul Opțiuni este de lungime variabilă, proprietate pe care o transmite astfel și antetului.

Urmează un câmp de șase biți care este neutilizat. Faptul că acest câmp a supraviețuit intact mai bine de un deceniu, este o mărturie despre cât de bine a fost conceput TCP-ul. Protocoalele mai prost concepute ar fi avut nevoie de el pentru a corecta erori ale proiectării inițiale.

Urmează acum șase indicatori de câte un bit. URG este poziționat pe 1 dacă Indicatorul de urgență este valid. Indicatorul de urgență este folosit pentru a indica deplasamentul în octeți față de numărul curent de secvență la care se găsește informația urgentă. O astfel de facilitate suplinește mesajele de întrerupere.

Bitul ACK este poziționat pe 1 pentru a indica faptul că Numărul de confirmare este valid. În cazul în care bitul ACK este poziționat pe 0, segmentul în discuție nu conține o confirmare și câmpul Număr de confirmare este ignorat.

Bitul PSH indică informația FORȚATĂ. Receptorul este rugat respectuos să livreze aplicației informația respectivă imediat ce este recepționată și să nu o memoreze în așteptarea umplerii buffer-elor de comunicație (lucru care, altminteri, ar fi fost făcut din rațiuni de eficiență).

Bitul RST este folosit pentru a desființa o conexiune care a devenit inutilizabilă datorită defecțiunii unui echipament sau oricărui alt motiv. El este de asemenea utilizat pentru a refuza un segment invalid sau o încercare de deschidere a unei conexiuni.

Bitul SYN este folosit pentru stabilirea unei conexiuni. Cererea de conexiune conține SYN=1 și ACK=0 pentru a indica faptul că acel câmp suplimentar de confirmare nu este utilizat. Răspunsul la o astfel de cerere conține o confirmare, având deci SYN=1 și ACK=1. În esență, bitul SYN este utilizat pentru a indica o CERERE DE CONEXIUNE și o CONEXIUNE ACCEPTATĂ, bitul ACK făcând distincția între cele două posibilități.

Bitul FIN este folosit pentru a încheia o conexiune. El indică faptul că emițătorul nu mai are nici o informație de transmis. Cu toate acestea, după închiderea conexiunii, un proces poate recepționa în continuare date pe o durată nedefinită.

Câmpul Fereastră indică numărul de octeți care pot fi trimiși, începând de la octetul confirmat. Un câmp Fereastră de valoare 0 este perfect legal și spune că octeții până la Număr de confirmare-1 inclusiv au fost recepționați, dar receptorul dorește o pauză. Permisiunea de expediere poate fi acordată ulterior prin trimiterea unui segment având același Număr de confirmare, dar un câmp Fereastră cu o valoare nenulă.

Antetul TCP este prevăzut cu o Sumă de control, în scopul obținerii unei fiabilități extreme. Această sumă este calculată pentru antet, informație și pseudo-antetul conceptual al TCP (asemănător cu cel din figura 1.5). Algoritmul de calcul al sumei de control este simplu, el adunând toate cuvintele de 16 biți în complement față de 1 și aplicând încă o dată complementul față de 1 sumei. În acest mod, atunci când receptorul aplică același calcul asupra întregului segment, inclusiv asupra sumei de control, rezultatul ar trebui să fie zero.

Câmpul Opțiuni a fost proiectat pentru a permite adăugarea de facilități suplimentare neacoperite de antetul obișnuit. Cea mai importantă opțiune este aceea care permite fiecărui echipament să specifice încărcarea maximă de informație utilă TCP pe care este dispus să o accepte. O altă opțiune propusă de RFC 1106, și care este în prezent utilizată pe scară largă, constă în utilizarea unei repetări selective în locul unui protocol cu întoarcere de n pași. Dacă receptorul primește un segment eronat urmat de un număr mare de segmente corecte, protocolul TCP clasic va constata într-un final o depășire de timp și va retrimite toate segmentele neconfirmate, deci și pe acelea care au fost recepționate corect. RFC 1106 introduce NAK-urile (Negative Acknowledgement) pentru a permite receptorului să ceară un anumit segment (sau segmente). După obținerea acestora, el poate confirma toată informația memorată reducând astfel informația retransmisă.

1.1.4. Nivelul Aplicație

În vârful ierarhiei arhitecturii protocolului TCP/IP se află nivelul Aplicație. Acest nivel include toate procesele ce folosesc protocoalele nivelului Transport pentru livrarea datelor. Există un număr mare de protocoale pentru aplicații. Majoritatea furnizează servicii pentru utilizator, iar noi servicii sunt adăugate mereu acestui nivel. Cele mai cunoscute și mai folosite protocoale de aplicații sunt:

telnet – Network Terminal Protocol, furnizează servicii de logare la distanță în rețea;

FTP – File Transfer Protocol, folosit pentru transfer de fișiere în mod interactiv;

SMTP – Simple Mail Transfer Protocol, pentru livrarea poștei electronice (e-mail);

HTTP – Hypertext Transfer Protocol, pentru livrarea de pagini Web în rețea.

Unele protocoale, cum sunt telnet și FTP, pot fi folosite în crearea de aplicații doar dacă programatorii au unele cunoștințe despre rețele. Alte protocoale, precum OSPF (Open Shortest Path First – deschide cea mai scurtă cale întâi), lucrează fără ca utilizatorul să știe de existența lor.

1.2. Transmisia de voce prin TCP/IP

1.2.1. Generalități

Tehnica de transmisie a vocii prin TCP/IP poartă denumirea de Voice over Internet Protocol (prescurtat VoIP) sau IP Telephony (telefonie IP). Această tehnică permite transportul traficului de voce prin rețele IP. Lucrul acesta poate fi realizat în orice tip de rețea IP, inclusiv Internet, intranet-uri ale companiilor sau rețele cu acoperire locală (LAN). În aceste medii, semnalul de voce este digitizat și încapsulat într-un pachet IP, care, ulterior, este transmis prin rețeaua IP.

Protocoalele standard ce furnizează structurile de semnalizare necesare “fuzionării” traficului de voce în rețelele de date sunt încă în stadiul de dezvoltare. Aceste protocoale permit localizarea utilizatorilor, negociază resursele necesare și realizează conexiunea.

Apariția acestei ramuri a comunicațiilor se datorează, în special, la două motive principale:

Marile companii, deținătoare a unor rețele proprii extinse, au dorit reducerea costurilor prin fuzionarea traficului de date cu cel de voce;

Apariția unor aplicații ce integrează atât date cât și voce. Aceste aplicații îmbunătățesc dezvoltarea afacerilor. De exemplu, colaborarea între mai multe centre pentru un anumit proiect, servicii de e-mail și voce integrate în același pachet, etc.

Pentru ca telefonia IP să se impună trebuiesc rezolvate câteva aspecte cheie. Câteva

din aceste aspecte rezultă din faptul că rețelele IP au fost proiectate inițial doar pentru traficul de date. Alte aspecte se referă la potențiala creștere și scalabilitate a mediului de dezvoltare. Ca cerințe minime pe care o rețea ce suportă VoIP trebuie să le îndeplinească sunt:

Calitatea: aceasta trebuie să fie cel puțin egală cu calitatea oferită de rețelele de telefonie actuale, numite POTS (Plain Old Telephone System). Întârzierea apărută de la sursă la destinație are un rol important în perceperea calității. Această întârziere include timpul necesar peluării și codării eșantioanelor de voce, precum și timpul necesar transmiterii acestor eșantioane codate prin rețea. Întărzierile excesive provoacă câteva probleme majore în mediul VoIP:

– Ecoul vocii, datorat reflexiei semnalului. Acesta apare și în rețelele de telefonie, însă în rețelele IP întârzierile sunt mai mari decât în rețelele de telefonie și trebuie folosite echipamente pentru minimizarea acestuia.

– Suprapunerea convorbirilor poate apare tot datorită întârzierilor excesive. Trebuie folosite în acest caz opțiuni de prioritizare a traficului pentru a minimiza această problemă.

– Claritatea vocii este un aspect datorat performanțelor rețelei IP. Pierderea de pachete de voce sau întârzierea lor are un impact major în perceperea clarității. Algoritmii de digitizare și compresie a semnalelor de voce au de asemenea un rol important în claritatea vocii.

– Ușurința în utilizare: aceasta împreună cu fiabilitatea rețelelor VoIP trebuie să fie cel puțin echivalente cu cele oferite în prezent de rețelele de telefonie. Rețelele VoIP nu vor fi acceptate pe scară largă dacă serviciul necesită planuri de numerotare complexe, au un procent mare de pierdere a legăturilor și este nevoie de un timp mai îndelungat pentru a realiza un apel;

– Scalabilitatea: sistemele VoIP au posibilitatea de a furniza servicii de calitate mai bună decât rețelele actuale PSTN (Public Switched Telephone Network). Acest lucru implică rate de creștere a utilizării foarte mari pe care sistemele de telefonie IP trebuie să le poată suporta, deci trebuie să fie extensibile.

– Interoperabilitatea și integrarea: mediul telefoniei IP trebuie să fie capabil să opereze cu echipamente asemănătoare, dar de la producători diferiți. De asemenea, trebuie să lucreze cu rețelele actuale PSTN. Aceste rețele diferite trebuie să apară ca un mediu unic pentru utilizatorii acestor servicii.

1.2.2. Stiva de protocoale pentru VoIP

Mediul VoIP cuprinde câteva protocoale individuale care interoperează într-o arhitectură ierarhică pentru a furniza serviciile dorite. Interacțiunea dintre aceste protocoale poate fi reprezentată sub formă de stivă. Pentru înțelegerea acestei stive am împărțit protocoalele în două secțiuni funcționale ca în figura 1.7.

Figura 1.7 – Stiva de protocoale pentru transmisii multimedia

Cele două secțiuni ale stivei furnizează următoarele funcții:

Protocoale de transport și calitate: sunt folosite pentru transportul traficului de voce propriu-zis;

Protocoale de semnalizare și suport: majoritatea activității în telefonia IP a fost dedicată dezvoltării protocoalelor de semnalizare. Aceste protocoale permit localizarea utilizatorilor, negociază resursele necesare și realizează conexiunea. În prezent există patru protocoale de semnalizare importante:

Recomandarea H.323 a Uniunii Internaționale a Telecomunicațiilor (ITU);

Protocolul Inițierii Sesiunii (SIP);

Protocolul de Control al Porții spre Mediul de transmisie (MGCP);

Recomandarea H.248 a ITU.

Aceste protocoale sunt incompatibile și deseori furnizează funcții redundante. Când

sunt folosite în același mediu, comunicarea între aceste protocoale se face folosind convertoare.

În consecință, pentru a încapsula pachetele de voce conform arhitecturii TCP/IP, parcurgem următorii pași:

Figura 1.8 – Încapsularea conform protocolului TCP/IP

1.2.2.1. Recomandarea H.323 a ITU

Această recomandare a fost dezvoltată inițial pentru videoconferințe multimedia prin rețele cu comutație de pachete. Ulterior, standardul a fost extins pentru a include și nevoile telefoniei IP. În acest moment este cel mai răspândit protocol de procesare a apelurilor.

Versiunea inițială a lui H.323 a fost dezvoltată foarte rapid. Lucrul a început în Mai 1995 și a fost terminat în Iunie 1996. de atunci au mai fost lansate încă trei versiuni.

În rețelele care lucrează după standardul H.323 pot fi delimitate patru componente:

Terminalele – sunt punctele finale ale unei rețele de telefonie IP. Orice terminal trebuie să suporte comunicații de voce, iar comunicațiile de date și video sunt opționale. Aceste terminale suportă conversații multipunct și sunt capabile de inițierea de conferințe ad-hoc.

Gateway-urile – oferă posibilitatea telefoanelor standard să folosească serviciile VoIP. Gateway-ul funcționează ca un translator, furnizând interfața dintre rețelele PSTN și rețelele IP. Alte funcții includ semnalizări între apeluri, multiplexare, transcodare audio.

Gatekeeper-ii – sunt cele mai importante componente din mediul H.323. O rețea de terminale H.323 împreună cu gateway-urile aflate sub controlul unui gatekeeper formează o subrețea într-o rețea mai mare de tip IP. Alte funcții: translatarea unei adrese din format H.323 în format IP, acordă permisiunea de a iniția apeluri, rutarea apelurilor precum și managementul apelurilor.

Unitățile de control multipunct (MCU) – se găsesc în punctele finale ale rețelei IP. Oferă posibilitatea ca trei sau mai multe terminale sau gateway-uri să participe într-o conferință multipunct. De asemena furnizează servicii de managementul conferinței și comutarea între medii de transmisie.

1.2.2.2. Protocolul Inițierii Sesiunii (SIP)

SIP este un protocol de semnalizare al nivelului aplicație folosit pentru a iniția, menține și termina apeluri, permițând clienților, gateway-urilor VoIP și altor sisteme să comunice. Acest standard este descris în RFC 2543.

Într-o rețea ce folosește standardul SIP există două componente:

Agentul utlizator – este echivalentul unui terminal H.323 și se găsește în punctele finale a oricărei rețele SIP. Include la rândul său două componente:

Agentul utilizator client (UAC) – responsabil pentru inițierea cererilor SIP;

Agentul utilizator server (UAS) – responsabil pentru recepționarea și răspunsul la cereri.

Server-ele de rețea – suportă funcții de apel avansate. Implementează, de obicei, o combinație între diferite tipuri de server-e și sunt echivalentul gatekeeper-ilor din standardul H.323.

1.2.2.3. Protocolul de Control al Porții spre Mediul de transmisie (MGCP)

MGCP este un protocol de semnalizare al apelurilor alternativ. A fost dezvoltat inițial pentru a rezolva deficiențele descoperite în protocolul H.323. Furnizează posibilitatea de a separa gateway-ul telefonului într-un set de componente aflate într-o relație de tip master-slave.

Modelul MGCP este “creierul” ce implementează funcțiile avansate de apel, deoarece terminalele au funcții tehnice minime (sunt, de asemenea, foarte ieftine), scăzând simțitor costurile unei astfel de soluții.

Componentele unei rețele ce implementează standardul MGCP sunt:

Agentul de apel – componentă de tip master, direcționează operațiile gateway-ului. În plus, este responsabil de funcțiile de procesare a apelului și semnalizări. Este echivalentul unui gatekeeper din standardul H.323;

Gateway-ul – componentă de tip slave, este responsabilă cu conversia semnalelor audio folosite în rețelele PSTN în pachete folosite în rețelele IP. Datorită relației de tip master-slave, se așteaptă ca gateway-urile să execute comenzile agentului de apel.

=== Capitolul 2 ===

Capitolul 2

Prelucrări primare ale semnalului audio

2.1. Generalități

O undă (sunet) audio este o undă cu o dimensiune acustică (presiune). Atunci când o undă acustică intră în ureche, pavilionul vibrează, făcând ca oasele urechii interne să vibreze o dată cu el, transmițând vibrații nervoase creierului. Aceste vibrații sunt percepute drept sunet de ascultător. În mod similar, atunci când o undă acustică lovește un microfon, acesta generează un semnal electric, reprezentând amplitudinea sunetului ca funcție de timp. Reprezentarea, procesarea, memorarea și transmisia acestor semnale audio constituie părți majore ale studiului sistemelor multimedia.

Intervalul de frecvență pentru urechea umană este cuprins între 20 Hz și 20 KHz, deși unele animale, în special câinii, pot percepe frecvențe mai înalte. Urechea percepe sunete în mod logaritmic, așa încât raportul a două semnale cu amplitudinile A și B este exprimat convențional în decibeli (dB) în concordanță cu formula:

dB=20log10(A/B)

Dacă definim limita inferioară de audibilitate (o presiune de circa 0,0003 dyne/cm2) pentru o undă sinusoidală de 1 KHz ca 0 dB, o conversație obișnuită este în jur de 50 dB și pragul de durere este în jur de 120 dB, un interval dinamic cu un factor de 1 milion. Dacă trebuie să folosim nivelul puterii, care este proporțional cu pătratul amplitudinii, coeficientul amplitudinii va fi 10, nu 20.

Urechea este surprinzător de sensibilă la variații ale sunetului care durează doar câteva milisecunde. Ochiul, în schimb, nu poate observa schimbările de nivel ridicat care durează doar câteva milisecunde. Rezultatul acestei observații constă în faptul că o variație de numai câteva milisecunde în timpul unei transmisii multimedia afectează calitatea sunetului perceput mai mult decât afectează calitatea imaginii percepute.

Undele audio pot fi convertite în formă numerică de un CAN (Convertor Analog-Numeric). Un CAN primește o tensiune electrică la intrare și generează un număr binar la ieșire. În figura 2.1(a) este prezentat un exemplu al unei unde sinusoidale.

Figura 2.1 – (a) O undă sinusoidală. (b) Eșantionarea undei sinusoidale.

(c) Cuantificarea eșantioanelor pe 3 biți

Pentru reprezentarea acestui semnal în formă numerică, putem să-l eșantionăm la fiecare ∆T secunde, așa cum se observă prin înălțimea barelor din figura 2.1(b). Dacă o undă sonică nu este o undă pur sinusoidală, ci o superpoziție liniară de unde sinusoidale, unde cea mai mare componentă a frecvenței prezente este f, atunci teorema lui Nyquist spune că este suficient să se eșantioneze la frecvența 2f. Eșantionând mai des, nu se obține nimic în plus, deoarece frecvențele mai înalte pe care discretizarea le poate detecta nu sunt prezente.

Eșantioanele digitale nu sunt niciodată exacte. Eșantioanele pe 3-biți din figura 2.1(c) permit doar 8 valori, de la -1,00 la +1,00, în pași de 0,25. Un eșantion pe 8-biți permite 256 de valori distincte, iar un eșantion pe 16-biți admite 65535 valori distincte. Eroarea introdusă de numărul finit de biți de eșantionare se numește zgomot de cuantizare (quantization noise). Dacă este foarte mare, urechea îl detectează.

Un exemplu de sunet eșantionat este telefonul. Modularea impulsului în cod (PCM), utilizează eșantioane pe 7 biți (America de Nord și Japonia) sau 8 biți (Europa) de 8000 de ori pe secundă. Acest sistem furnizează o viteză a datelor de 56000 bps (bit per second) sau 64000 bps. Cu doar 8000 de eșantioane/sec, frecvențele peste 4 KHz sunt pierdute.

Sunetele digitizate pot fi ușor procesate de calculatoare, prin programe. Există zeci de programe pentru PC-uri, care permit utilizatorilor să înregistreze, să afișeze și să editeze undele sonore, uneori de la surse multiple. Câteva sisteme de generare și transmisie de voce pot folosi modele de sisteme vocale pentru a reduce vocea la câțiva parametri (de exemplu, mărimile și formele diferitelor cavități), mai degrabă decât a eșantiona forma de undă pentru voce.

2.2. Prelucrări asupra semnalului de voce

Pentru a realiza un sistem simplu de tip VoIP sunt necesare următoarele echipamente:

Un calculator (minim cu procesor 386);

O placă de sunet capabilă full-duplex;

O placă de rețea, o conexiune la Internet sau un alt tip de interfață ce permite conectarea între două PC-uri.

Funcționarea acestui sistem este următoarea:

Se convertește vocea din analog în digital (biți) folosind un CAN;

Biții sunt “comprimați” folosindu-se un algoritm de compresie;

Pachetele de voce sunt introduse în pachete de date folosindu-se un protocol de transmisie în timp real, de exemplu RTP (Real-time Transfer Protocol);

Se folosește un protocol de semnalizare pentru inițierea apelului (de exemplu protocolul H 323);

La recepție se desfac pachetele, se extrag datele, se transformă biții în semnale de voce analogice și se trimit către placa de sunet.

2.2.1. Conversia analog-numerică

Această conversie se face de către partea hardware, de obicei, un convertor analog-numeric integrat pe placa de sunet.

Astăzi, majoritatea plăcilor de sunet permit conversia pe 16 biți pentru o bandă de 22050 Hz (pentru eșantionare avem nevoie de o frecvență de 44100 Hz pentru principiul lui Nyquist). Se obține astfel un transfer de 2 octeți * 44100(eșantiane pe secundă) =88200 octeți/secundă sau 176,4 kocteți/secundă pentru o transmisie stereo. În realitate nu este nevoie de un transfer atât de mare, deoarece se mai folosesc și algoritmi de compresie.

2.2.2. Algoritmi de compresie

Fluxul de biți obținut după conversie este apoi transformat într-un format standard pentru o transmisie rapidă. Pentru aceasta se folosește modulația impulsurilor în cod (PCM) sau modulația impulsurilor în cod diferențială adaptivă (ADPCM), ambele urmate de compandare.

2.2.2.1. Modulația impulsurilor în cod (PCM)

PCM este o metodă utilizată pentru a digitiza sau cuantiza un semnal analogic, folosit apoi în transmiterea vorbirii. Ca în orice conversie analog-digitală, procesul de cuantizare realizează o estimare a semnalului, cu posibilitatea introducerii unei erori în reprezentarea digitală a acestuia, datorită numărului finit de biți utilizați. Teoretic, această eroare poate fi nesemnificativă dacă se folosesc un număr destul de mare de biți pentru reprezentare (precizie mare). În practică, trebuie să existe un echilibru între mărimea erorii și mărimea reprezentării semnalului binar. Țelul este de a cuantiza datele într-un număr cât mai mic de biți, dar, în același timp, să existe o eroare tolerabilă. În cazul vorbirii, minimul necesar este o cuantizare liniară pe 13 sau 14 biți.

Modulația impulsurilor în cod este compusă din trei pași succesivi: eșantionare, cuantizare și codare.

Eșantionarea reprezintă determinarea amplitudinii semnalului la intervale de timp regulate. Deoarece banda semnalului vocal este de aproximativ 4 KHz, pentru o reproducere fidelă este nevoie de o frecvență de eșantionare de cel puțin 8 KHz (conform teoremei lui Nyquist), ceea ce înseamnă o eșantionare la fiecare 125 μs. Odată obținută amplitudinea semnalului, aceasta este cuantizată într-un set de nivele de amplitudine pentru o reprezentare numerică. Cuantizarea se obține prin împărțirea benzii semnalului în intervale de cuantizare. Toate amplitudinile dintr-un interval de cuantizare sunt reprezentate printr-un punct la mijlocul intervalului de cuantizare. Procesul de cuantizare introduce erori de cuantizare în semnalul digital, minimizarea erorilor făcându-se prin micșorarea lățimii intervalului de cuantizare. La final, codarea semnalului se face convertind fiecare nivel de cuantizare în cuvinte de cod.

S-a observat că urechea umană folosește o cuantizare neuniformă. De asemenea, se crede că auzul este un proces logaritmic în care sunetele de amplitudine înaltă nu au nevoie de aceeași rezoluție ca sunetele de amplitudine joasă. Conversia la o scară logaritmică permite intervalelor de cuantizare să crească cu amplitudinea și astfel semnalele de amplitudine mică sunt digitizate cu o pierdere mică a fidelității. În plus, sunt necesari mai puțini biți pe eșantion pentru a obține un raport semnal/zgomot adecvat pentru semnalele mici.

Pentru a reduce erorile de cuantizare se folosește procesul numit compandare. Compandarea se poate face folosind un echipament fizic numit CODEC sau prin software parcurgând un tabel de compandare.

Există două standarde internaționale de compandare: legea A (A-Law) folosită în Europa și legea μ (μ-Law) folosită în Japonia și SUA. Aceste standarde rețin cei mai importanți 5 biți din codarea semnalului pe 8 biți.

2.2.2.2. Legea A (A-Law)

Funcția continuă după care se face compresia este:

, unde

În această funcție, A este parametru de compresie (pentru Europa A=87,6) și x este întregul normalizat care trebuie comprimat. O aproximare a acestei funcții este prezentată în figura 2.2.

Figura 2.2 – Curba de compandare după legea A

Compandarea după legea A este aproximată prin segmente liniare. O ieșire de nivel zero pentru primul nivel de cuantizare nu este definită. Valoarea maximă a întregului care poate fi convertit este 4096. Datorită pasului de 2/4096, care introduce erori de cuantizare mari, semnalele de amplitudine mică obținute cu legea A au o calitate inferioară celor obținute cu legea μ. Totuși dinamica semnalului compandat după legea A este puțin mai mare decât dinamica celui compandat după legea μ (48,4 dB pentru legea μ și 48,7 dB pentru legea A).

În tabelul următor este prezentat modul de compresie după legea A:

Din cuvântul de cod comprimat, biții 4-6 reprezintă coarda, iar biții 0-3 reprezintă pasul. Au fost omise din tabel extensia de semn a valorii de intrare și bitul de polaritate a cuvântului de cod comprimat.

O dată determinate coarda și pasul, se determină apoi polaritatea întregului inițial. Dacă aceasta era negativă, bitul de polaritate (bitul 7) este făcut 1, altfel acesta este 0.

Expandarea pentru legea A este definită de ecuația continuă:

, unde

Expandarea după legea A urmărește deasemenea un tabel:

După decodare, semnul întregului este restaurat conform bitului de polaritate.

2.2.2.3. Modulația impulsurilor în cod diferențială adaptivă (ADPCM)

Spre deosebire de PCM, ADPCM nu transmite valoarea eșantionului de voce, ci diferența dintre o valoare preconizată și valoarea reală a eșantionului, folosind un predictor adaptiv, care se ajustează în funcție de fiecare eșantion de intrare, reducând astfel numărul de biți necesari pentru reprezentarea datelor de la 8 biți la 4 biți.

De obicei, un transcodor ADPCM este inserat într-un sistem PCM pentru a mări capacitatea canalului de voce aferent sistemului. În consecință, codorul ADPCM acceptă la intrare valori PCM, iar decodorul ADPCM scoate la ieșire valori PCM.

Predictorul adaptiv calculează o valoare medie între ultimele șase valori ale diferenței necuantizate și ultimele două valori preconizate. Valoarea preconizată se calculează cu ecuația:

, unde

În consecință, folosirea unei modulații a impulsurilor în cod diferențială adaptivă face ca lățimea de bandă necesară pentru o transmisie să fie de doar 32 kbps (conform standardului G.726 a Uniunii Internaționale a telecomunicațiilor).

2.2.3. Protocolul de transport în timp real (RTP)

După obținerea datelor comprimate acestea trebuiesc încapsulate după modelul TCP/IP. Se urmărește structura:

Figura 2.3 – Încapsularea conform protocolului TCP/IP

În primul rând, tehnologia VoIP nu folosește TCP pentru transportul pachetelor de voce, fiindcă antetul pachetelor TCP este mare și inutil în aplicații în timp real. În schimb se folosesc datagrame UDP.

În al doilea rând, UDP nu are control asupra ordinii în care sosesc pachetele la destinație sau a timpului necesar pentru livrare. Ambele concepte sunt foarte importante pentru calitatea vocii și a conversației. RTP rezolvă această problemă, permițând destinatarului să aranjeze în ordinea corectă pachetele și să nu aștepte prea mult pachetele care întârzie sau sunt pierdute pe parcurs.

Antetul unui pachet RTP este prezentat în figura 2.4:

32 de biți

Figura 2.4 – Antetul pachetului RTP

Câmpurile individuale din antet reprezintă:

V: versiunea RTP folosită;

P: bitul de „umplutură”. Dacă acest bit este setat, pachetul conține un număr de biți de „umplutură” care nu fac parte din încărcătura pachetului. Acest bit este folosit de unele programe de criptare;

X: conține bitul de extensie. Dacă este setat, extensia antetului urmărește antetul fix;

Numărător CSRC: conține numărul identificatorilor surselor ce contribuie la încărcătura pachetului;

M: marchează evenimentele semnificative din fluxul de pachete;

Tipul încărcăturii: specifică tipul încărcăturii pachetelor RTP. La un moment dat un emitor de pachete RTP poate emite doar un singur tip de pachete RTP;

Numărul secvenței: este folosit de destinatar pentru a reface ordinea pachetelor primite și a detecta pachetele pierdute;

Eticheta de timp: este o valoare reprezentând momentul în care încărcătura a fost eșantionată;

Identificatorul SSRC: sursa de sincronizare este un identificator ales aleatoriu pentru o gazdă RTP. Pachetele provenind de la aceeași sursă au același identificator SSRC. Aceast lucru ajută destinatarul să grupeze pachetele pentru redare;

Identificatorii CSRC: conține lista tuturor surselor ce au contribuit la crearea încărcăturii. Este folosit când un mixer combină diferite fluxuri de pachete.

Voi discuta mai detaliat despre RTP în capitolul 3.

2.3. Elemente de calitate a transmisiei

Odată cu creșterea folosirii rețelelor bazate pe IP, atenția s-a concentrat pe furnizarea resurselor necesare pentru rularea unor aplicații. Pentru a înțelege mai bine, putem spune că unele aplicații sunt mai importante decât altele, acestea având nevoie de un tratament preferențial prin rețea. În plus, aplicațiile au nevoi diferite. De exemplu, aplicațiile în timp real au nevoie de timpi de așteptare mici și lățime de bandă mare.

În consecință, fără un control asupra lățimii de bandă, calitatea fluxurilor în timp real depinde de banda disponibilă la un moment dat. Lățimi de bandă mici sau instabile duc la pierderi ale legăturii sau primirea unui ton de ocupat. De aceea sunt necesare câteva concepte pentru a garanta calitatea serviciului (Quality of Service – QoS) pentru aplicațiile în timp real. QoS poate fi descris ca un set de parametrii ce descriu calitatea (de exemplu, lățimea de bandă, ocuparea buffer-ului, prioritate, gradul de folosire al procesorului, etc.) pentru un flux de date specific.

Stiva protocolului IP furnizează doar un anume QoS numit „cel mai bun efort”. Astfel pachetele sunt transmise de la un punct la altul fără garanția unei lățimi de bandă speciale sau timpi de întârziere minimi. În acest caz, cererile prin rețea sunt tratate după strategia „primul venit, primul servit”, adică toate cererile au aceeași prioritate, fără posibilitatea de a rezerva o anumită lățime de bandă sau a crește prioritatea anumitor cereri speciale.

Pentru a integra QoS în rețelele IP și Internet se folosesc două tipuri de servicii:

Serviciile integrate (Integrated Services – IS) aduc îmbunătățiri rețelelor IP pentru a suporta transmisii în timp real și garantează lățimea de bandă pentru fluxuri specifice;

Servicii diferențiate (Differentiated Services) în care clase diferite de servicii pot fi alocate la grupuri distincte de utilizatori, adică tot traficul este distribuit pe grupuri sau clase, fiecare dintre ele cu parametri QoS diferiți. Aceste servicii oferă performanțe preconizate pentru un anumit tip de încărcătură la un anumit moment (întârzieri, pierderi de pachete, ocuparea benzii, etc.). Pentru a distinge pachetele de date de la diferiți utilizatori, în pachetele IP se adaugă un șablon de biți pentru a marca un anume tratament la trecerea pachetului prin fiecare nod al rețelei.

În consecință, pentru o transmisie de voce sunt necesari următorii parametrii:

lățimea de bandă pentru voce este 4 KHz, deci avem nevoie de o frecvență de eșantionare de 8 KHz (conform teoremei lui Nyquist);

fiecare eșantion este reprezentat pe 8 biți (256 de valori posibile);

transferul este de 8000Hz*8bit=64Kbit/s, ca și o linie telefonică clasică;

pentru compresie se folosește legea A, care codează semnalul analogic în scară logaritmică pe 12 biți în loc de 8 biți conform standardului G.711 (ce implementează protocolul PCM) al Uniunii Internaționale a Telecomunicațiilor (ITU).

=== Capitolul 3 ===

Capitolul 3

Descrierea RTP

3.1 Generalități

RTP este prescurtarea de la Real-time Transport Protocol (Protocol de Transport în timp real). RTP furnizează servicii de transport a datelor în rețele de tip end-to-end, unde aplicațiile transmit date în timp real (precum audio, video sau rezultate ale unor simulări). Aceste servicii includ identificarea tipului încărcăturii pachetelor, numerotarea secvențelor și etichetarea de timp. Transportul datelor este îmbunătățit de un protocol de control (RTCP – Real-time Transport Control Protocol) care permite monitorizarea livrării datelor într-o manieră scalabilă rețelelor de tip multicast mari și pentru a pemite un control minim și capacități de identificare.

Aplicațiile, în general, folosesc RTP peste UDP pentru a folosi serviciile acestuia de multiplexare. Ambele protocoale (RTP și UDP) contribuie la funcționalitatea protocoalelor de transport. RTP suportă transferul de date spre destinații multiple folosind distribuția multicast, dacă aceasta este suportată de rețea.

RTP nu conține nici un mecanism de adresare a rezervării resurselor și nu asigură calitatea serviciilor în timp real, ci se bazează pe serviciile nivelelor inferioare pentru acest lucru.

În concluzie, RTP se definește ca fiind format din două componente aflate în strânsă legătură:

Protocolul de transport în timp real (RTP) pentru transportul datelor și care are proprietăți de timp real;

Protocolul de control RTP (RTCP) pentru monitorizarea calității serviciului și transmiterea informațiilor despre participanții la o sesiune în curs.

RTP este un protocol maleabil care furnizează informațiile necesare unei anumite aplicații, de aceea va fi implementat în procesele acesteia și nu ca un nivel separat. În plus, RTP este un protocol neterminat în mod deliberat, astfel că, pe lângă funcțiile comune tuturor aplicațiilor, se pot introduce funcții specifice fiecărei aplicații modificând antetul pachetelor RTP.

O sesiune RTP reprezintă asocierea unui set de participanți care comunică prin RTP. Pentru fiecare participant, sesunea este definită de o pereche de adrese de transport la destinație. Aceste adrese pot fi comune tuturor participanților la sesiune (în cazul transmisiilor multicast) sau separate pentru fiecare participant.

3.2 Scenariu de utilizare a RTP

Să presupunem că se dorește o conferință audio simplă multicast folosindu-se serviciile de conferință audio a Internetului. Printr-un mecanism de alocare se obține adresa de grup multicast și perechi de porturi. Un port este folosit pentru datele audio, iar celălalt pentru pachete de control (pachete RTCP). Această adresă, împreună cu informațiile despre porturi, este transmisă către toți participanții. Dacă se dorește securitatea transmisiei, datele și pachetele de control pot fi criptate, în acest caz fiind nevoie de o cheie de criptare care trebuie și ea transmisă participanților la conferință.

Aplicația pentru conferința audio folosită de fiecare participant trimite datele audio în secvențe mici cu durata de 20ms. Fiecare secvență este precedată de un antet RTP; antetul și datele RTP sunt conținute la rândul lor în pachetul UDP. Antetul RTP conține tipul codării audio (PCM sau ADPCM) din fiecare pachet, participanții putând schimba tipul de codare în timpul conferinței, de exemplu, pentru a gazdui un nou participant la conferință care este conectat printr-o legătură cu lățime de bandă mică sau ca reacție la o congestie în rețea aparută pe parcurs.

Internetul, ca și alte rețele cu comutație de pachete, pierde ocazional pachete sau le schimbă ordinea de sosire și le întârzie cu un anumit timp. Pentru a face față acestor impedimente, antetul RTP conține numărul secvenței și informații despre timing, permițând destinatarului să refacă ordinea secvențele astfel încât secvențele sunt redate continuu. Reconstrucția timing-ului se face separat pentru fiecare sursă de pachete RTP din conferință. Numerotarea secvențelor poate fi folosită de destinatar pentru a estima câte pachete sunt pierdute.

Deoarece participanții la o conferință pot intra sau părăsi conferința oricând, este bine de știut cine participă la conferință și cât de bine recepționează datele audio la un moment dat. În acest scop, fiecare instanță a aplicației audio din conferință transmite periodic un raport despre calitatea recepției și numele utilizatorului prin portul rezervat pentru RTCP. Pe lângă numele utilizatorului se pot transmite informații cu privire la limitări în lățimea de bandă. Dacă un participant părăsește conferința se transmite un pachet RTCP numit BYE.

Un alt avantaj al folosirii RTP este acela că atunci când se transmite audio și video simultan, se folosesc două sesiuni RTP separate (una pentru fiecare tip de transmisie) cu pachte RTCP transmise pe două perechi de porturi UDP separate. Un motiv al acestei separări este acela de a permite unor participanți la conferință să recepționeze doar un tip de transmisie. În ciuda separării, sincronizarea transmisiilor se face prin informațiile de timing din pachetele RTCP din ambele sesiuni.

În cazul de mai sus am presupus că toate locațiile recepționează datele în același format. Totuși această abordare nu este totdeauna corectă.

Să considerăm cazul în care participanții dintr-o zonă au o conexiune cu viteză mică, iar majoritatea celorlalți au o conexiune rapidă. În locul forțării tuturor participanților să folosesască o lățime de bandă mică și o codare audio de calitate inferioară, se poate folosi un mixer în apropierea zonei cu lățime de bandă mică. Mixerul resincronizează pachetele audio sosite pentru reconstrucția diferenței constante de timp de 20ms generate la transmisie, adună aceste pachete într-un singur flux audio, transformă codarea într-o codare pentru lățimea de bandă mică și le transmite pe rețeaua cu viteză mică. Aceste pachete pot fi transmise unuia sau mai multor destinatari. Antetul RTP include moduri prin care mixerul poate identifica toate sursele care au contribuit la pachet, astfel încât destinatarul știe de la cine a primit pachetul.

Un alt caz ar fi acela în care participanții sunt toți conectați la rețele cu viteză mare, dar unii dintre ei sunt protejați de un firewall care nu permite trecerea nici unui pachet IP. În acest caz, nu se mai folosesc mixere, ci translatoare. Sunt instalate două translatoare, câte unu pentru fiecare parte a firewall-ului. Translatorul din afara firewall-ului recepționează toate pachetele transmise, pe care le transmite apoi translatorului din interiorul firewall-ului pe un canal sigur, iar de aici către destinatari.

3.3 Formatul antetului pachetelor RTP

În figura 3.1 este prezentat antetul pachetelor RTP:

32 de biți

Figura 3.1 – Antetul pachetului RTP

Câmpurile individuale din antet reprezintă:

V (2 biți): versiunea RTP folosită;

P (1 bit): bitul de „umplutură”. Dacă acest bit este setat, pachetul conține un număr de octeți de „umplutură” care nu fac parte din încărcătura pachetului. Ultimul octet al „umpluturii” conține numărul de octeți ai „umpluturii” ce trebuie ignorați. Acest bit este folosit de unele programe de criptare;

X (1 bit): conține bitul de extensie. Dacă este setat, antetul fix este urmat de o singură extensie de antet;

Numărător CSRC (4 biți): conține numărul identificatorilor surselor ce contribuie la încărcătura pachetului;

M (1 bit): marchează evenimentele semnificative din fluxul de pachete. Interpretarea lui M este definită de un profil, care poate defini și alți biți de marcare sau poate să nu definească nici unul;

Tipul încărcăturii (7 biți): specifică tipul încărcăturii pachetelor RTP și determină modul de interpretare al lor de catre aplicație. La un moment dat un emitor de pachete RTP poate emite doar un singur tip de pachete RTP. Alte tipuri de încărcături pot fi definite dinamic prin metode non-RTP;

Numărul secvenței (16 biți): este folosit de destinatar pentru a reface ordinea pachetelor primite și a detecta pachetele pierdute. Acest câmp se incrementează de fiecare dată când un pachet RTP este transmis. Inițial, acest câmp este un număr aleator pentru a face ca atacurile asupra transmisiei să fie mai dificile;

Eticheta de timp (32 de biți): este o valoare reprezentând momentul în care încărcătura a fost eșantionată. Rezoluția ceasului trebuie să fie suficiantă pentru acuratețea sincronizării. Frecvența ceasului este dependentă de formatul încărcăturii și este specificată static în acesta. De exemplu, pentru o transmisie audio cu frecvență fixă, ceasul etichetei de timp se va incrementa cu 1 pentru fiecare perioadă de eșantionare. Dacă aplicația audio citește blocuri de 160 de perioade de eșantionare, atunci eticheta de timp se va incrementa cu 160 pentru fiecare bloc, indiferent dacă blocul este transmis sau nu. Valoarea inițială a acestui câmp este aleatoare;

Identificatorul SSRC (32 de biți): sursa de sincronizare este un identificator ales aleatoriu pentru o gazdă RTP. Pachetele provenind de la aceeași sursă au același identificator SSRC. Aceast lucru ajută destinatarul să grupeze pachetele pentru redare. Deși probabilitatea ca mai multe surse să aleagă același identificator este mică, toate implementările RTP trebuie să fie pregătite să detecteze și să identifice coliziunile. Dacă o sursă își schimbă adresa, atunci trebuie să-și schimbe și identificatorul SSRC pentru a nu fi interpretată ca o sursă în buclă locală;

Identificatorii CSRC (de la 0 la 15 surse, fiecare de 32 de biți): conține lista tuturor surselor ce au contribuit la crearea încărcăturii. Este folosit când un mixer combină diferite fluxuri de pachete. Numărul surselor este dat de numărătorul CSRC. Dacă sunt mai mult de 15 surse, doar 15 din ele vor fi recunoscute. Identificatorii CSRC sunt inserați de mixere, folosind identificatorii SSRC ai surselor. De exemplu, pentru pachetele audio, toți identificatorii SSRC ai surselor care au fost mixați pentru a crea un pachet sunt listați.

3.3.1. Modificări ale antetului RTP determinate de profile specifice

Deși antetul RTP actual se crede a fi complet pentru setul de funcții cerute de aplicațiile actuale, acesta poate fi modificat pentru a putea fi folosit de profile specifice după cum urmează:

Bitul M și câmpul Tipul încărcăturii poartă informații specifice profilului. Octetul care conține aceste câmpuri poate fi redefinit de un profil pentru a răspunde anumitor cerințe, de exemplu, pot fi unul sau mai mulți biți M. Dacă există biți M, unul din ei trebuie să fie în bitul cel mai semnificativ al octetului;

Informația adițională necesară pentru un anume format al încărcăturii ar trebui să existe în secțiunea încărcăturii din pachet. Aceasta secțiune poate fi în antetul prezent în fiecare pachet sau poate fi indicată de o valoare rezervată din șablonul datelor;

Dacă o anumită clasă de aplicații are nevoie de funcționalitate adițională independentă de tipul încărcăturii, profilul sub care aceste aplicații funcționează ar trebui să definească câmpuri fixe suplimentare după câmpul SSRC al antetului fix.

3.4. Protocolul de control RTP ( RTCP)

Arhitectura RTCP definește patru tipuri de informație de control folosite pentru a raporta performanțele curente:

Raportul emițătorului: emis de sursa unui flux de date RTP. Furnizează statistici de emisie și recepție observate de la emițător. Acest raport este transmis către toți participanții la o sesiune RTP;

Raportul receptorului: conține statistici de recepție de la participanții care nu emit activ. Un raport al emițătorului este transmis dacă un echipament a emis într-un anumit interval de la ultimul raport, altfel se emite un raport al receptorului;

Raportul de descriere al sursei: este transmis pentru a da informații despre posibilitățile locale.

BYE: acest mesaj este emis de sursă când părăsește conferința. De asemenea când un mixer se închide se emite același mesaj.

32 de biți

Figura 3.2 – Formatul pachetului RTCP

Se observă ca antetul este format din trei secțiuni. Prima secțiune conține antetul. Aici se specifică tipul pachetului, lungimea lui și identificatorul emițătorului. A doua secțiune conține informații despre emițător. A treia secțiune conține blocuri de raport al receptorului. Pachetul poate conține mai multe astfel de blocuri.

Câmpurile din pachet sunt folosite pentru:

V (2 biți) – indică versiunea;

P (1 bit) – indică dacă la sfârșitul pachetului se găsesc octeți de „umplutură”;

RC (5 biți) – conține numărul blocurilor de control din pachet;

PT (8 biți) – conține tipul raportului;

Lungime (16 biți) – conține lungimea pachetului;

SSRC al emițătorului (32 biți) – conține identificatorul SSRC al emițătorului;

Eticheta de timp NTP (64 biți) – conține timpul absolut raportat de NTP, adică numărul de secunde din 1 Ianuarie 1900;

Eticheta de timp RTP (32 biți) – conține eticheta de timp a pachetelor RTP conform cu emițătorul;

Numărul pachetelor de la emițător (32 biți) – conține numărul total de pachete RTP emise de la începutul transmisiei;

Numărul octeților de la emițător (32 biți) – conține numărul total de octeți de încărcătură emiși de la începutul transmisiei;

SSRC_n (32 biți) – conține identificatorul SSRC al altui emițător de la care au fost primite pachete RTP. Numărul de blocuri de raport cu alt SSRC depinde de numărul surselor de pachete RTP ce emit;

Fracțiuni pierdute (8 biți) – conține fracțiunile de pachete RTP pierdute de la ultimul raport;

Numărul total al pachetelor pierdute (24 biți) – indică numărul total de pachete pierdute de la sursa SSRC_n;

Numărul ultimei secvențe RTP (32 biți) – conține numărul ultimei secvențe recepționate dintr-un pachet;

Jitter-ul dintre recepții (32 biți) – conține varianța estimată a timpului dintre două recepții. Dacă recepția este regulată valoarea jitter-ului este zero, altfel valoarea este mare;

LSR (32 biți) – conține 32 de biți din mijlocul etichetei de timp NTP de 64 de biți din ultimul pachet RTCP recepționat de la sursa SSRC_n;

DLSR (32 biți) – conține întârzierea de la ultimul LSR recepționat de la sursa SSRC_n și emisia blocului de raport curent în unități de 1/65536 secunde.

3.5. Rezolvarea coliziunilor

Deși probabilitatea ca două surse să aibă același identificator SSRC este mică, toate implementările RTP trebuie să fie pregătite să detecteze coliziunile și să le corecteze. Dacă o sursă descoperă în orice moment că altă sursă folosește același identificator SSRC trebuie să trimită un pachet BYE și să-și aleagă alt identificator aleator. Dacă la recepție se descoperă că două surse sunt în coliziune, aici se pot opri pachetele de la una din surse și ignora pachetele de la cealaltă. De asemenea, se așteaptă ca cele două surse să rezolve situația cât mai repede.

Coliziunile apar cu cea mai mare probabilitate când toți participanții la conferință se conectează simultan. Dacă N este numărul participanților și L este lungimea identificatorului (în cazul nostru 32 de biți), probabilitatea ca două surse să aleagă același identificator este aproximativ . De exemplu, pentru N=1000 probabilitatea este .

Probabilitatea tipică de coliziune este mult mai mică decât în cazul cel mai defavorabil prezentat mai sus. Dacă un participant intră într-o conferință RTP în care toți participanții au deja un identificator unic, probabilitatea de coliziune este de, însemnând că pentru 1000 de participanți ea este de aproximativ.

De asemenea, probabilitatea unei coliziuni este și mai redusă ca urmare a faptului că un participant poate primi pachete RTP de la ceilalți participanți, înainte ca el să transmită primul pachet. Deci poate compara identificatorul său cu al celorlalți, iar dacă sunt două la fel poate alege altul.

=== Capitolul 4 ===

Capitolul 4

Măsurători în rețea

4.1. Generalități

Integrarea serviciilor de voce pe rețelele digitale de date poate oferi reduceri de costuri semnificative. Însă atragerea clienților și, mai important, păstrarea acestora se realizează doar prin oferirea unor servicii de calitatea celor oferite de rețelele cu comutație de circuite tradiționale (PSTN). Există mai multe aspecte legate de calitatea serviciilor de VoIP (întârzieri, ecou, distorsiuni, pierderea de pachete, etc.), însă cea mai importantă rămâne calitatea vocii. Aceste aspecte sunt standardizate de ITU-T printr-o serie de reglementări. De asemenea, multe firme au dezvoltat pachete software pentru măsurarea calității vocii în rețelele digitale pornind de la aceste reglementări.

4.2. Distorsiuni

Pentru a măsura caracteristicile de distorsiune la emisie și recepție în transmisiile digitale se folosesc două metode: metoda „Noise” și metoda „Sinewave”.

Metoda „Noise” se utilizează în general pentru codecurile ce folosesc compresia după legea A. Valorile acceptate de standardul G. 726 al ITU-T pentru un transfer de 32 kbiți/s sunt prezentate în tabelul de mai jos.

Pentru metoda „Sinewave” valorile acceptate de standardul G. 726 pentru un transfer de 32 kbiți/s sunt:

În cazul în care semnalul depășește banda de 3,4KHz la emisie (de exemplu 4,6 KHz sau 8 KHz), atunci nivelul pentru frecvența obținută la interfața digitală trebuie să fie sub nivelul de referință obținut pentru un semnal digital cu frecvența de referință de 1 KHz cu cel puțin valoarea specificată în tabelul de mai jos:

Pentru recepție valorile acceptate sunt:

4.3. Supresia ecoului

Ecoul are un efect major în calitatea transmisiilor de voce. Acesta poate cauza dificultăți utilizatorilor în realizarea unei convorbiri. De asemenea, poate afecta transmisia de date în banda de voce sau transmiterea de faxuri.

Supresoarele de ecou din rețelele digitale sunt create pentru a elimina ecoul și de a permite utilizatorilor realizarea transmisiilor de voce și date. Aceste supresoare sunt procesoare de semnal adaptive, plasate în zona cu 4 fire de transmisie, care reduc ecoul prin extragerea unui ecou estimat din semnalul de ecou ce se întoarce. Aceste supresoare de ecou trebuie să se comporte adecvat și la transmisiile de date nu numai la transmisiile de voce și nu doar în condiții de laborator (ideale).

O altă caracteristică a supresoarelor este aceea că întârzierea obținută la ieșirea lor să nu fie mai mare de 1 ms. Nivelul semnalului la ieșirea din supresorul de ecou se calculează cu formula:

unde S(k) – nivelul semnalului în dBm0;

ei – echivalentul liniar al semnalului codat PCM la momentul i;

k – index de timp;

n – numărul de eșantioane;

Caracterisiticile supresoarelor de ecou pentru rețelele digitale sunt standardizate de Recomandarea G.168 a ITU-T.

Se presupune că supresoarele de ecou sunt „half duplex”, adică se găsesc doar pe calea de emisie. Pentru „full duplex” se atașează câte un supresoar de ecou pe fiecare cale, fiecare poziționate în direcții opuse. De asemenea, supresoarele de ecou pot fi activate sau dezactivate folosind semnale de control sau manual.

4.4. Intârzierile

Întârzierea în rețelele digitale este, de fapt, compusă din patru componente:

Întârzierea de propagare – reprezintă timpul necesar transportului pachetelor prin rețea. De exemplu, timpul de propagare între Galați și Timișoara este mai mare decât timpul de propagare între Galați și Brăila;

Întârzierea de transport – reprezintă timpul necesar trecerii pachetelor prin echipamentele de rețea. Rețelele cu multe routere, firewall-uri și switch-uri au întârzieri mai mari de transport decât o rețea pe același etaj al unei clădiri;

Întârzierea la împachetare – este timpul necesar unui codec de a digitiza semnalul analog și crearea de frame-uri, iar la destinație refacerea semnalului analog pentru redare. De exemplu, codecurile după standardul G. 729 au o întârziere mai mare de împachetare decât codecurile după standardul G. 711, pentru că durează mai mult compresia;

Întârzierea datorată buffer-ului – este o întârziere fixă introdusă de buffer-ul de la recepție, dacă acesta reține prea multe datagrame.

Valoarea combinată a întârzierii de propagare și a întârzierii de transport sunt cunoscute sub denumirea de „întârziere într-un singur sens”. Întârzierea de împachetare și întârzierea datorată buffer-ului sunt întârzieri constante, indiferent de distanța dintre emisie și recepție.

Măsurarea timpului de răspuns („întârzierea în ambele sensuri”) și împărțirea acestuia la 2 pentru a determina „întârzierea într-un singur sens” nu este o metodă indicată, deoarece pachetele RTP (emise și recepționate) pot parcurge diferite căi, fiecare cale putând avea caracteristicile ei.

4.5. Pierderea de pachete

Pierderea de pachete este un fenomen normal într-o rețea cu comutație de pachete. Pierderea de pachete apare din diverse motive: supraîncărcarea legăturilor, existența coliziunilor excesive în rețelele locale, neregularități în cablurile de legătură.

Codecurile audio iau în considerare aceste pierderi, deoarece protocolul RTP transmite date folosind UDP. De exemplu, unele codecuri folosesc chiar pachetul sosit înainte de pachetul pierdut în locul acestuia sau utilizează interpolări sofisticate pentru eliminarea întreruperilor provocate de pierderile de pachete.

Aceste pierderi devin supărătoare dacă depășesc pragul de 5% sau sunt grupate într-o avalanșă de pachete. În acest caz, nici cele mai bune codecuri nu pot ascunde pierderile de pachete și apare o degradare a calității vocii.

4.6. Concluzii

Optimizarea unei rețele digitale de date pentru utilizarea în paralel cu servicii VoIP ridică noi probleme ce trebuie rezolvate ca acest serviciu să fie competitivdin punct de vedere al calității.

Problema costurilor pare rezolvată, deoarece s-a demonstrat că scad costurile de întreținere și utilizare prin folosirea unei singure rețele (atât pentru date, cât și pentru voce) și nu două separate (câte una pentru fiecare serviciu). Însă investiția inițială în dezvoltarea unei singure rețele (extinderea celei actuale și modernizarea echipamentelor pentru a fi compatibile cu VoIP) rămâne destul de mare, dar scade constant, proporțional cu apariția unor noi tehnologii mai eficiente și mai ieftine.

În privința calității vocii , reglementările ITU-T se vor a fi un punct de reper în dezvoltarea de soluții pentru VoIP a companiilor din domeniu. De asemenea, aceste reglementări conduc la realizarea unei compatibilități ridicate între echipamentele dezvoltate de firme diferite. Câteva dintre aceste reglementări au devenit deja standarde:

Întârzierea într-un singur sens să fie mai mică de 125 ms;

Întârzierea datorată buffer-ului să fie mai mică de 20 ms;

Pierderile de pachete să fie de 0,2% sau mai mici.

=== Capitolul 5 ===

Capitolul 5

Realizarea comunicațiilor folosind UDP

5.1. Generalități

Transferul traficului de voce prin rețelele cu comutație de pachete, în special folosind protocolul IP (VoIP), este acceptat pe scară din ce în ce mai mare. Acest lucru se întâmplă, în principal, din cauză că acest transfer reduce costurile pe minutul de convorbire al utilizatorului. Un alt motiv este acela că traficul pe Internet și traficul de voce converg formând o singură rețea.

Astăzi există două categorii principale de rețele de comunicații: rețelele cu comutație de circuite și rețelele cu comutație de pachete.

În rețelele cu comutație de circuite, înainte ca două echipamente să comunice, se stabilește un circuit între ele, iar calea astfel stabilită este folosită pe întreaga comunicație, chiar dacă există și altă legătură între cele două echipamente care comunică.

În rețelele cu comutație de pachete nu este stabilită o cale propriu-zisă între cele două echipamente, ci blocurile de date aparținând aceluiași fișier sau aceleiași comunicații pot fi transmise la destinație folosind mai multe căi.

Pentru transmisiile multimedia în timp real se folosește UDP (User Datagram Protocol). Acesta este un protocol fără conexiune (connectionless protocol), spre deosebire de TCP care este orientat pe conexiune (connection-oriented protocol). Înainte de începerea comunicației, TCP transmite spre destinație un bit de sincronizare (SYN), primește doi biți (ACK+SYN) și abia apoi se începe comunicația. Deși UDP nu dispune de asemenea mecanism, este un protocol mai rapid decât TCP. Rapiditatea provine din faptul că nu se mai stabilește o conexiune (acest lucru este și un dezavantaj, deoarece este posibil ca la destinație să nu ajungă nici un pachet transmis) și prin faptul că acest protocol nu adaugă un antet mare la pachetele de date transmise (8 octeți pentru UDP, față de 24 octeți pentru TCP), fiind posibilă transmisia și prin rețele cu o lățime de bandă mică și transfer mic (antetul mic => pachet de date de dimensiuni mici => nu e nevoie de segmentarea pachetului pentru transmisie => nu e nevoie de refacerea pachetului inițial la destinație => reducerea timpului de recepție a datelor).

Un alt avantaj al UDP față de TCP este acela că, în timp ce TCP are un mecanism de control al pachetelor recepționate (dacă nu s-a recepționat un pachet se transmite la sursă o cerere de retransmisie a pachetului pierdut), la UDP lipsește. În cazul transmisiilor de voce acest lucru este benefic, deoarece un pachet care nu ajunge la destinație nu reduce drastic inteligibilitatea convorbirii (un pachet tipic pentru comunicațiile de voce în timp real conține 20 ms de captură de voce, iar pentru a deveni supărătoare, pauzele între pachete ar trebui să fie mai mari de 500 ms, deci s-ar putea pierde mai mult de 25 de pachte).

5.2. Windows Sockets

Majoritatea aplicațiilor care comunică printr-o rețea (indiferent dacă este Internet sau un LAN), folosesc aceleași principii pentru a realiza comunicația. O aplicație este pornită pe un calculator așteptând o cerere de conectare. Se spune că această aplicație “ascultă” o cerere de conectare. Pentru ca o altă aplicație să se conecteze la ea, trebuie ca acea aplicație să știe adresa din rețea a calculatorului cu care se dorește stabilirea comunicației și emite o cerere de conectare. Dacă este acceptată cererea, se stabilește conexiunea. Odată stabilită conexiunea, se începe transmisia de mesaje între cele două stații de lucru. La terminarea transmisiei se închide conexiunea.

Sub platforma Windows, realizarea acestor operațiuni se face folosind Windows Sockets (sau prescurtat WinSock). Funcționalitatea socket-urilor este descrisă în fișierul WinSock.dll sau WSock32.dll. Socket-ul este, în esență, un obiect ce scrie și citește mesaje care circulă între două aplicații.

Pentru comunicația prin socket-uri sunt definite mai multe funcții. Acestea sunt funcții de creare a socket-ului (Create), de transmisie a datelor și recepție a datelor (SendTo, RecvFrom) și de închidere a conexiunii (Close). De asemenea sunt definite funcții ce manipulează evenimente diverse (OnAccept, OnClose, OnConnect, OnReceive, OnSend).

Socket-urile sunt bidirecționale, adică oricare dintre cele două stații care comunică, poate trimite și recepționa date simultan. Astăzi, utilizarea Windows Sockets se face în conjuncție cu unul din protocoalele TCP sau UDP, ele putând să fie clasificate astfel:

Socket flux (stream socket) – este folosit când se construiește o aplicație orientată pe conexiune (connection-oriented) fiind utilizat în combinație cu TCP. În esență, se stabilește întâi conexiunea și apoi se începe schimbul de date (garantându-se astfel că pachetele ajung la destinație);

Socket de datagrame (datagram socket) – este folosit când se construiește o aplicație orientată pe pachete și nu pe conexiune (connection-less), fiind utilizat în combinație cu UDP. În acest caz nu se garantează recepția pachetelor la destinație, fiind nevoie de un mecanism incorporat în aplicație care să garanteze recepția.

Folosind UDP este nevoie să alocăm adresei destinatarului și un port de comunicație. Pe acest port aplicația la care vrem să ne conectăm ascultă pentru o cerere de conectare (porturile sunt predefinite în momentul în care instalăm aplicația). Pentru a înțelege mai bine noțiunea de port voi face o analogie cu viața reală. Să presupunem că vrem să discutăm la telefon cu cineva care se află la locul de muncă. Pentru aceasta formăm numărul de telefon al firmei (prin analogie, numărul de telefon este adresa IP), apoi cerem numărul de interior (prin analogie numărul de interior este portul de comunicație).

Asignarea unui port diferit de comunicație pentru fiecare aplicație utilizată pe un calculator (implicit de la aceeași adresă IP) este necesară pentru a se face distincția între mesajele transmise și recepționate de fiecare aplicație în parte.

5.3.Tipuri de comunicații

Astăzi pot fi deosebite trei tipuri de comunicații:

Mesaje unicast

Mesaje broadcast

Mesaje multicast

5.3.1. Mesaje unicast

Mesajele unicast sunt mesajele pe care s-a “construit” comunicația de tip World Wide Web (WWW). Utilizatorii înaintau o cerere spre un server, iar acesta trimitea informația dorită la fiecare utilizator. Se aloca astfel resurse pentru fiecare utilizator.

Mesaje unicast

Figura 5.1 – Emisia și recepția de mesaje unicast

Utilizarea transmisiilor unicast se face pentru vizualizarea paginilor Web sau a textelor și, în general, pentru activități ce necesită o lățime de bandă mică. Odată cu răspândirea Internetului și dezvoltarea de aplicații de tip transmisii live audio și video.

Să ne imaginăm un post de radio care emite on-line și că sunt la un moment dat 100.000 de ascultători, fiecare cu o rată de transfer medie de 128 Kb. Ar rezulta că server-ul care se ocupă de transmisie are trebui să aibă o viteză de transmisie de:

128 Kb * 100.000=12,8 Gb/s

ceea ce este enorm atât din punct de vederea al costurilor pentru infrastructură, cât și a costurilor la energia electrică.

Aplicațiile care folosesc UDP ca protocol de transport nu au un mecanism de detecție a pierderilor de date și control al congestiilor adaptiv cum are protocolul TCP. Însă acest mecanism poate fi inclus de programator în aplicație care poate fi mult mai performant decât cel inclus în TCP. Acest mecanism poate realiza următoarele funcții:

Reduce rezoluția datelor (de exemplu, trimite date audio/video cu o fidelitate mai mică reducând frecvența sau rata frame-urilor) pentru a reduce rata de transfer;

Reducerea ratei de transfer a unui flux de date pentru a favoriza transmiterea altui flux cu prioritate mai mare (de exemplu, sacrifică transmisia video pentru transmisia audio);

Transmite datele cu o rată de transfer mai mică (de exemplu, cu o codare diferită) la o adresă sau/și port multicast separate pentru cei cu rate de pierdere a datelor mare.

5.3.2. Mesaje broadcast

Mesajele broadcast sunt mesaje transmise de un echipament din rețea tuturor celorlalte echipamentelor din rețea. Aceste mesaje sunt folosite când informația este necesară tuturor utilizatorilor din rețea sau când cel ce emite nu știe adresa exactă a destinatarului.

Mesaj broadcast

Rețea

Figura 5.2 – Emiterea unui mesaj broadcast

Transmisiile broadcast se fac folosind adrese speciale rezervate pentru acest tip de transmisie. Adresele de broadcast sunt adrese IP în care câmpul rezervat gazdei are toți biții 1. De exemplu, pentru a transmite un mesaj tuturor gazdelor dintr-o subrețea cu masca 255.255.255.0, iar adresele sunt în domeniul 192.168.0, atunci adresa de broadcast va fi 192.168.0.255. Astfel, orice gazdă din rețea a cărei adresă începe cu 192.168.0 va primi mesajul.

Mesajele broadcast se transmit, fără excepție, folosind UDP, pentru că ar fi neperformant să se aloce pentru fiecare destinatar în parte resurse pentru a-i transmite mesajul. Astfel se alocă aceleași resurse, indiferent de numărul gazdelor din rețea (chiar dacă acestea ascultă sau nu mesajul).

O problemă a mesajelor broadcast este aceea că nu pot fi transmise în subrețele separate, deoarece router-ele nu permit trecerea mesajelor broadcast prin ele din considerente de fluența a traficului (rețeaua poate fi congestionată dacă un server, de exemplu, ar transmite mesaje broadcast des).

5.3.3. Mesaje multicast

Mesajele multicast sunt un compromise între cele două tipuri de mesaje prezentate mai sus: sunt trasnmise unui grup de stații de lucru care îndeplinesc un set de criterii bine definite. Aceste stații de lucru sunt legate între ele printr-o anumită relație (fie realizează anumite funcții comune sau fac parte dintr-un grup multicast). De remarcat că mesajul broadcast poate fi considerat un caz special de caz mesaj multicast către grupul multicast “toate stațiile din rețea”.

Mesajul multicast este cel mai complex tip de mesaj, deoarece acesta are nevoie de mijloacele necesare identificării unui set finit de echipamente din rețea care vor primi mesajul. De asemenea, crearea mai multor grupuri poate face ca o stație de lucru să aparțină mai multor grupuri, deci e nevoie de un mecanism de a identifica apartenența unei stații la un grup (stațiile dintr-un grup pot face parte din subrețele diferite).

Retea locală

Figura 5.3 – Emiterea unui mesaj multicast către un grup

Aplicațiile care folosesc tehnica multicast se împart în trei categorii:

Aplicații Unu-la-Mulți (One-to-Many) – o singură stație emite, iar două sau mai multe stații recepționează (figura 5.4). Exemple de aplicații de acest fel sunt numeroase: transmisiile live audio și video, monitorizarea burselor, etc.;

Figura 5.4 – Transmisie multicast de tip One-to-Many

Aplicații Mulți-la-Mulți (Many-to-Many) – mai multe stații de lucru emit în același grup multicast, dar și recepționează în același timp (figura 5.5). Aplicațiile care folosesc acest tip de comunicație sunt: conferințe multimedia (audio/video), procesarea distribuită, grupuri de chat, jocuri on-line, etc. ;

Figura 5.5 – Transmisie multicast de tip Many-to-Many

Aplicații Mulți-la-Unu (Many-to-One) – mai multe stații emit mesaje spre o singură stație din grupul multicast (figura 5.6).Exemplu de aplicații: colectarea de date de la mai mulți senzori, licitații on-line, etc.

Figura 5.6 – Transmisie multicast de tip Many-to-One

În cazul aplicațiilor multicast, mecanismul de feedback pentru pierderea de date trebuie modificat. Dacă toți receptorii trimit simultan la sursă rapoarte de pierdere a datelor, atunci aceasta este copleșită de numărul de rapoarte și se blochează. Această problemă este cunoscută drept “problema imploziei”.

O altă problemă care poate apare este aceea că receptorii sunt eterogeni din punctul de vedere al lățimii de bandă și al vitezei de transmisie. Dacă sursa transmisiei își adaptează viteza de transfer și rezoluția datelor în funcție de rata de pierdere a datelor pentru cel mai “slab” participant la transmisie, atunci ceilalți cu transfer mai bun ar avea de suferit în principal datorită rezoluției datelor (de exemplu, fidelitate mică la transmisia audio/video). Strategii de rezolvare a acestei probleme există. Iată două dintre ele:

1) Când este detectată pierderea unui pachet, receptorul pornește un timer. Dacă în timpul așteptării se recepționează un raport de pierdere a datelor de la alt receptor, atunci oprește timer-ul și nu mai transmite un raport de pierdere de pachet. Dacă nu recepționează acel raport, atunci trimite la sursă un raport de pierdere de date. O astfel de strategie este implementată de RTCP (strategia poartă denumirea de “shared learning”);

2) Unii receptori pot fi doar puncte intermediare într-o transmisie (transcoder), care pot retrtansmite pachetele pierdute (posibil folosind o transmisie unicast) sau pot folosi o altă codare, pentru cei cu lățime de bandă mai mică, înainte de a retransmite pachetele pierdute (această strategie se numește “local recovery”)

5.4. Scenarii de utilizare a comunicațiilor UDP

În acest scenariu voi descrie realizarea unei comunicații peer-to-peer (în traducere liberă “de la egal la egal” – adică ambele calculatoare au aceeași prioritate), între două calculatoare folosindu-se un server intermediar. De asemenea, în rețea există NAT-uri (Network Address Translation – Translatoare de Adresa IP). Aceste NAT-uri sunt echipamente folosite pentru a proteja adresele IP private (calculatoare aflate în rețele private), adică, la trecerea printr-un NAT, adresei IP private îi este asignată altă adresă publică (în consecință aceste NAT-uri fac legătura între adresa IP privată și cea publică). În afară de translatarea adreselor IP, NAT-urile translatează și porturile de comunicație (acastă facilitate este obținută prin folosirea NAPT – Network Adress/Port Translation, o extensie a NAT).

În scenariu voi folosi termenul sesiune, care înseamnă comunicația între două calculatoare, fiecare calculator fiind descris de o pereche (adresă IP, port).

Schema generală a unei astfel de rețele este prezentată în figura 5.7.

(Adrese IP globale)

Internet

(Adrese IP private)

Figura 5.7 – Rețea cu adrese IP publice și private

În aceast nou tip de organizare, cu adrese IP globale și private, pot fi contactate cu ușurință doar nodurile cu adresă globală, pentru că doar acestea au adrese IP unice (aceleași adrese IP private pot fi asignate în rețele diferite). Este dificil pentru ca două calculatoare din rețele private să comunice direct, de aceea se folosește o arhitectură client/server, adică calculatoarele care vor să comunice vor face conexiunea printr-un server. De asemenea este nevoie și de un mecanism eficient și robust prin care să se poată comunica, chiar dacă pe calea de comunicație există unul sau mai multe NAT-uri. Acest mecanism se numește UDP hole punching (în traducere liberă crearea unei breșe). Contrar cu denumirea, acest mecanism nu crează breșe în securitatea rețelei private, ci doar oferă aplicațiilor posibilitatea de a semnaliza NAT-urilor o cerere de comunicație.

Să presupunem că clientul A dorește să comunice cu clientul B. Mecanismul UDP hole punching procedează astfel:

Inițial A nu știe cum să-l contacteze pe B, deci cere server-ului S să-l ajute să stabilească o sesiune cu B;

Serverul S răspunde lui A cu un mesaj care conține adresa IP publică și privată a lui B precum și porturile de comunicare. În același timp, S trimite lui B o cerere de conectare, conținând adresa IP publică și privată și porturile lui A. Odată cu recepția acestor mesaje, ambii clienți știu adresele private și publice precum și porturile celuilalt;

Când A primește adresa IP privată și publică a lui B de la S, atunci acesta începe să transmită pe ambele adrese primite (cea privată și cea publică), până primește un răspuns valid de la una din adresele lui B, iar B face același lucru la primirea adreselor lui A.

5.4.1. UDP hole punching pentru două calculatoare sub același NAT

Cel mai simplu scenariu este atunci când ambele calculatoare se află sub același NAT, deci adresle lor IP private sunt din același domeniu. Acest lucru este ilustrat în figura 5.8.

Clientul A a stabilit o sesiune UDP cu server-ul S, pentru care NAT-ul a asignat portul public 62000. Similar, clientul B a stabilit o sesiune UDP cu server-ul S, pentru care, același NAT, a stabilit portul public 62005.

Să preupunem că clientul A folosește tehnica hole punching descrisă mai sus pentru a stabili o sesiune UDP cu clientul B, folosind server-ul S pentru stabilirea conexiunii. Clientul A trimite server-ului S o cerere de conectare la B. Server-ul S

răspunde lui A cu un mesaj conținând adresele IP publică și privată, precum și porturile publice și private ale lui B. În același timp, trimite și lui B aceleași date despre A. Apoi ambii clienți încearcă să trimită datagrame UDP (unul celuilalt), folosind atât adresa IP privată cât și cea privată (implicit folosind porturile asignate). Comunicația se va face pe ruta cea mai rapidă, deci vor fi folosite adresele private ale clienților. Acest lucru este posibil datorită unei facilități ale NAT-urilor de a nu mai trece datele transmise prin server-ul care a intermediat conectarea clienților, facilitate numită “hairpin translation” (în traducere liberă translatare în ac de păr).

5.4.2. UDP hole punching pentru două calculatoare sub NAT-uri diferite

Să presupunem că ambii clienți, A și B, au adrese IP private aflate sub două NAT-uri diferite cum este ilustrat în figura 5.9. Atât A, cât și B au inițiat sesiuni UDP cu server-ul S de pe portul lor local 4321 la portul 1234 al server-ului. Pentru manevrarea acestor sesiuni, NAT-ul A a asignat portul 62000 la adresa sa publică 155.99.25.11, pentru sesiunea lui A cu server-ul, iar NAT-ul B a asignat portul 31000 la adresa sa publică 138.76.29.7, pentru sesiunea lui B cu server-ul.

În mesajul de înregistare a lui A la S, clientul A transmite lui S adresa IP privată și portul privat (10.0.0.1 : 4321). Server-ul S memorează aceste date și mai adaugă la înregistrare adresa IP publică și portul public a lui A (155.99.25.11 : 62000). Similar B se înregistrează la server-ul S cu datele private (10.1.1.3 : 4321), la care S mai adaugă datele publice (138.76. 29.7 : 31000).

În continuare, clientul A folosește procedeul hole punching, pentru a realiza o sesiune directă cu clientul B. A trimite un mesaj lui S pentru a se conecta la B. Drept urmare, S trimite lui A adresa IP publică și privată, respective porturile de conectare, iar în același timp trimite aceleași date, despre A, clientului B. Apoi clienții încep transmisia de datagrame UDP pe fiecare din aceste adrese.

Deoarece A și B sunt în rețele diferite, iar adresele lor private nu sunt rutabile la nivel global, mesajele pot ajunge greșit la alte calculatoare. Acest lucru este posibil deoarece adresa privată a lui B poate exista în rețeaua privată a lui A și deci mesajele pot ajunge la un calculator cu aceeași adresă IP privată ca și B din rețeaua privată a lui A.

Deci este nevoie ca aplicațiile care comunică să realizeze o autentificare a mesajelor înainte de a începe comunicația.

Să considerăm acum primul mesaj UDP pe care-l trimite clientul A spre adresa publică a clientului B. Când acest mesaj trece prin NAT-ul A, acesta își dă seama că este primul pachet UDP dintr-o nouă sesiune. Această sesiune are aceeași sursă ca și sesiunea în curs dintre A și S (10.0.0.1 : 4321), dar este altă destinație. NAT-ul reține datele private ale lui A și translatează toate sesiunile de la adresa și portul privat a lui A (10.0.0.1 : 4321) la adresa și portul public (155.99.25.11 : 62000). Astfel primul mesaj UDP crează o breșă în NAT-ul A pentru o nouă sesiune UDP identificată cu adresa/portul (10.0.0.1 :4321, 138.76.29.7 : 31000) în rețeaua privată a lui A și adresa/portul (155.99.25.11 : 62000, 138.76.29.7 : 31000) pe Internet.

Dacă mesajul de la A ajunge la adresa/ portul public a lui B înainte ca primul mesaj de la B să treacă prin NAT-ul B, atunci NAT-ul B poate considera mesajul de la A ca trafic nedorit și să nu-l lase să treacă. Primul mesaj de la B la adresa publică a lui A crează o breșă în NAT-ul B pentru o nouă sesiune UDP identificată cu adresa/portul (10.1.1.3 :4321, 155.99.25.11 : 62000) în rețeaua privată a lui B și adresa/portul (138.76.29.7 : 31000, 155.99.25.11 : 62000) pe Internet. Odată trecute primele mesaje de la A și B prin NAT-urile corespunzătoare lor, sunt deschise breșe de comunicație UDP în ambele sensuri. Dacă clienții A și B observă că sesiunea dintre adresele publice funcționează, ei pot opri sesiunea dintre adresele private.

5.4.3. UDP hole punching pentru două calculatoare sub mai multe nivele de NAT-uri

În anumite topologii de rețele care cuprind mai multe nivele de NAT, doi clienți nu pot stabili o comunicație peer-to-peer optimă fără a ști topologia în cauză. Considerăm scenariul ilustrat în figura 5.10.

Presupunem că NAT-ul C este un NAT “industrial” (de capacitate mare) introdus de provider-ul de Internet pentru multiplexarea mai multor adrese private în adrese publice. NAT-urile A și B sunt introduse de două rețele private conectate la Internet prin intermediul NAT-ului C. Doar server-ul S și NAT-ul C au adrese publice rutabile global, pe când adresele publice ale NAT-urilor A și B sunt private pentru NAT-ul C. De

asemenea adresele calculatoarelor din rețelele private de sub NAT-urile A și B sunt adrese private pentru aceste NAT-uri.

Fiecare din clienții A și B inițiază o cerere de conexiune spre server-ul S. NAT-urile A și B realizează fiecare câte o translație privat/public pentru cereri, iar NAT-ul C realizează câte o translație privat/public pentru fiacre cerere.

Acum să presupunem că A și B încearcă să stabilească o conexiune UDP directă prin hole punching. Strategia optimă ar fi ca clientul A să trimită mesaje spre adresa “semi-publică” a lui B la NAT-ul B (10.0.1.2:55000), iar B să trimită mesaje la adresa “semi-publică” a lui A la NAT-ul A (10.0.1.1:45000). Din păcate, A și B nu au de unde să afle aceste adrese, deoarece server-ul S nu vede decât adresele globale furnizate de NAT-ul C (155.99.25.11: 62000, respectiv 155.99.25.11 : 62005).

În consecință, clienții A și B nu pot decât să folosescă adresele globale văzute de server-ul S, și să se bazeze pe NAT-ul C pentru a nu mai trece datele prin server-ul S (hairpin translation). Când A transmite o datagramă UDP spre adresa globală a lui B (155.99.25.7 : 62005), NAT-ul A translatează adresa sursei din datagramă din 10.0..0.1:4321 în 10.0.1.1:45000. Datagrama ajunge apoi la NAT-ul C, care recunoaște adresa destinație ca fiind una din adresele sale publice translatate. NAT-ul C translatează apoi ambele adrese și retransmite pachetul cu adresa sursă 155.99.25.11 : 62000 și adresa destinație 10.0.1.2 : 55000. NAT-ul ce recepționează datagrama, translatează adresa destinație în 10.1.1.3 :4321 și pachetul ajunge la clientul B. Drumul parcurs de datagrame de la B la A este similar cu cel descries mai sus.

Deoarece protocolul de transport UDP nu furnizează NAT-urilor un serviciu care să determine timpul de viața a unei sesiuni UDP printr-un NAT, majoritatea NAT-urilor inchid sesiunea UDP dacă nu există trafic pentru o anumită perioadă de timp. Nu există o perioadă standard (unele NAT-uri sunt setate pentru chiar pentru 20 secunde). Dacă o aplicație are nevoie să mențină o sesiune UDP deschisă prin mecanismul hole punching, este nevoie ca aceasta să trimită pachete periodic, pachete numite keep-alive packets. Pentru a nu trimite aceste pachete periodic, multe aplicații detectează când o sesiune UDP nu mai funcționează și repornesc procedeul hole punching la cerere.

=== Capitolul 6 ===

Capitolul 6

Funcționarea aplicației

Proiectul folosește programarea orientată pe obiecte (POO). Fiecare fereastră grafică componentă este descrisă de o clasă de obiecte. De asemenea există clase care nu definesc neapărat o fereastră grafică, ci anumite funcții necesare conectării a două calculatoare și transmisiei de pachete între ele (DSocket), pentru preluarea semnalului de la placa de sunet sau redarea pachetelor audio recepționate (CRecordSound și PlaySound1). De asemenea, unele clase au fost preluate și adaptate la aplicație de pe site-ul www.codeguru.com.

Pentru a putea iniția o comunicație este necesar ca ambele calculatoare să aibă plăci de sunet, plăci de rețea, sunt conectate într-o rețea, iar aplicația să ruleze simultan pe ambele calculatoare care vor comunica.

Această aplicație realizează o eșantionare la 8 KHz, fiecare eșantion fiind reprezentat pe 8 biți. Fiecare pachet transmis conține 20 ms de captură audio, adică 160 octeți de date.

6.1. Fereastra Transmisie

Fereastra Transmisie face partea din clasa CTransmisieDlg. La rularea programului Transmisie.exe apare fereastra Transmisie cu următoarele câmpuri: Placa de rețea ( conține numele plăcii de rețea), MAC (conține adresa MAC a plăcii de rețea, adresă care este unică la nivel mondial – nu există în lume două plăci cu aceeași adresă MAC), Adresa IP locală, Subnet Mask (Masca subrețelei), Adresa IP remote (va conține adresa de conectare a aplicației), Port audio, Port control, o zonă de statistici (ce conține numărul pachetelor transmise și numărul pachetelor recepționate) și trei butoane: Conectare, Terminare conexiune și Ieșire.

6.1.1. Inițializarea câmpurilor Placa de rețea, MAC, Subnet Mask

Inițializarea acestor câmpuri se face apelând la pornirea programului funcția void NICRefresh(). Aceasta la rândul ei apelează funcțiile int GetNICInfo() (pentru a afla numărul plăcilor de rețea și pentru a obține descrierea fiecăreia dintre ele) și void DisplayNICInfo(UINT NICIndex, tSTRUCTNICINFO *pNICInfo) pentru afișarea datelor dorite (date care în prealabil sunt formatate) despre placa de rețea existentă în sistem.

Funcția GetNICInfo() interoghează sistemul gazdă dacă acesta conține plăci de rețea. Dacă nu există nici o placă de rețea întoarce o eroare.

Dacă există cel puțin o placă de rețea atunci funcția execută următoarele operații:

Memorează numărul de plăci de rețea în variabila m_NICCount;

Pentru fiecare placă de rețea inițializează o structură de date ce conține următoarele câmpuri: MACLength, MAC, SubnetMask, Description.

Valorile acestor câmpuri din structură sunt atribuite folosind funcții predefinite în clasele MibII și MibExtLoad.

Astfel, pentru fiecare placă de rețea vor fi reținute următoarele date: denumirea plăcii de rețea, Adresa MAC și masca de subrețea. Aceste date sunt memorate în format hexazecimal. În vederea formatării datelor pentru afișare se apelează funcția DisplayNICInfo().

Funcția void DisplayNICInfo (UINT NICIndex, tSTRUCTNICINFO *pNICInfo) formatează datele pentru a fi afișate într-un format inteligibil și nu în format hexazecimal. De asemenea datele sunt transformate în șiruri de caractere pentru afișare. Această formatare se face folosind comanda Format astfel: m_NICInfoSubnetMask.Format("%d.%d.%d.%d",pNICInfo->SubnetMask[0], pNICInfo->SubnetMask[1], pNICInfo->SubnetMask[2], pNICInfo->SubnetMask[3]). Formatul variabilei m_NICInfoSubnetMask după formatare va fi, de exemplu, 255.255.255.0 și nu numărul hexazecimal 0xFFFFFF00. După formatare, datele sunt afișate.

Parametrii de apelare al funcției sunt:

UINT NICIndex – reprezintă numărul de ordine al plăcii de rețea, dacă sunt mai multe astfel de plăci, sau este zero când este doar o singură placă de rețea.

tSTRUCTNICINFO *pNICInfo – este un pointer la structura tSTRUCTNICINFO, structură ce conține datele despre placa de rețea.

6.1.2. Inițializarea câmpului Adresa IP locala

Inițializarea câmpului Adresa IP locala se face apelând la inițializarea ferestrei Transmisie funcția void OnScanareLocal(). Rezultatul întors de apelarea funcției este memorat în variabila m_strLocal de tip CString, care este apoi afișată.

Funcția void OnScanareLocal() funcție realizează următoarele acțiuni:

Inițializează versiunea de Windows Sockets pe care o va folosi (în cazul nostru este versiunea 2.0) (wVersionRequested = MAKEWORD( 2, 0 ))

Dacă versiunea aleasă este suportată de sistem, atunci se verifică dacă sistemul are un nume (gethostname ( name, sizeof(name)) == 0), iar dacă acesta există atunci în variabila hostinfo sunt memorate informațiile existente ale gazdei cu numele name (hostinfo = gethostbyname(name))

În variabila ip de tip CString este memorată adresa IP (ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list)). De remarcat că funcția inet_ntoa formatează deja adresa IP în forma cunoscută, de exemplu, 192.168.0.100

Se memorează adresa IP în variabila m_strLocal și se afișează (m_strLocal=ip)

Se încheie folosirea Windows Sockets prin comanda WSACleanup().

6.1.3. Inițializarea câmpului Adresa IP remote

Inițializarea acestui câmp se face abia după realizarea conectării celor două calculatoare în vederea transmisiei de voce.

6.1.4. Inițializarea câmpurilor Port audio și Port control

Aceste câmpuri sunt definite drept constante (#define PORT_CONTROL 30001

#define PORT_AUDIO 30000), ele nefiind modificate ulterior în timpul rulării programului.

La definirea acestor constante am luat în calcul următoarele cerințe:

Pentru folosirea protocolului RTP se utilizează un port par pentru transmisia audio și portul imediat următor pentru mesaje de control (în cazul nostru 30000 pentru audio și 30001 pentru mesaje de control);

Porturile cu valori peste 16000 și până la 32263 au o prioritate mai mare în utilizare decât celelalte porturi.

6.1.5. Zona de statistici

În această zonă sunt următoarele câmpuri Pachete transmise și Pachete recepționate. Aceste câmpuri au definite două variabile X (pentru numărul de pachete trimise) și Y (pentru numărul de pachete recepționate). La fiecare pachet trimis se incrementează X și se afișează. La fel pentru fiecare pachet recepționat. De remarcat că transmisia se face cu rata de un pachet la fiecare 20 de milisecunde.

6.1.6. Butonul Conectare

Acționarea butonului Conectare duce la apelarea funcției void OnBconnect(). Acest buton este dezactivat odată cu realizarea conexiunii între cele două calculatoare și începerea conferinței.

Funcția OnBconnect() apelează fereastra Conectare (conectaredlg->DoModal()). Pointerul conectaredlg este de tip CConectareDlg, care este clasa din care face parte fereastra Conectare.

6.1.7. Butonul Terminare conexiune

Inițial acest buton este dezactivat, dar este activat după conectare și începerea conferinței. Acționarea butonului în aceste condiții duce la apelarea funcției void OnDisconnect().

Operațiile efectuate de această funcție sunt:

Se trimite un mesaj de control prin care utilizatorul remote este anunțat că utilizatorul local s-a deconectat

dcontrol.SendControlMessage(MESG_DISCONNECT,NULL)

Se apelează funcția void DestroyConference()

Se șterge din afișare adresa IP remote (SetDlgItemText(IDC_IP_REMOTE,"")).

Funcția DestroyConference() efectuează următoarele operații:

Verifică dacă este pornită conferința. Dacă este pornită se atribuie variabilei isStarted valoarea FALSE

Reactivează butonul Conectare și se dezactivează butonul Terminare conexiune

Suspendă firul de execuție pentru preluarea sunetului de la placa de sunet

Închide Windows Sockets (daudio.CloseSocket())

Redă și ultimele date audio din buffer.

6.1.8. Butonul Iesire

Acționarea butonului Iesire duce la apelarea funcției void OnExit(). Aceasta afișează o fereastră obișnuită de Windows cu trei opțiuni: YES, NO, CANCEL. Acționarea butonului YES produce următoarele acțiuni:

Dacă este pornită conferința, se transmite un mesaj de deconectare spre stația remote (dcontrol.SendControlMessage(MESG_DISCONNECT,NULL)) , se încheie conferința (DestroyConference()) și se încheie rularea programului. Dacă nu este pornită conferința, se încheie doar rularea programului.

6.2. Fereastra Conectare

Funcționalitatea acestei ferestre este încorporată de clasa CConectareDlg. Fereastra apare în momentul în care se acționează butonul Conectare din fereastra Transmisie.

Fereastra Conectare conține o listă pentru afișarea calculatoarelor care se găsesc conectate în grupul de lucru curent (acestea apar sub forma Nume->Adresa IP), un câmp pentru introducerea adresei IP corespunzătoare calculatorului cu care vrem să comunicăm și trei butoane ( Scanare adrese, Conectare și Renunțare).

6.2.1. Butonul Scanare adrese

Acționarea butonului Scanare adrese duce la apelarea funcției void OnScanare(). Această funcție realizează următoarele acțiuni:

Inițializează lista în care vor fi afișate numele calculatoarelor și adresele lor (pList->ResetContent(), unde pList este un pointer la lista CListBox)

Se începe enumerarea resurselor conectate la momentul respectiv în grupul de lucru curent (WNetOpenEnum( dwScope, NULL, NULL, NULL, &hEnum ))

Inițializează versiunea de Windows Sockets pe care o va folosi (în cazul nostru este versiunea 2.0) (WSAStartup(MAKEWORD(2,0),&wsaData)))

Se numără resursele, iar numărul lor este memorat în variabila Count

Pentru fiecare resursă se extrage numele (memorat în variabila strFullName) și adresa IP (în pointerul ptr)

Se formatează adresa IP în format zecimal cu punct (de ex. 192.168.0.100)

Se formatează tipul afișării:

strTemp.Format("%s –> %d.%d.%d.%d",strFullName,a,b,c,d)

Se adaugă în listă variabila strTemp (pList->AddString(strTemp))

După terminarea afișării tuturor resurselor se eliberează buffer-ul unde au fost memorate resursele (delete Buffer), se încheie enumerarea (WNetCloseEnum( hEnum )) și se încheie folosirea Windows Sockets prin comanda WSACleanup().

6.2.2. Câmpul Adresa IP

În acest camp se introduce adresa IP corespunzătoare calculatorului cu care dorim să comunicăm. Adresa IP se introduce sub formatul cunoscut, de exemplu 192.168.0.100. De asemenea, în locul adresei IP se poate introduce numele calculatorului cu care vrem să comunicăm (așa cum apare el la scanarea rețelei). Introducerea numelui nu este key sensitive (adică nu este important dacă scriem cu majuscule, minuscule sau cu ambele tipuri de scriere).

6.2.3. Butonul Conectare

După introducerea adresei IP sau a numelui calculatorului cu care dorim să ne conectăm se acționează butonul Conectare. Acest lucru duce la apelarea funcției void OnConnect().

Această funcție realizează următoarele acțiuni:

Memorează în variabila hostname adresa introdusă în câmpul Adresa IP

this->GetDlgItemText(IDC_EDIT1,hostname,200)

Trimite un mesaj de control la calculatorul cu care se dorește conectarea

((CTransmisieDlg*)mdlg)->dcontrol.SendControlMessage(MESG_CONNECT,hostname)

6.2.4. Butonul Renunțare

Acționarea butonului Renuntare duce la apelarea funcției void OnCancel() care ne readuce în fereastra Transmisie.

6.3. Fereastra Invitatie

Funcționalitatea acestei ferestre este încorporată de clasa CCerereDlg. Fereastra apare la calculatorul cu care dorim să comunicăm în momentul în care se acționează butonul Conectare din fereastra Conectare (după introducerea adresei IP corespunzătoare) de la calculatorul care inițiază comunicația.

Fereastra conține un mesaj prin care se solicită acceptarea conexiunii și două butoane: Acceptare și Respingere.

6.3.1. Butonul Acceptare

Acționarea acestui buton duce la apelarea funcției void OnAccept(). Această funcție realizează următoarele acțiuni:

Transmite o notificare spre calculatorul care a inițiat comunicația că s-a acceptat conexiunea

((CTransmisieDlg*)pdlg)->dcontrol.SendControlMessage(MESG_ACCEPT, NULL)

Se pornește conferința apelând funcția void StartConference()

((CTransmisieDlg*)pdlg)->StartConference()

Funcția void StartConference() realizează următoarele acțiuni:

Inițializează variabila isStarted cu valoarea TRUE (isStarted=TRUE)

Dezactivează butonul Conectare și activează butonul Terminare conexiune din fereastra Transmisie

GetDlgItem(IDC_BCONNECT)->EnableWindow(FALSE)

GetDlgItem(IDC_DISCONNECT)->EnableWindow(TRUE)

Crează socketul de comunicare audio

daudio.CreateSocket(PORT_AUDIO,TYPE_AUDIO)

Preia sunetul de la placa de sunet în vederea transmiterii

record->PostThreadMessage(WM_RECORDSOUND_STARTRECORDING,0,0)

Redă datele audio din buffer-ul de recepție

play->PostThreadMessage(WM_PLAYSOUND_STARTPLAYING,0,0)

6.3.2 Butonul Respingere

Acționarea acestui buton duce la apelarea funcției void OnReject(). Această funcție realizează o singură acțiune și anume transmiterea unui mesaj de respingere a conexiunii către calculatorul care a inițiat conectarea, după care se revine la fereastra Transmisie.

((CTransmisieDlg*)pdlg)->dcontrol.SendControlMessage(MESG_REJECT, NULL)

=== Capitolul 7 ===

Capitolul 7

Calcul economic

7.1. Generalități

Costul de producție este o categorie economică legată de existența producției de mărfuri, de procesul de formare a valorii și de prețuri.

Calculul economic reprezintă un calcul foarte amănunțit al costului de producție al programelor respective.

În sfera producției materiale, costul de producție este forma bănească a unui conținut ce reprezintă consumul de mijloace materiale și forță de muncă, necesare pentru producerea și desfacerea bunurilor materiale. El include tot ceea ce înseamnă cheltuială de producție suportată de întreprinzător pentru producerea și desfacerea bunului respectiv.

Între costul de producție și prețul de vânzare există deosebiri atât cantitative, cât și calitative. Astfel prețul este mai mare decât costul de producție incluzând în plus și profitul. Deosebirea calitativă este că, în timp ce prețul asigură mijloacele necesare producției lărgite, costul de producție asigură doar recuperarea cheltuielilor de producție.

Potrivit legislației în vigoare, în țara noastră costul de producție este împărțit în următoarele grupe de cheltuieli:

Cheltuieli materiale (materii prime, energie, materiale și combustibili) Cmp.

Cheltuieli directe cu munca vie (retribuții directe plătite muncitorilor,

impozit pe fondul de retribuții directe Ifr, contribuții pentru asigurări sociale Cas).

Cdmv = Rd + Ifr + Cas

– Contribuții la fondul de cercetări științifice;

– Impozite (pe clădiri);

– Fond pentru ajutor de șomaj, alte cheltuieli.

Elementele componente ale costului de producție se modifică de la o

perioadă de timp la alta sub influența factorilor externi și interni.

Mărimea costului de producție exprimă toate cheltuielile cu mijloacele de producție și plata salariilor, cheltuieli ce se efectuează pentru producerea și desfacerea bunurilor de materiale.

Reducerea costului de producție înseamnă micșorarea cheltuielilor pe unitatea de produs și este o necesitate obiectivă impusă de creșterea rentabilității, sporirea profitului și a productivității muncii.

Diminuarea costului de producție se poate face pe mai multe căi:

Prin reducerea costului materialelor;

Prin utilizarea eficientă a capitalului fix;

Prin creșterea productivității muncii;

Prin reducerea cheltuielilor administrativ – gospodărești.

La efectuarea calculului economic se poate ține cont și de o serie de

costuri, cum ar fi:

Costul fix se referă la cheltuieli independente de volumul producției

(chirii, amortizarea mașinilor, a clădirilor, etc.);

Costul variabil se modifică odată cu modificarea volumului de

producție;

Costul marginal exprimă sporul de cheltuieli necesare pentru obținerea

unei unități suplimentare de produs;

Costul cercetării științifice este dat de cheltuielile pentru cercetarea

propriu – zisă și pentru aplicarea în practică a rezultatelor activității de cercetare- -proiectare în vederea realizării prototipului;

Costul tehnologic se caracterizează prin individualizarea cheltuielilor

directe și a unei părți însemnate din cheltuielile indirecte, în special cu întreținerea și folosirea utilajelor.

Procesul de formare al costului de producție este dat de nivelul secției de cheltuielile directe la care se adaugă cheltuielile cu întreținerea și funcționarea utilajelor (CIFU), cheltuieli generale ale secției cu munca vie (CMDV), care sunt necesare în scopul asigurării necesităților de iluminare și încălzire, etc.

Cd – reprezintă cheltuieli directe la care se adaugă cheltuielile necesare pentru materii și materiale și cheltuielile directe cu munca vie, din care se scade costul materialelor refolosibile și recuperabile (C)

Cd = Cmp + Cdmv – C

unde: – Cmp – cheltuieli directe cu materii prime și materiale.

Valoarea aparatului se calculează cu relația următoare:

I = Pa + Cm + Pc + Ct

unde: – I – investiția;

Pa – prețul aparatului;

Cm – cheltuieli de montaj (asamblare);

Ct – cheltuieli pentru transport.

În prețul aparatului intră: prețul pentru circuite integrate, pentru componente pasive, pentru cablajul imprimat, cheltuieli pentru carcasă, cheltuieli pentru probe, etc.

7.2. Calculul economic pentru aplicație

Programarea ca formă de muncă este plătită cu circa 25Euro/zi la o normă de 40 de linii de program, această sumă fiind variabilă în funcție de țară, nivel de trai, etc.

În cazul nostru avem circa 859 de linii de program realizate în Visual C++.

Nr. linii program la zi = 40

Nr. total linii =859

Nr. zile = Nr. total linii / Nr. linii program la zi

Nr. zile = 859 / 40 = 22 zile

Această sumă poate varia de la firmă la firmă în funcție de prețul pe linia de program sau de norma pe care o are un programator.

După un calcul elementar, cheltuielile pentru realizarea aplicației sunt de 4831 $. Pentru producerea în masă a aplicației, de exemplu 10000 de copii, cu un profit estimat de 20%, costul aplicației este de 2 $. Profitul în acest caz este 3300 $.

Acest calcul este pur estimativ. Am considerat că un singur programator a lucrat la dezvoltarea aplicației. Nu am luat în considerare timpul petrecut pentru documentare.

=== Desene ===

Server S Server S Server S

(18.181.0.31) (18.181.0.31) (18.181.0.31)

Sesiunea A-S Sesiunea B-S Sesiunea A-S Sesiunea B-S

18.181.0.31:1234 18.181.0.31:1234 18.181.0.31:1234 18.181.0.31:1234

155.99.25.11:62000 155.99.25. 11:62005 155.99.25.11:62000 155.99.25.11:62005

Sesiune A-S Sesiune B-S (a) (a) Sesiune A-S Sesiune B-S

18.181.0.31:1234 18.181.0.31:1234 18.181.0.31:1234 18.181.0.31:1234

10.0.0.1:4321 10.1.1.3:4321 10.0.0.1:4321 10.1.1.3:4321

(b)

(b) Sesiune A-B

Client A Client B Client A Client B Client A 10.0.0.1:4321 Client B

(10.0.0.1) (10.1.1.3) (10.0.0.1) (10.1.1.3) (10.0.0.1) 10.1.1.3:4321 (10.1.1.3)

Înainte de UDP hole punching Procedeul UDP hole punching După UDP hole punching

Figura 5.8 – Calculatoare aflate sub același NAT

Server S Server S Server S

(18.181.0.31) (18.181.0.31) (18.181.0.31)

Sesiunea A-S Sesiunea B-S Sesiunea A-S Sesiunea B-S

18.181.0.31:1234 18.181.0.31:1234 18.181.0.31:1234 18.181.0.31:1234

155.99.25.11:62000 138.76.29.7:31000 155.99.25.11:62000 138.76.29.7:31000

Sesiunea A-B

155.99.25.11:62000

138.76.29.7:31000

Sesiunea A-S Sesiunea B-S

18.181.0.31:1234 18.181.0.31:1234

10.0.0.1:4321 10.1.1.3:4321 Sesiunea A-S Sesiunea A-B

18.181.0.31:1234 138.76.29.7:31000

10.0.0.1:4321 10.0.0.1:4321

Sesiunea B-S

Sesiune A-B 18.181.0.31:1234

155.99.25.11:62000 10.1.1.3:4321

(a) (a) 10.1.1.3:4321

(b) (b)

Client A Client B Client A Client B Client A Client B

(10.0.0.1) (10.1.1.3) (10.0.0.1) (10.1.1.3) (10.0.0.1) (10.1.1.3)

Înainte de UDP hole punching Procedeul UDP hole punching După UDP hole punching

Figura 5.9 – Calculatoare aflate sub NAT-uri diferite

Server S Server S Server S

(18.181.0.31) (18.181.0.31) (18.181.0.31)

Sesiunea A-S Sesiunea B-S Sesiunea A-S Sesiunea B-S

18.181.0.31:1234 18.181.0.31:1234 18.181.0.31:1234 18.181.0.31:1234

155.99.25.11:62000 155.99.25.11:62005 155.99.25.11:62000 155.99.25.11:62005

Sesiunea A-S Sesiunea A-B Sesiunea B-S

Sesiunea A-S Sesiunea B-S 18.181.0.31:1234 155.99.25.11:62005 18.181.0.31:1234

18.181.0.31:1234 18.181.0.31:1234 (a) (a) 10.0.1.1:45000 10.0.1.1:45000 10.0.1.2:55000

10.0.1.1:45000 10.0.1.2:55000

Sesiunea A-B

155.99.25.11:62000

10.0.1.2.:55000

Sesiunea A-S Sesiunea B-S

18.181.0.31:1234 18.181.0.31:1234 Sesiunea A-S Sesiunea A-B

10.0.0.1:4321 10.1.1.3:4321 18.181.0.31:1234 155.99.25.11:62005 Sesiunea B-S

10.0.0.1:4321 10.0.0.1:4321 18.181.0.31:1234

10.1.1.3:4321

(b) (b)

Client A Client B Client A Client B

(10.0.0.1) (10.1.1.3) (10.0.0.1) (10.1.1.3) Client A Client B (10.0.0.1) (10.1.1.3)

Înainte de UDP hole punching Procedeul UDP hole punching După UDP hole punching

Figura 5.10 – Calculatoare aflate sub mai multe nivele de NAT

Similar Posts