Is it necessary to have two TCP Socket per client

Emon Haque 3,176 Reputation points
2020-11-10T13:27:46.463+00:00

As I was learning WPF and MVVM, I developed a small Trading Server and 2 type of Clients in WPF. In that application I'd 2 Scoket per client on both Client and Server side, one for Receive and one for Send byte[]. In some places I read that TCP Socket is bidirectional and there's no need to have 2 sockets per client and in one/two places, I've read that there'll be a deadlock if the socket Sends and Receives simultaneously. So to be on safe side, during my learning process, I used 2 sockets. I developed that with SocketAsyncEventArgs and its Async functions. Now, I'm planning to develop another one with Task, CancellationTokenSource and plain Socket.

So before starting I want to know, do I really have to have 2 sockets per client or one is capable to handle bidirectional operation simultaneously?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,760 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,321 Reputation points
    2020-11-11T08:34:11.09+00:00

    Hi Emon,
    no, it's not necessary to have two TCP Socket per client. You can use the same socket in both directions like in following demo:

    Server:

    using System;  
    using System.Net;  
    using System.Net.Sockets;  
    using System.Text;  
    using System.Threading;  
    using System.Threading.Tasks;  
      
    namespace Server  
    {  
      internal class DemoServer  
      {  
        public DemoServer()  
        {  
          host = Dns.GetHostEntry(Dns.GetHostName());  
          addr = host.AddressList[0];  
          ep = new IPEndPoint(addr, 8888);  
          listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);  
        }  
      
        private IPHostEntry host;  
        private IPAddress addr;  
        private EndPoint ep;  
        private Socket listener;  
        private Socket client;  
      
        internal void Start()  
        {  
          listener.Bind(ep);  
          listener.Listen(5);  
          Console.WriteLine("Server listen");  
          client = listener.Accept();  
          Console.WriteLine("Socket connection established!");  
          Task.Run(() =>  
          {  
            try  
            {  
              while (true)  
              {  
                byte[] bufIn = new Byte[1024];  
                int numByte = client.Receive(bufIn);  
                Console.WriteLine($"Text received -> {Encoding.ASCII.GetString(bufIn, 0, numByte)} ");  
              }  
            }  
            catch (Exception ex)  
            {  
              Console.WriteLine(ex.Message);  
            }  
          });  
          Task.Run(() =>  
          {  
            int n = 1;  
            try  
            {  
              while (true)  
              {  
      
                Thread.Sleep(2000);  
                string msg = $"{n++}. Message from Server: {DateTime.Now:mm.ss.fff} ";  
                Console.WriteLine("Text sent -> {0} ", msg);  
                byte[] bufOut = Encoding.ASCII.GetBytes(msg);  
                client.Send(bufOut);  
              }  
            }  
            catch (Exception ex)  
            {  
              Console.WriteLine(ex.Message);  
            }  
          });  
        }  
      }  
    }  
    

    ViewModel Client:

    using System;  
    using System.Collections.ObjectModel;  
    using System.ComponentModel;  
    using System.Net;  
    using System.Net.Sockets;  
    using System.Runtime.CompilerServices;  
    using System.Text;  
    using System.Threading;  
    using System.Threading.Tasks;  
    using System.Windows.Input;  
      
    namespace WpfApp1  
    {  
      class ViewModel : INotifyPropertyChanged  
      {  
      
        public ViewModel()  
        {  
          sc = SynchronizationContext.Current;  
          host = Dns.GetHostEntry(Dns.GetHostName());  
          addr = host.AddressList[0];  
          ep = new IPEndPoint(addr, 8888);  
          sender = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);  
        }  
      
        SynchronizationContext sc;  
        private IPHostEntry host;  
        private IPAddress addr;  
        private EndPoint ep;  
        private Socket sender;  
      
        public string Input { get; set; }  
        public ObservableCollection<string> Output { get; set; } = new ObservableCollection<string>();  
      
        public ICommand Cmd { get => new RelayCommand(CmdExec, CanCmdExec); }  
      
        public void CmdExec(object parameter)  
        {  
          switch (parameter.ToString())  
          {  
            case "Connect":  
              Task.Run(() =>  
              {  
                try  
                {  
                sender.Connect(ep);  
                OnPropertyChanged(nameof(Cmd));  
                while (true)  
                {  
                  byte[] bufIn = new Byte[1024];  
                  int numByte = sender.Receive(bufIn);  
                  string data = Encoding.ASCII.GetString(bufIn, 0, numByte);  
                  sc.Post(new SendOrPostCallback((state) => { Output.Insert(0, state.ToString()); }), data);  
                }  
                }  
                catch (Exception ex)  
                {  
                  sc.Post(new SendOrPostCallback((state) => { Output.Insert(0, state.ToString()); }), ex.Message);  
                }  
              });  
              break;  
            case "Send":  
              int byteSent = sender.Send(Encoding.ASCII.GetBytes(Input));  
              break;  
            default:  
              break;  
          }  
        }  
        public event EventHandler CanExecuteChanged;  
      
        public bool CanCmdExec(object parameter)  
        {  
          switch (parameter.ToString())  
          {  
            case "Send":  
              return sender.Connected;  
            default: return true;  
          }  
        }  
        public event PropertyChangedEventHandler PropertyChanged;  
        private void OnPropertyChanged([CallerMemberName] string propName = "") =>  
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));  
      }  
    }  
    

    38960-x.gif

    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.