/// ---------------------------------------------------------------------------------------- /// TCP Demo (Client side) /// (c) by Vector Informatics GmbH /// /// /// Hints: /// - If reference to "System" is missing you have to add it manually. /// /// - To use SystemVariables, you have to be sure the generated "...sysvars.dll" is within your references. /// If not, add it by browsing to the file. /// ---------------------------------------------------------------------------------------- /// using System; using Vector.Tools; using Vector.CANoe.Runtime; using Vector.CANoe.Sockets; using System.Runtime.Remoting.Messaging; using System.Collections.Generic; using System.Diagnostics; /// /// ---------------------------------------------------------------------------------------- /// TcpClientNode Measurement Script class. /// This represent the CANoe-Simulation node, that is a basic MeasurementScript /// that will initialize a TcpServer, doing the networking stuff. /// ---------------------------------------------------------------------------------------- /// public class TcpClientNode : MeasurementScript { /// /// initialize measurement /// public override void Initialize() { this.Client = new SimpleTcpClient(); NSys.TcpClient.ServerResponse.Value = ""; } /// /// start measurement /// public override void Start() { } /// /// stop measurement /// public override void Stop() { if (this.Client.Connected) { this.Client.Disconnect(); } } /// /// shutdown measurement /// public override void Shutdown() { } /// /// server instance. /// private SimpleTcpClient Client { get; set; } /// /// react changing the action variable and do the requested action. /// [OnChange(typeof(NSys.TcpClient.Action))] public void OnActionChanged() { this.Client.DoAction(NSys.TcpClient.Action.Value); } } /// /// ---------------------------------------------------------------------------------------- /// The main TCP server class. /// /// Demonstrates how to work with the TCP networking classes. /// ---------------------------------------------------------------------------------------- /// internal class SimpleTcpClient { /// /// constant integer for the TCP-Buffer's size. /// private const int TCP_DATA_SIZE = 8192; /// /// constructor /// public SimpleTcpClient() { } /// /// connect to server. /// public void Connect() { this.Disconnect(); Output.WriteLine("Client: Connecting to server..."); // to keep things simple, connect to the server configured within the system variables.. var serverIP = System.Net.IPAddress.Parse(NSys.TcpServer.IP.Value); var serverPort = NSys.TcpServer.Port.Value; this.CurrentState = eClientState.eConnecting; // create a tcp client socket... this.ClientSocket = new TcpClient(); // start connecting to the server... this.ClientSocket.BeginConnect(serverIP, serverPort, this.OnConnectionRequestDoneCallback, null); } /// /// connection to server established... /// /// private void OnConnectionRequestDoneCallback( IAsyncResult result ) { if (result != null) { if (result.IsCompleted) { this.ClientSocket.EndConnect(result); Output.WriteLine("Client: Connection established. Await welcome..."); // start receiving and check for welcome... this.ReceiveResponse(); } else { Output.WriteLine("Client: Connection to server could not be established. (Server started?)"); } } } /// /// send data to the server and await the server response. /// /// /// private void SendRequest( string data ) { var noConnectionErrorMsg = "Client: Can not send due there is no connection to the server established."; if (this.ClientSocket == null) { Output.WriteLine(noConnectionErrorMsg); return; } if (!this.ClientSocket.Connected) { Output.WriteLine(noConnectionErrorMsg); return; } Output.WriteLine(String.Format("Client: Sending '{0}' to server...", data)); try { this.ClientSocket.GetStream().BeginWrite(System.Text.Encoding.UTF8.GetBytes(data), 0, data.Length, this.OnWriteDoneCallback, null); } catch (Exception /*e*/) { Output.WriteLine("Client: Exception on writing to network stream."); this.Disconnect(); } /// ... and await /receive the response this.ReceiveResponse(); } /// /// writing data to socket done. /// /// private void OnWriteDoneCallback( IAsyncResult result ) { if (result != null) { this.ClientSocket.GetStream().EndWrite(result); if (!result.IsCompleted) { // incomplete... disconnect. Output.WriteLine("Client: Writing to network stream not completed."); this.Disconnect(); } } } /// /// receive response from server... /// private void ReceiveResponse() { if (this.ClientSocket.Connected) { var buffer = new byte[TCP_DATA_SIZE]; try { this.ClientSocket.GetStream().BeginRead(buffer, 0, TCP_DATA_SIZE, this.OnReadDoneCallback, buffer); } catch (Exception /*e*/) { Output.WriteLine("Client: Exception on reading from network stream."); this.Disconnect(); } } else { Output.WriteLine("Client Error: Can't receive from socket due socket is not connected."); } } /// /// on reading from client done. /// /// private void OnReadDoneCallback( IAsyncResult result ) { if (result != null) { if (result.IsCompleted) { this.ClientSocket.GetStream().EndRead(result); var buffer = (byte[])result.AsyncState; var dataString = System.Text.Encoding.UTF8.GetString(buffer).Split('\0')[0]; Output.WriteLine(String.Format("Client: Received data from server: '{0}'", dataString)); this.ReactOnResponse(dataString); } else { // incomplete... disconnect. Output.WriteLine("Client: Reading from network stream not completed."); this.Disconnect(); } } } /// /// act on received data, depending on the current client state. /// /// private void ReactOnResponse( string dataString ) { // just write the received data to the panel... NSys.TcpClient.ServerResponse.Value = dataString; // depending on the current state different reactions are done... switch (this.CurrentState) { case eClientState.eDisconnected: { // in disconnected state do nothing special. break; } case eClientState.eConnecting: { // after connecting to server, the server should send a welcome to us... if (dataString=="WELCOME") { Output.WriteLine("Client: Got welcome from server."); this.CurrentState = eClientState.eConnected; break; } else { Output.WriteLine(String.Format("Client: Expected WELCOME from server, but got '{0}'", dataString)); this.CurrentState = eClientState.eDisconnected; } break; } case eClientState.eConnected: { // during connection the server sends us either an OK followed by the u-case string we sent, // OR a plain "FAILED". if (dataString.CompareTo("NOT CONVERTED") == 0) { Output.WriteLine("Client: Server did not convert already uppercase string."); } else if (dataString.CompareTo("OK") == 0) { Output.WriteLine("Client: Server converted the string..."); this.CurrentState = eClientState.eAwaitResultFromServer; // continue receiving, due the server now sends the converted string... this.ReceiveResponse(); } break; } case eClientState.eAwaitResultFromServer: { // after awaited converting result from server, switch back to the connected state. this.CurrentState = eClientState.eConnected; break; } default: { Output.WriteLine("Client: Unsupported state " + this.CurrentState.ToString()); break; // do nothing due this state is yet unknown. } } } /// /// disconnect from server /// public void Disconnect() { if (this.CurrentState == eClientState.eConnected) { if (this.ClientSocket != null) { if (this.ClientSocket.Connected) { Output.WriteLine("Client: Disconnecting."); this.ClientSocket.Close(); } this.ClientSocket = null; } } else { Output.WriteLine("Client: Already disconnected."); } this.CurrentState = eClientState.eDisconnected; } /// /// a tcp socket for the client /// private TcpClient ClientSocket = null; /// /// current client state property to know what is expected from the server. /// private eClientState _currentState = eClientState.eDisconnected; public eClientState CurrentState { get { return this._currentState; } private set { this._currentState = value; NSys.TcpClient.Connected.Value = (value == eClientState.eDisconnected) ? 0 : 1; } } /// /// client states to know what expected from server... /// internal enum eClientState { eDisconnected = 0, eConnecting = 1, eConnected = 2, eAwaitResultFromServer = 3 }; public void DoAction( int actionNr ) { // execute action with given number. switch (actionNr) { case 0: { // do nothing. break; } case 1: { // connect to server allowing to reconnect when triggering action multiple times. this.Connect(); break; } case 2: { // disconnect from server. this.Disconnect(); break; } case 3: { // send data string to server and await the response... this.SendRequest(NSys.TcpClient.DataString.Value); break; } default: { Output.WriteLine(String.Format("Client: Unimplemented action #{0}", actionNr)); break; } } // reset the action back to 0 to be able to react on a variable change again. NSys.TcpClient.Action.Value = 0; } /// /// get information if client is connected. /// public bool Connected { get { return this.CurrentState == eClientState.eConnected; } } }