Komunikacja TCP pomiędzy Delphi (klient) a C# (serwer)

0

Witam,

Mam dwie aplikacje (przykłady z internetu) i dochodzi pomiędzy nimi do komunikacji, ale nie gdy przesyłam z Delphi komunikat to nie wyświetla go w C#.

Kody poniżej:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Sockets;

type
  TForm1 = class(TForm)
    TcpClient1: TTcpClient;
    Edit1: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if TcpClient1.Connect then
  begin
    TcpClient1.send(edit1.text);
    TcpClient1.Disconnect;
  end;
end;

end.
1

Ja się nie znam ale gdzie tu serwer?

0

Sorki, mój błąd.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace TCPServer
{
    public partial class Form1 : Form
    {
        int i;
        TcpListener server = new TcpListener(IPAddress.Any, 1980); // Creates a TCP Listener To Listen to Any IPAddress trying to connect to the program with port 1980
        NetworkStream stream; //Creats a NetworkStream (used for sending and receiving data)
        TcpClient client; // Creates a TCP Client
        byte[] datalength = new byte[4]; // creates a new byte with length 4 ( used for receivng data's lenght)

        public Form1()
        {
            InitializeComponent();
        }


        public void ServerReceive()
        {
            stream = client.GetStream(); //Gets The Stream of The Connection
            new Thread(() => // Thread (like Timer)
            {
                while ((i = stream.Read(datalength, 0, 4)) != 0)//Keeps Trying to Receive the Size of the Message or Data
                {
                    // how to make a byte E.X byte[] examlpe = new byte[the size of the byte here] , i used BitConverter.ToInt32(datalength,0) cuz i received the length of the data in byte called datalength :D
                    byte[] data = new byte[BitConverter.ToInt32(datalength, 0)]; // Creates a Byte for the data to be Received On
                    stream.Read(data, 0, data.Length); //Receives The Real Data not the Size
                    this.Invoke((MethodInvoker)delegate // To Write the Received data
                    {
                        txtLog.Text += System.Environment.NewLine + "Client : " + Encoding.Default.GetString(data); // Encoding.Default.GetString(data); Converts Bytes Received to String
                    });
                }
            }).Start(); // Start the Thread

        }



        public void ServerSend(string msg)
        {
            stream = client.GetStream(); //Gets The Stream of The Connection
            byte[] data; // creates a new byte without mentioning the size of it cuz its a byte used for sending
            data = Encoding.Default.GetBytes(msg); // put the msg in the byte ( it automaticly uses the size of the msg )
            int length = data.Length; // Gets the length of the byte data
            byte[] datalength = new byte[4]; // Creates a new byte with length of 4
            datalength = BitConverter.GetBytes(length); //put the length in a byte to send it
            stream.Write(datalength, 0, 4); // sends the data's length
            stream.Write(data, 0, data.Length); //Sends the real data
        }

        private void btnListen_Click(object sender, EventArgs e)
        {
            server.Start(); // Starts Listening to Any IPAddress trying to connect to the program with port 1980
            //MessageBox.Show("Waiting For Connection");
            txtLog.Text = "Oczekiwanie na klienta...\r\n";
            new Thread(() => // Creates a New Thread (like a timer)
            {
                client = server.AcceptTcpClient(); //Waits for the Client To Connect
                //MessageBox.Show("Connected To Client");
                txtLog.Text = "Połączono z klientem...\r\n";
                if (client.Connected) // If you are connected
                {
                    ServerReceive(); //Start Receiving
                }
            }).Start();
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            if (client.Connected) // if the client is connected
            {
                ServerSend(txtSend.Text); // uses the Function ClientSend and the msg as txtSend.Text
            }
        }
    }
}
//Credits 100% to Tarek Adel (Server)
0

Tylko napomnę, że klient chciałbym, aby działał przez konsolę lub sterownik dll. Jest główna aplikacja na której pracujemy i gdy wykona się np. metoda lub funkcja z głównej aplikacji np. SUMA to wówczas wynik poprzez TCP/IP leci do serwera i tam zostaje wyświetlony.

0

