Bug 57918 - System.Net.Sockets.EndSend returns incorrect value
Summary: System.Net.Sockets.EndSend returns incorrect value
Status: RESOLVED FIXED
Alias: None
Product: Class Libraries
Classification: Mono
Component: System (show other bugs)
Version: master
Hardware: PC All
: --- major
Target Milestone: Future Release
Assignee: Katelyn Gadd
URL:
Depends on:
Blocks:
 
Reported: 2017-07-03 21:56 UTC by Ben Coburn
Modified: 2017-10-19 19:35 UTC (History)
5 users (show)

See Also:
Tags: bugpool-archive
Is this bug a regression?: ---
Last known good build:


Attachments
test program (1.38 KB, text/plain)
2017-07-03 21:56 UTC, Ben Coburn
Details

Description Ben Coburn 2017-07-03 21:56:40 UTC
Created attachment 23270 [details]
test program

Overview: 

System.Net.Sockets.BeginSend will always send the complete buffer supplied to it, but sometimes the corresponding EndSend call in the callback supplied to BeginSend will return a value that is less than the number of bytes actually sent.

Steps to Reproduce: 

Run the attached t.cs with this command line on a Linux system to send data:
mcs t.cs && mono t.exe 192.168.1.32 9999

and on a different computer, run netcat like this to recieve:
nc -l -p 9999|wc -c (linux)
nc -l 9999 |wc -c (mac)

It's important to use two computers, the socket behavior is enough different on localhost to break the test.

Actual Results: 
The sending machine will print a number of bytes sent that is lower than the number of bytes received on the receiving side.

Expected Results: 
The send and receive sides should display the same number of bytes.

Build Date & Hardware:
ben@ben-desktop:~$ mono --version
Mono JIT compiler version 4.8.1 (Stable 4.8.1.0/22a39d7 Wed Apr 12 12:00:40 UTC 2017)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
        TLS:           __thread
        SIGSEGV:       altstack
        Notifications: epoll
        Architecture:  amd64
        Disabled:      none
        Misc:          softdebug
        LLVM:          supported, not enabled.
        GC:            sgen

On a Linux x86_64 system.

Additional Builds and Platforms: 
I believe the bug is present in the Mono master code on GitHub as of commit 6295559e0452bffa087d321658d896e28f88dc51

Additional Information:
It's possible I am missing something, but I believe I've found the issue in the publicly available Mono source code on github.

This is a snippet of mono/mcs/class/System/System.Net.Sockets/Socket.cs taken from here: https://github.com/mono/mono/blob/master/mcs/class/System/System.Net.Sockets/Socket.cs

static void BeginSendCallback (SocketAsyncResult sockares, int sent_so_far)
{
	int total = 0;

	try {
		total = Socket.Send_internal (sockares.socket.m_Handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error, false);
	} catch (Exception e) {
		sockares.Complete (e);
		return;
	}

	if (sockares.error == 0) {
		sent_so_far += total;
		sockares.Offset += total;
		sockares.Size -= total;

		if (sockares.socket.CleanedUp) {
			sockares.Complete (total);
			return;
		}

		if (sockares.Size > 0) {
			IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, sent_so_far), sockares));
			return; // Have to finish writing everything. See bug #74475.
		}

		sockares.Total = sent_so_far;
	}

	sockares.Complete (total);
}

I couldn't find a bug #74475, but observe that if the first call to
total = Socket.Send_internal (sockares.socket.m_Handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error, false);
doesn't consume the whole buffer being sent, the "total" value will be less than the size of the buffer.  The "if (sockares.Size > 0)" block makes sure that the whole buffer will be sent, but the last line, "sockares.Complete (total);" means that the EndSend call in application code gets the original, incorrect, value for the number of bytes sent.

It's also possible that this is a duplicate of https://bugzilla.xamarin.com/show_bug.cgi?id=41506 it wasn't totally clear to me.
Comment 1 Marek Safar 2017-09-11 22:18:51 UTC
With setup like

nc -l -p 9999|wc -c  // MACHINE #1

then I run on second machine

mcs t.cs && mono t.exe <machine#1-IP> 9999

I get quite different results

e.g.

Wrote 56360 bytes (instead of 1048576)
Comment 2 Ludovic Henry 2017-09-13 17:17:06 UTC
Fixed with https://github.com/mono/mono/pull/5545

Note You need to log in before you can comment on or make changes to this bug.