The key is that the write pointer cannot catch up with the read pointer, so that the read pointer and the write pointer keep a byte distance to distinguish the two pointers from each other.
How to Ensure thread security?
In fact, when a thread reads or writes data, it obtains the pointer at a certain time point to determine that the condition is met. The condition can only be better and better, but cannot be worse and worse. For example, when the data can be written, there must be more and more space.
// The actual buffer size must be one more byte
Struct ringbuffer
{
// Buffer header pointer
Char * m_pbuffer;
// Buffer size
Int m_ibuffersize;
// Read pointer
Char * m_pread;
// Write pointer
Char * m_pwrite;
// Buffer pointer Header
Char * m_phead;
// The End Of The buffer pointer
Char * m_ptail;
};
// The actual buffer size must be one more byte
Void ip_reset_ringbuffer (ringbuffer * pringbuffer)
{
Assert (null! = Pringbuffer );
Pringbuffer-> m_pwrite = pringbuffer-> m_pread;
}
Ringbuffer * ip_create_ringbuffer (INT buffersize)
{
Ringbuffer * pringbuffer = (ringbuffer *) malloc (sizeof (ringbuffer ));
If (null = pringbuffer)
{
# Ifdef _ log
Printf ("% s: % d get memory is null", _ file __, _ line __);
# Else
Writelog (errlog, "% s: % d get memory is null", _ file __, _ line __);
# Endif
Return NULL;
}
Else
{
Pringbuffer-> m_ibuffersize = buffersize;
Pringbuffer-> m_pbuffer = (char *) malloc (sizeof (char) * buffersize );
If (null = pringbuffer-> m_pbuffer)
{
# Ifdef _ log
Printf ("% s: % d get memory is null", _ file __, _ line __);
# Else
Writelog (errlog, "% s: % d get memory is null", _ file __, _ line __);
# Endif
Free (pringbuffer );
Return NULL;
}
Memset (pringbuffer-> m_pbuffer, 0, buffersize );
Pringbuffer-> m_phead = pringbuffer-> m_pbuffer;
Pringbuffer-> m_ptail = pringbuffer-> m_pbuffer + buffersize;
Pringbuffer-> m_pread = pringbuffer-> m_pwrite = pringbuffer-> m_phead;
Return pringbuffer;
}
}
// At a certain time point, the write thread first checks whether the buffer zone has sufficient space for write operations.
// When the write pointer catches up with the read pointer, it must keep one byte spacing with the read pointer,
// Cannot catch the read pointer
Bool ip_checkcanwrite_ringbuffer (ringbuffer * pringbuffer, int checksize)
{
Assert (null! = Pringbuffer );
// Save the read pointer. It is possible that the read thread is reading data.
Char * psaveread = pringbuffer-> m_pread;
// Remaining space size
Int left = 0;
// Write pointer before reading pointer, or read pointer catch up with writing pointer
If (pringbuffer-> m_pwrite> = psaveread)
{
// Determine whether the remaining part is 1500 bytes
Left = pringbuffer-> m_ptail-pringbuffer-> m_pwrite;
If (left> = checksize)
{
Return true;
}
// You need to add the distance from the buffer header to the read pointer. In this case, you need to calculate the total length.
// Calculate one byte to prevent the write pointer from overlapping when catching up with the read pointer.
// Because the read pointer is behind the write pointer, it will not be used to write the pointer. Here, the read pointer can be obtained in real time.
// At this time, the thread may have read data again.
Else
{
Left = left + pringbuffer-> m_pread-pringbuffer-> m_phead;
If (left> = checksize + 1)
{
Return true;
}
Else
{
Return false;
}
}
}
// The write pointer runs fast.
// The Real-time read pointer cannot be obtained here, because the read thread may cause the read pointer to run to the end of the write pointer.
Else
{
Left = psaveread-pringbuffer-> m_pwrite;
If (left> = checksize + 1)
{
Return true;
}
Else
{
Return false;
}
}
}
// This function can be written only after the remaining space check is performed.
// The check function must have enough 1500 bytes.
Void ip_pushdata_ringbuffer (ringbuffer * pringbuffer, char * pdata, int length, int maxfree)
{
Assert (null! = Pringbuffer );
Assert (null! = Pdata );
Assert (length <= maxfree );
// Save the read pointer. It is possible that the read thread is reading data.
Char * psaveread = pringbuffer-> m_pread;
// Write pointer greater than read pointer
If (pringbuffer-> m_pwrite> = psaveread)
{
Int part = pringbuffer-> m_ptail-pringbuffer-> m_pwrite;
// The space at the end of the array is sufficient.
If (Part> = length)
{
// Copy the data to the end
Memcpy (pringbuffer-> m_pwrite, pdata, length );
If (Part> length)
{
// Modify the write pointer
Pringbuffer-> m_pwrite = pringbuffer-> m_pwrite + length;
}
Else
{
// Write pointer pointing to array Header
Pringbuffer-> m_pwrite = pringbuffer-> m_phead;
}
}
// The space at the end is not enough, and the object must be copied to the array header.
Else
{
// First copy to the end of the array
Memcpy (pringbuffer-> m_pwrite, pdata, part );
// Move the source data pointer
Pdata = pdata + part;
Length = length-part;
// Copy the object to the array Header
Memcpy (pringbuffer-> m_phead, pdata, length );
// Modify the write pointer
Pringbuffer-> m_pwrite = pringbuffer-> m_phead + length;
}
}
// Write pointer less than read pointer
Else
{
Int left = psaveread-pringbuffer-> m_pwrite;
Assert (left> = maxfree + 1 );
Memcpy (pringbuffer-> m_pwrite, pdata, length );
Pringbuffer-> m_pwrite = pringbuffer-> m_pwrite + length;
}
}
// Try to find a complete IP package in the buffer and then send
Static void sendippacket (tunwriter * ptunwriter, ringbuffer * pringbuffer, int tundevicefd)
{
Assert (null! = Ptunwriter );
Assert (null! = Pringbuffer );
Again:
Char psendbuffer [send_buffer];
Char * pbegin = psendbuffer;
Memset (psendbuffer, 0, send_buffer );
Int ipacketlength;
Int itotallength;
// Save the write pointer first
Char * pwritesave = pringbuffer-> m_pwrite;
// Write pointer before reading pointer
If (pwritesave> = pringbuffer-> m_pread)
{
// Judge the length of the IP package from the current read pointer
Int left = pwritesave-pringbuffer-> m_pread;
// The header of less than one IP package
If (left <20)
{
Return;
}
// The header of an IP packet
Else if (left> = 20)
{
// Obtain the length of the IP package
Ipacketlength = ntohs (* (unsigned short *) (pringbuffer-> m_pread + 2 )));
// A complete IP package
If (left> = ipacketlength)
{
// Copy the complete IP package first
Memcpy (pbegin, pringbuffer-> m_pread, ipacketlength );
// Read pointer ++
Pringbuffer-> m_pread = pringbuffer-> m_pread + ipacketlength;
// Enter the sending stage
Goto send;
}
// A complete IP package is missing
Else
{
Return;
}
}
}
// When the read pointer is in front of the write pointer, the write pointer will certainly not catch up with the read pointer. Here we can get the real-time write pointer
Else
{
// Determine the entire remaining length
Int left = pringbuffer-> m_ptail-pringbuffer-> m_pread + pringbuffer-> m_pwrite-pringbuffer-> m_phead;
// The header of less than one IP package
If (left <20)
{
Return;
}
// The header of an IP packet
Else if (left> = 20)
{
// First copy the first data
// All at the end of the buffer
Int part = pringbuffer-> m_ptail-pringbuffer-> m_pread;
If (Part> = 20)
{
Memcpy (pbegin, pringbuffer-> m_pread, 20 );
}
// Some are at the end and some are at the header
Else
{
Memcpy (pbegin, pringbuffer-> m_pread, part );
Pbegin = pbegin + part;
Part = 20-part;
Memcpy (pbegin, pringbuffer-> m_phead, part );
}
// Obtain the length of the IP package
Pbegin = psendbuffer;
// Remaining packet length at the end
Part = pringbuffer-> m_ptail-pringbuffer-> m_pread;
Ipacketlength = ntohs (* (unsigned short *) (pbegin + 2 )));
Itotallength = ipacketlength;
// A complete IP package
If (left> = ipacketlength)
{
// Determine whether the buffer end is sufficient
If (Part> = ipacketlength)
{
Pbegin = psendbuffer;
// Copy from scratch
Memcpy (pbegin, pringbuffer-> m_pread, ipacketlength );
// Modify the read pointer
Pringbuffer-> m_pread = pringbuffer-> m_pread + ipacketlength;
}
// Not enough. copy from the buffer Header
Else
{
Memcpy (pbegin, pringbuffer-> m_pread, part );
Itotallength = itotallength-part;
Pbegin = pbegin + part;
Memcpy (pbegin, pringbuffer-> m_phead, itotallength );
// Modify the read pointer
Pringbuffer-> m_pread = pringbuffer-> m_phead + itotallength;
}
// Enter the sending stage
Goto send;
}
// A complete IP package is missing
Else
{
Return;
}
}
}
Send:
Int isendtun = write (tundevicefd, psendbuffer, ipacketlength );
If (-1 = isendtun) & (errno = eintr ))
{
Goto send;
}
// In the following two cases, the Tun device cannot write data and does not need to read data.
Else if (-1 = isendtun) & (errno = ewouldblock ))
{
Return;
}
Else if (-1 = isendtun) & (errno! = Eintr) & (errno! = Ewouldblock ))
{
# Ifdef _ log
Ip_writelog_threadlog (ptunwriter-> m_plog, log_emerg, "sendippacket ()", "Write Tun fail with errno % d", errno );
# Else
Writelog (errlog, "tunwriter: % d write Tun fail with errno % d", ptunwriter-> m_ithreadid, errno );
# Endif
Return;
}
// Sent successfully
Else
{
// Write a complete package to the Tun Device
Assert (isendtun = ipacketlength );
Goto again;
}
}