Nie za bardzo mam doświadczenie w C# ale czy ten serwer działa poprawnie z klientem napisanym w C# z kodu który był zamieszczony przed chwilą a z napisanym w Delphi nie działa?

0

Napisałem w C# klienta i serwer i wszystko działa super. W delphi zrobiłem to samo i też klient łączy się z serwerem i komunikaty przechodzą pomiędzy nimi. Jednak gdy łączę klienta w Delphi z serwerem w C# dochodzi do połączenia, ale już przesyłany tekst z Delphi do C# nie wyświetla się w textBoxie w C#.

0

Zamieść w załączniku binarkę serwera to popatrzę, bo nie mam zainstalowanego kompilatora C#.

0

W paczce są kody Delphi i C# wraz z plikami .exe

0

Nie znam C# ale:
C# serwer

TcpListener server = new TcpListener(IPAddress.Any, 1980); // Creates a TCP Listener To Listen to Any IPAddress trying to connect to the program with port 1980

C# klient

client = new TcpClient("127.0.0.1", 1980); //Trys to Connect

Delphi klient

  object TcpClient1: TTcpClient
    RemotePort = '5555'
0

Tak, masz rację. Drobny błąd (ponieważ przesłałem pliki przed zmianą portu na 1980), ale niestety on nie rozwiązuje tego problemu. Jak zmieniłem port to aplikacja C# zmienia komunikat na "Połączono z klientem..." i się zawiesza lub nie reaguje.

0

Wydaje mi się, że przesyłasz nie tak dane. Serwer oczekuje strumienia

stream = client.GetStream(); //Gets The Stream of The Connection

a przesyłasz zwykły string

TcpClient1.Sendln(edit1.text);
0

Przerobiłem kod metody void ServerReceive() w serwerze na:

public void ServerReceive()
        {
            stream = client.GetStream();

            if (stream.CanRead)
            {
                // Reads NetworkStream into a byte buffer.
                byte[] bytes = new byte[client.ReceiveBufferSize];

                // Read can return anything from 0 to numBytesToRead. 
                // This method blocks until at least one byte is read.
                stream.Read(bytes, 0, (int)client.ReceiveBufferSize);

                // Returns the data received from the host to the console.
                string returndata = Encoding.UTF8.GetString(bytes);

                txtLog.Text += "Klient: " + returndata + "\r\n";

            }
            
        }

i jest dobrze, bo odbiera komunikat od klienta, ale tylko raz... ;/

0

Odbiera raz bo tak zrobiłeś w Delphi :) Zmień kod tak jak masz klienta w C#. Oddzielnie button do połączenia a oddzielnie do wysyłania. Obecnie pod jednym buttonem masz połączenia (które nota bene jest inicjowane na wstępie bo TCPClient jest active), wysłanie komunikatu i rozłączenie. Podczas gry serwer jak się połączysz trzyma stare połączenie bo go nie resetujesz.

0

Zrobiłem tak:

procedure TForm1.btn_ConnectClick(Sender: TObject);
begin
  TcpClient1.Connect;
end;

procedure TForm1.btn_DisconnectClick(Sender: TObject);
begin
  TcpClient1.Disconnect;
end;

procedure TForm1.btn_SendClick(Sender: TObject);
begin
  TcpClient1.Sendln(edit1.text);
end;

w C#:

public void ServerReceive()
        {
            stream = client.GetStream();
            new Thread(() => // Thread (like Timer)
            {
                
                    // Reads NetworkStream into a byte buffer.
                    byte[] bytes = new byte[client.ReceiveBufferSize];

                    // Read can return anything from 0 to numBytesToRead. 
                    // This method blocks until at least one byte is read.
                    stream.Read(bytes, 0, (int)client.ReceiveBufferSize);

                    // Returns the data received from the host to the console.
                    string returndata = Encoding.UTF8.GetString(bytes);

                    txtLog.Text += "Klient: " + returndata + "\r\n";

                
            }).Start();
        }

i jest tak samo czyli raz komunikat ;/ Co robię źle?

0

Spróbuj na końcu każdego wysłanego komunikatu przesłać pusty wiersz.

0

