C ++ uses WinSock to implement full-duplex voice communication
Http://www.4oa.com/Article/html/6/33/486/2005/17465.html
Http://www.4oa.com/Article/html/6/33/486/2005/17465_2.html
I. Introduction
Windows 95, as a microcomputer operating system, has fully integrated the network and communication functions. It can not only establish a "peer-to-peer network" in a Windows 95-only environment, but also support multiple protocols, such as TCP/IP, IPX/SPX, and netbui. In TCP/IP protocol groups, TPC is a connection-oriented collaboration that provides users with reliable and full-duplex byte stream services, it has functions such as validation, flow control, multiplexing and synchronization, and is suitable for data transmission. The UDP protocol is connectionless. Each group carries a complete Destination Address and each group is transmitted independently in the system. It does not guarantee the order of groups, and does not recover and retransmit packet errors. Therefore, it does not guarantee the transmission reliability. However, it provides highly efficient datagram services, suitable for real-time voice, image transmission, broadcast message and other network transmission.
The Winsock interface provides a new method for inter-process communication. It can be used not only for inter-process communication on the same machine, but also for network communication. With the launch of Windows 95. Winsock has been formally integrated into the Windows system, including 16-bit and 32-bit programming interfaces. Winsock development tools can also be found in Borland C ++ 4.0 and Visual C ++ 2.0 C compilers, mainly named Winsock. h header file and dynamic Connection Library Winsock. DLL or wsodk32.dll, these two dynamic connection libraries are used for Win16 and Win32 applications respectively.
In this paper, we use UDP protocol to implement real-time network communication for full-duplex voice transmission. Use the Visual C ++ 2.0 compiling environment, and its dynamic Connection Library name is wsock32.dll.
Ii. Usage of Main Functions
By establishing dual sockets, you can easily implement full-duplex network communication.
1. Socket creation function:
Socket socket (INT family, int type, int Protocol)
For UDP, write as follows:
Sockret S;
S = socket (af_inet, sock_dgram, 0 );
Or S = socket (af_inet, sock_dgram, ipproto_udp)
To create two sockets, you must bind the address repeatedly. That is, when a socket has been bound to a local address, you must call BIND () before binding a second socket to a function, use setsockopt () to set the so_reuseaddr socket option for the socket. You can use the getsockopt () function to obtain the socket option setting status. Note that the port numbers of the two sockets cannot be the same.
In addition, the socket buffer settings are also involved. according to regulations, the setting range of each zone is: no less than 512 bytes, which is larger than 8 K Bytes. 4 K bytes are selected as needed.
2. Socket binding function
Int BIND (socket S, struct sockaddr_in * Name, int namelen)
S is the socket just created. Name points to the pointer of the struct describing the communication object. namelen is the length of the struct. The components in this struct include: IP address (corresponding to name. sin_addr.s_addr), Port Number (name. sin_port), address type (name. sin_family, generally assigned to af_inet, indicating Internet address ).
(1) Fill in the IP Address: In full duplex communication, convert the point-to-point representation address corresponding to the user name to a 32-bit long integer IP address, using the inet_addr () function.
(2) The port number is used to indicate different processes (applications) of the same computer. There are two ways to allocate the port number: 1) the process allows the system to automatically assign a port number to the socket, you only need to specify the port number as 0 before calling bind. The port number automatically allocated by the system is located in 1024 ~ Between 5000, and 1 ~ Any TCP or UDP port between 1023 is retained, and the system does not allow any process to use the retained port unless its valid user ID is zero (Super User ).
(2) A process can specify a specific port for the socket. This is useful for servers that need to allocate all ports to the socket. The value range is 1024 and 65536.
In this program, the port numbers of the two sockets are set to 2000 and 2001. The former corresponds to the sending socket, and the latter corresponds to the receiving socket.
The port number needs to be converted from a 16-bit unsigned number (u_short type) to a network byte order.
Htons () function.
Based on the above two functions, we can provide a piece of program for the establishment and binding of dual sockets;
// Set related global variables
Socket Sr, SS;
Hpstr sockbuffers, sockbufferr;
Handle hsenddata, hreceivedata;
Dwrod dwdatasize = 1024*4;
Struct sockaddr_in therel. there2;
# Define local_host_addr 200.200.200.201
# Define REMOTE_HOST-ADDR 200.200.200.202
# Define local_host_port 2000
# Define local_host_port 2001
// Socket-based function Creation
Bool make_skt (hwnd)
{
Struct sockaddr_in here, here1;
Ss = socket (af_inet, sock_dgram, 0 );
Sr = socket (af_inet, sock_dgram, 0 );
If (Ss = invalid_socket) | (Sr = invalid_socket ))
{
MessageBox (hwnd, "socket creation failed !", "", Mb_ OK );
Return (false );
}
Here. sin_family = af_inet;
Here. sin_addr.s_addr = inet_addr (local_host_addr );
Here. sin_port = htons (lical_host_port );
// Another socket
Herel. sin_family = af_inet;
Herel. sin_addr.s_addr (local_host_addr );
Herel. sin_port = htons (local_host_port1 );
Socketbuffer (); // lock settings of the socket buffer
Setsockopt (SS, sol_socket, so_sndbuf, (char far *) sockbuffers, dwdatasize );
If (BIND (SS, (lpsockaddr) & here, sizeof (here )))
{
MessageBox (hwnd, "sending socket binding failed !", "", Mb_ OK );
Return (false );
}
Setsockopt (SR SQL _socket, so_rcvbuf | so_reuseaddr, (char far *)
Sockbufferr, dwdatasize );
If (BIND (Sr, (lpsockaddr) & here1, sizeof (here1 )))
{
MessageBox (hwnd, "failed to receive socket binding !", "", Mb_ OK );
Return (false );
}
Return (true );
}
// Socket buffer settings
Void sockbuffer (void)
{
Hsenddata = globalalloc (gmem_moveable | gmem_share, dwdatasize );
If (! Hsenddata)
{
MessageBox (hwnd, "failed to locate sending Socket buffer !", Null,
Mb_ OK | mb_iconexclamation );
Return;
}
If (sockbuffers = globallock (hsenddata) = NULL)
{
MessageBox (hwnd, "sending Socket buffer lock failed !", Null,
Mb_ OK | mb_iconexclamation );
Globalfree (hrecorddata [0];
Return;
}
Hreceivedata = globalalloc (gmem_moveable | gmem_share, dwdatasize );
If (! Hreceivedata)
{
MessageBox (hwnd, "" receive Socket buffer locating failed !", Null
Mb_ OK | mb_iconexclamation );
Return;
}
If (sockbuffert = globallock (hreceivedata) = NULL)
MessageBox (hwnd, "sending Socket buffer lock failed !", Null,
Mb_ OK | mb_iconexclamation );
Globalfree (hrecorddata [0]);
Return;
}
3. data sending and receiving functions;
Int sendto (socket S. char * Buf, int Len, int flags, struct sockaddr_in to, int
Tolen );
Int recvfrom (socket S. char * Buf, int Len, int flags, struct sockaddr_in
Fron, int * fromlen) where the flags parameter is generally 0.
The recvfrom () function actually reads a data packet sent by the sendto () function. When the number of data bytes read is less than the specified number of received data bytes, all data is received, the number of actually received bytes is returned. When more data is read than the specified value, excess data is discarded in the data packet mode. In the stream mode, the remaining data is read from the lower recvfrom. To send and receive data, you must create a data sending buffer and a data receiving buffer. Rule: A datagram at the IP layer cannot exceed 64 KB (including data headers ). When the buffer is set too much or too large, the establishment of socket fails due to insufficient memory. This error disappears after the buffer is reduced. 4 K bytes are used in the experiment.
In addition, pay attention to the writing of the final parameters in the two functions. The final parameter for sendto () is an integer, while the recvfrom () is a pointer to an integer.
4. socket close function: closesocket (socket S)
When the communication ends, the specified socket should be closed to release the relevant resources.
When closing the socket, release the locked buffer. The program segment is:
Void closesocket (void)
{
Globalunlock (hsenddata );
Globalfree (hsenddata );
Globalunlock (hreceivedata );
Globalfree (hsf-edava );
If (wsaaysncselect (SS, hwnd, 0, 0) = socket_error)
{
Messagebos (hwnd, "failed to close the sending socket !", "", Mb_ OK );
Return;
}
If (wsaaysncselect (Sr, hwnd, 0, 0) = socket_error)
{
MessageBox (hwnd, "failed to close the receiving socket !", "", Mb_ OK );
Return;
}
Wsacleanup ();
Closesockent (SS );
Closesockent (SR );
Return;
}
}
Iii. Programming features and asynchronous selection mechanism of WinSock
1. Blocking and Processing Method
In network communication, due to network congestion or the amount of data sent at a time is too large, data exchange may often fail to be transmitted in a short period of time. Therefore, functions for sending and receiving data cannot be returned, this phenomenon is called blocking. Winsock provides two Processing Methods for functions that may be blocked: blocking and non-blocking. In blocking mode, the function for sending and receiving data can be returned until the transfer is completed or an error occurs after it is called. During the blocking period, the blocked function does not call the system function getmessage () to keep the message loop running normally. For non-blocking mode, the function is returned immediately after it is called. After the transfer is completed, Winsock sends an agreed message to the program.
During programming, try to use non-blocking methods. In the blocking mode, the user may try to close the program during a long wait, because the message loop is still working, so the program window may be closed, in this way, when the function is returned from the Winsock dynamic Connection Library, the main program has been deleted from the memory, which is obviously extremely dangerous.
2. Use of the asynchronous selection function wsaasyncselect ()
Winsock uses wsaasyncselect () to automatically set the socket to be in a non-blocking mode. The key to implementing Windows Network Programming with Windows Sockets is that it provides asynchronous message-based access to network events and is used to register network events that are of interest to applications. It requests Windows Sockets DLL to send a message to the window when detecting a network event on the socket. For UDP, these network events are mainly:
Fd_read expects to receive a notification when the socket receives data (that is, the read is ready;
Fd_write expects to receive notifications when the number of sockets can be sent (that is, when the write is ready;
Fd_close is expected to receive a power-on notification when the socket is closed.
The message variable wparam indicates the socket where a network event occurs. The low-byte value of the variable 1param describes the network event that occurs. The high text includes the error code. For example, add a branch to the message loop of the window function:
Int OK = sizeof (sockaddr );
Case wmsg;
Switch (1 PARAM)
{
Case fd_read:
// Read data on the socket
If (recvfrom (Sr. lpplaydata [J], dwdatasize, 0, (struct sockaddr far *) & there1,
(INT far *) & OK) = socket_error0
{
MessageBox) hwnd, "data reception failed !", "", Mb_ OK );
Return (false );
}
Case fd_write:
// Write data on the socket
}
Break;
In programming, the wsaasyncselect () function should be flexibly placed in the corresponding message loop as needed. For other instructions, see [1]. In addition, it should be pointed out that the message boxes in the above program fragments are mainly set for convenience of program debugging, and will not appear in formal products. At the same time, a special fault tolerance processing function should be established according to the program error tolerance design. Various errors that may occur in the program will be handled by this function. Based on the degree of hazard of the error, several different processing measures will be established. This ensures smooth and reliable communication between the two parties. Iv. Conclusion
This article is an important part of multimedia network transmission projects. At present, combined with hardware full duplex voice cards and other equipment, it has successfully achieved full duplex voice communication. The content of the entire multimedia transmission system design will be described in another article.