Nie można. Kiedy przesyła jeden komunikat to się zawiesza.
Próbowałem tego i się nie udało:

procedure TForm1.btn_SendClick(Sender: TObject);
begin
  TcpClient1.Active := true;
  TcpClient1.Sendln(edit1.text);
  TcpClient1.Active := false;
end;
0

Nie napisałeś wcześniej że się zawiesza tylko że nie można wysłać drugi raz. Zawiesza się klient czy serwer? W którym momencie jest zawieszenie?

0

Nie napisałem, że się zawiesza aplikacja tylko zawiesza się proces. Wysyła komunikat i koniec - ani disconnect ani connect ani send ani nic już nie działa ani w kliencie ani w serwerze. Zero reakcji i to mnie dziwi :)

0

No a dodałeś ta pustą linię na końcu??

TcpClient1.Sendln(edit1.text + #13#10);
0

Dodałem i nic się nie zmieniło. Przesyła i dodaje komunikat tylko raz.

0

A mógłbyś doprecyzować co znaczy że się zawiesza proces? Przyciski są klikalne ms serwerze i kliencie? Formatki można przesuwać?

0

Masz rację, źle to ująłem. Uruchamiam aplikacje, a następnie klikam przycisk "Listen" na serwerze, a aplikacja oczekuje na klienta. Klient podczas startu automatycznie łączy się z serwerem i gdy klikam Send to wysyła komunikat. Gdy klikam kolejne razy nic się nie dzieje, nic nie przesyła, przyciski można klikać ile się chce, bo można je klikać, ale nic one nie robią. Jak robię Disconnect i później Send jeszcze raz to serwer nie reaguje, ale jak kliknę na serwerze jeszcze raz Listen to łączy się na nowo i wyświetla wcześniej przesłany komunikat, który powstał z klikania wszystkich przycisków klienta.

Reasumując: aplikacje i ich elementy się nie zawieszają. Po prostu komunikat przesyła się raz, a później nie wiadomo co się robi.

0

A co się stanie jak z serwera wywalisz wątek (Thread)?

0

wyświetla wcześniej przesłany komunikat, który powstał z klikania wszystkich przycisków klienta.

Jak wyślesz na kliencie 3 różne komunikaty to po ponownym połączeniu serwera te 3 komunikaty się pojawią czy tylko pierwszy?

ja bym Ci radził przerobić to na strumienie tak jak miałeś w C#:

            stream.Write(datalength, 0, 4); // sends the data's length
            stream.Write(data, 0, data.Length); //Sends the real data

Może jak wysyłasz stringa jest jakiś problem z określeniem końca transmisji i serwer cały czas czeka na jej zakończenie. Serwer działa na wątkach. Każde odebranie wiadomości to stworzenie nowego wątku. Jak połączysz się ponownie wcześniej przesłane dane lądują do nowego wątku. Tak ja to widzę.

@kAzek, w C# klient dobrze działa z tymi wątkami, raczej coś nie tak po stronie klienta Delphi

0

To tez próbowałem i niestety nic się nie zmienia ;/ Jak zrobię Thread jako komentarz to działa tak jakby nic się nie zmieniło.

0

Powiem Ci @Clarc, że też mi się tak wydaje, ale niestety Delphi znam dużo słabiej niż C# i nie wiem jak wywołać tam Stream.

A jakby udało się zrobić to po Stream to mógłbym wysyłać komunikaty i obrazki, co pewnie w przyszłości byłoby bardzo przydatne.

0

Daj binarkę aktualnego serwera. Próbowałeś w Delphi robić na Indy 10?

0

Nie próbowałem, ponieważ mam Turbo Delphi i licencja nie pozwala mi na dogranie Indy.

0

mam Turbo Delphi

ja ze sprawdzeniem po stronie Delphi raczej nie pomogę bo w Tokyo nie ma TCPClient/Server. Dodali za to komponenty od IPWorks. A tych w Turbo Delphi nie ma.

0

A nie mógłbyś spróbować zrobić to najprostszym sposobem poprzez Turbo Delphi? Wydaje mi się, ze można to spokojnie zrobić, gdyż po części to działa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1