Ok so I built a semi-simple multiplayer game that connects through UDP. However I can't get it to fully connect outside my local network. Now! I have seen this question asked and answered many times so here is where it differs from similar questions:
I have already port forwarded the correct ports.
That's right! I am a Minecraft veteran so the one thing I do know how to do is forward ports. ;)
Now, this works fine on my local network. It works if I run two instances on the same machine of course. But it also works if I use a different computer connecting to the LAN IP or if I use a different computer connecting to my routers public address. The connection only seems to fail if the messages are originating from outside the network they are being hosted on.
The host receives incoming messages from clients, but clients do not receive messages from host. And it doesn't mater who is the host and who is the client. So I had my friend try connecting to my game from his house and had a break point on the function that receives the messages. Sure enough I see the expected message come into the host, and then the host sends out the acknowledgement message, but his client never received it. Similarly, if my friend hosts and I try to connect, his host sees the message and sends the acknowledgment but my client never receives it. So at this point we both have the port forwarded but only the host ever receives anything.
So.... Anyone have any idea what I might be doing wrong? Or what Cthulhu of the internet I need to kill?
My Code Below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public enum MessageType
{
None = 0,
Connect = 1,
Command = 2,
EntityUpdate = 3,
Response = 4,
}
[System.Serializable]
public class Message
{
public MessageType _MessageType = MessageType.None;
public object Data = null;
public Guid Origin = Guid.Empty;
public Guid MessageID;
public Message(object data, MessageType type, Guid origin)
{
if (type == MessageType.None || data == null)
{
throw new Exception("Invalid Message!");
}
Data = data;
_MessageType = type;
Origin = origin;
MessageID = Guid.NewGuid();
}
}
public static class NetLayer
{
private const int FRAMES_BETWEEN_SENDS = 2;
private const int NUM_RETRIES = 10;
private const int FAMES_BEFORE_RETRY_ENTITY = 120;
private const int FAMES_BEFORE_RETRY_CLIENT = 30;
private const int BUFF_SIZE = 8 * 2048;
private const int DEFAULT_PORT = 27015;
private const string DEFAULT_ADDRESS = "localhost";
private const int MAX_BYTES = 1100;
private const int MAX_BYTES_PER_PACKET = 1100 - (322 + 14); //281 is number of existing bytes in empty serilized packet //14 is... I dunno. the overhead for having the byte aray populated I assume
private static Socket mSocket = null;
private static List<EndPoint> mClients = null;
private static Dictionary<EndPoint, PlayerData> mUnconfirmedClients = null;
private static string mServerAdress = DEFAULT_ADDRESS;
private static int mPort = DEFAULT_PORT;
public static bool IsServer { get { return mClients != null; } }
public static bool IsConnected = false;
public static bool Initiated { get { return mSocket != null; } }
private static Dictionary<Guid, Packet> mUnconfirmedPackets = null;
private static Queue<Packet> ResendQueue = new Queue<Packet>();
private static EndPoint mCurrentPacketEndpoint;
private static Guid mCurrentMessageID;
private static byte[] mDataToSend;
private static int mCurrentDataIndex = 0;
private static int mCurrentPacketIndex = 0;
private static int mTotalPacketsToSend = 0;
private static Queue<Packet> ResponseQueue = new Queue<Packet>();
private static Dictionary<Guid, Dictionary<int, Packet>> IncomingPacketGroups = new Dictionary<Guid, Dictionary<int, Packet>>();
private static Queue<QueueuedMessage> MessageQueue = new Queue<QueueuedMessage>();
public static int QueueCount { get { return MessageQueue.Count; } }
private static int mCurrentFramesToRetryClient;
private static int mCurrentFramesToRetryEntity;
private static int mLastSendFrame = 0;
[System.Serializable]
public class Packet
{
public byte[] Data;
public Guid MessageID;
public Guid PacketID;
public int PacketNumber;
public int NumPacketsInGroup;
public int NumBytesInGroup;
public bool RequireResponse;
[System.NonSerialized]
public int retries; //We don't need to send this data, just record it locally
[System.NonSerialized]
public EndPoint Destination; //We don't need to send this data, just record it locally
public Packet(byte[] data, Guid messageID, int packetNumber, int numPacketsinGroup, int numBytesInGroup, EndPoint destination, bool requireResponse)
{
Data = data;
if(data.Length > MAX_BYTES_PER_PACKET)
{
Debug.LogError("Creating a packet with a data size of " + data.Length + " which is greater then max data size of " + MAX_BYTES_PER_PACKET);
}
MessageID = messageID;
PacketID = Guid.NewGuid();
PacketNumber = packetNumber;
NumPacketsInGroup = numPacketsinGroup;
NumBytesInGroup = numBytesInGroup;
retries = 0;
Destination = destination;
RequireResponse = requireResponse;
}
}
private class QueueuedMessage
{
public Message m;
public EndPoint e;
public QueueuedMessage(Message message, EndPoint endpoint)
{
m = message;
e = endpoint;
}
}
[System.Serializable]
private class PacketResponse
{
public Guid MessageIDToConfirm;
public PacketResponse(Guid newGuid)
{
MessageIDToConfirm = newGuid;
}
}
public static void InitAsServer(string address, int port)
{
Reset();
mClients = new List<EndPoint>();
mUnconfirmedClients = new Dictionary<EndPoint, PlayerData>();
mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
mSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true);
mSocket.Blocking = false;
mSocket.SendBufferSize = BUFF_SIZE;
mSocket.ReceiveBufferSize = BUFF_SIZE;
mSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
mPort = port;
IsConnected = true;
mUnconfirmedPackets = new Dictionary<Guid, Packet>();
Packet newPacket = new Packet(new byte[MAX_BYTES_PER_PACKET], Guid.NewGuid(), 1, 15, MAX_BYTES_PER_PACKET, null, true);
byte[] serilized = Util.Serialize(newPacket);
Debug.Log("Total Packet Size = " + serilized.Length);
}
public static void InitAsClient(string address, int port)
{
Reset();
mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
mSocket.Blocking = false;
mSocket.SendBufferSize = BUFF_SIZE;
mSocket.ReceiveBufferSize = BUFF_SIZE;
mSocket.Bind(new IPEndPoint(IPAddress.Parse(GetLocalIPAddress()), port));
mServerAdress = address;
mPort = port;
mUnconfirmedPackets = new Dictionary<Guid, Packet>();
}
public static void NetTick()
{
if (Initiated)
{
//Keep retrying connection messages untill we get a response from the player, in case they miss our first message
if(IsServer)
{
mCurrentFramesToRetryClient++;
if (mCurrentFramesToRetryClient >= FAMES_BEFORE_RETRY_CLIENT)
{
mCurrentFramesToRetryClient = 0;
foreach (EndPoint ep in mUnconfirmedClients.Keys)
{
Debug.Log("Pinging client again: " + ep);
SendConnectionConfirmationToClient(ep, mUnconfirmedClients[ep]); //TODO Add timeout. Should stop pinging after 10 attempts
}
}
}
//Retry messages untill we get a responce or give up
mCurrentFramesToRetryEntity++;
if (mCurrentFramesToRetryEntity >= FAMES_BEFORE_RETRY_ENTITY)
{
List<Guid> deadMessages = new List<Guid>();
List<Guid> resendMessages = new List<Guid>();
mCurrentFramesToRetryEntity = 0;
foreach (Guid messageID in mUnconfirmedPackets.Keys)
{
Packet packet = mUnconfirmedPackets[messageID];
packet.retries++;
if (packet.retries > NUM_RETRIES)
{
Debug.LogError("Failed to send Packet " + messageID);
deadMessages.Add(messageID);
}
else
{
resendMessages.Add(messageID);
}
}
//Resend everything we have selected to resend
for (int i = 0; i < resendMessages.Count; i++)
{
Packet packet = mUnconfirmedPackets[resendMessages[i]];
ResendQueue.Enqueue(packet);
}
//Give up on messages that have been marked dead
for (int i = 0; i < deadMessages.Count; i++)
{
mUnconfirmedPackets.Remove(deadMessages[i]);
}
}
if (Time.frameCount - mLastSendFrame >= FRAMES_BETWEEN_SENDS)
{
if (ResponseQueue.Count > 0) //Send responses first
{
Packet p = ResponseQueue.Dequeue();
SendImmediate(p, p.Destination, false);
}
else if (ResendQueue.Count > 0) //Then resends
{
Packet p = ResendQueue.Dequeue();
SendImmediate(p, p.Destination, false);
}
else //Then normal messages
{
StreamSend();
}
mLastSendFrame = Time.frameCount;
}
}
}
public static string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
return null;
}
public static void Reset()
{
if(mSocket != null)
{
mSocket.Close();
}
mSocket = null;
mClients = null;
mServerAdress = DEFAULT_ADDRESS;
mPort = DEFAULT_PORT;
IsConnected = false;
}
public static void Send(Message newMessage, EndPoint endpoint = null)
{
if (IsServer)
{
if (endpoint != null)
{
SendToQueue(new QueueuedMessage(newMessage, endpoint));
}
else
{
for (int i = 0; i < mClients.Count; i++)
{
SendToQueue(new QueueuedMessage(newMessage, mClients[i]));
}
}
}
else
{
SendToQueue(new QueueuedMessage(newMessage, new IPEndPoint(IPAddress.Parse(mServerAdress), mPort)));
}
}
private static void SendToQueue(QueueuedMessage newPacket)
{
MessageQueue.Enqueue(newPacket);
}
private static void StreamSend()
{
if(mDataToSend != null)
{
//Get number of bytes to send in this packet
int bytesToSend = Mathf.Min(mDataToSend.Length - mCurrentDataIndex, MAX_BYTES_PER_PACKET);
//Populate packet byte array
byte[] data = new byte[bytesToSend];
for(int i = 0; i < bytesToSend; i++)
{
data[i] = mDataToSend[mCurrentDataIndex + i];
}
//Increment index by the nubmer of bytes sent
mCurrentDataIndex += bytesToSend;
//Create and send packet
Packet newPacket = new Packet(data, mCurrentMessageID, mCurrentPacketIndex, mTotalPacketsToSend, mDataToSend.Length, mCurrentPacketEndpoint, true);
SendImmediate(newPacket, mCurrentPacketEndpoint, true);
//Increment packet index
mCurrentPacketIndex++;
if(mCurrentDataIndex >= mDataToSend.Length)
{
//We have finished sending. Clear data array
mDataToSend = null;
}
}
else if(MessageQueue.Count > 0)
{
//Prepare the next group for packing
QueueuedMessage queueuedMessage = MessageQueue.Dequeue();
mCurrentMessageID = queueuedMessage.m.MessageID;
mCurrentPacketEndpoint = queueuedMessage.e;
mDataToSend = Util.Serialize(queueuedMessage.m);
mCurrentDataIndex = 0;
mCurrentPacketIndex = 1;
mTotalPacketsToSend = Mathf.CeilToInt((float)mDataToSend.Length / (float)MAX_BYTES_PER_PACKET);
}
}
public static void SendImmediate(Packet packet, EndPoint endpoint, bool verify)
{
byte[] data = Util.Serialize(packet);
if(data.Length > MAX_BYTES)
{
Debug.LogError("Attempting to send packet with " + data.Length + " bytes. This will most likely fail! Please ensure all packets are limited to " + MAX_BYTES + " bytes or less!");
}
mSocket.SendTo(data, endpoint);
//Don't assume the packet is late untill we have actually sent it.
//The old way of verifying. Instead things sending imediate that don't want to be veiried should set verify to false if (newMessage._MessageType != MessageType.Response && newMessage._MessageType != MessageType.Connect && !mUnconfirmedPackets.ContainsKey(newMessage.MessageID))
if (verify)
{
mUnconfirmedPackets.Add(packet.PacketID, packet);
}
}
public static void SendConnectionMessage(PlayerData playerData)
{
Message message = new Message(playerData, MessageType.Connect, playerData.ID);
Send(message);
}
public static void SendCommand(Command command, EndPoint ep = null)
{
Message message = new Message(command, MessageType.Command, GameManager.LocalPlayer.PlayerID);
Send(message, ep);
}
public static void SendEntityUpdate(EntityData entityData, EndPoint ep = null)
{
Message message = new Message(entityData, MessageType.EntityUpdate, GameManager.LocalPlayer.PlayerID);
Send(message, ep);
}
public static void Receive()
{
if (Initiated && mSocket.Available > 0)
{
byte[] bytes = new byte[BUFF_SIZE];
EndPoint epFrom = new IPEndPoint(IPAddress.Any, 0); //TODO Do something here to stop reciving messages from dead clients...
mSocket.ReceiveFrom(bytes, ref epFrom);
Reassemble(bytes, epFrom);
}
}
public static void Reassemble(byte[] bytes, EndPoint epFrom)
{
Packet receivedPacket = Util.Deserilize(bytes) as Packet;
//Create new packet group if one does not already exist
if(!IncomingPacketGroups.ContainsKey(receivedPacket.MessageID))
{
IncomingPacketGroups.Add(receivedPacket.MessageID, new Dictionary<int, Packet>());
}
Dictionary<int, Packet> currentPacketGroup = IncomingPacketGroups[receivedPacket.MessageID];
currentPacketGroup[receivedPacket.PacketNumber] = receivedPacket;
if(currentPacketGroup.Count == receivedPacket.NumPacketsInGroup)
{
byte[] completedData = new byte[receivedPacket.NumBytesInGroup];
int index = 0;
for(int i = 0; i < receivedPacket.NumPacketsInGroup; i++)
{
Packet currentPacket = currentPacketGroup[i + 1];
for(int j = 0; j < currentPacket.Data.Length; j++)
{
completedData[index] = currentPacket.Data[j];
index++;
}
}
//Clean up the packet group now that it is compleated
IncomingPacketGroups.Remove(receivedPacket.MessageID);
//Call this once we have re-assembled a whole message
Message receivedMessage = Util.Deserilize(completedData) as Message;
ReceiveCompleateMessage(receivedMessage, epFrom);
}
//Make sure to call back to sender to let them know we got this packet
if(receivedPacket.RequireResponse)
{
SendMessageResponse(receivedPacket.PacketID, epFrom);
}
}
private static void ReceiveCompleateMessage(Message receivedMessage, EndPoint epFrom)
{
if (receivedMessage.Origin == GameManager.LocalPlayer.PlayerID)
{
//Reject messages we sent ourselves. Prevent return from sender bug.
return;
}
//TODO Consider adding a check that adds the endpoing into the connections even if we don't have a connecton message because players can sometimes re-connect.
//TODO or maybe reject messages from unknown clients becasue we shouldn't trust them
switch (receivedMessage._MessageType)
{
case MessageType.None:
Debug.LogError("Recived message type of None!");
break;
case MessageType.EntityUpdate:
GameManager.ProcessEntityUpdate(receivedMessage);
break;
case MessageType.Command:
GameManager.ProcessCommand(receivedMessage);
break;
case MessageType.Response:
ProcessResponse(receivedMessage);
break;
case MessageType.Connect:
ProcessConnection(receivedMessage, epFrom);
break;
}
//If this is the server reciving a messgae, and that message was not sent by us
if (IsServer && receivedMessage._MessageType != MessageType.Connect && receivedMessage._MessageType != MessageType.Response && receivedMessage.Origin != GameManager.LocalPlayer.PlayerID)
{
//Mirror the message to all connected clients
Send(receivedMessage);
}
}
public static void ProcessConnection(Message receivedMessage, EndPoint epFrom)
{
//This is the only time where we should process the data directly in net layer since we need the endpoint data
if (IsServer)
{
if (!mClients.Contains(epFrom))
{
if (epFrom != null)
{
mClients.Add(epFrom);
Debug.Log("Client attempting to join: " + epFrom.ToString()); //TODO becasue we are binding socket this won't work locally anymore but see if it at least works globally before trying to fix
//Get new player from recived data
PlayerData newPlayer = receivedMessage.Data as PlayerData;
newPlayer.Team = GameManager.GetNewTeam();
newPlayer.HostHasResponded = true;
if (!mUnconfirmedClients.ContainsKey(epFrom))
{
mUnconfirmedClients.Add(epFrom, newPlayer);
}
SendConnectionConfirmationToClient(epFrom, newPlayer);
}
else
{
Debug.LogError("Got connection message from null!");
}
}
else
{
if (mUnconfirmedClients.ContainsKey(epFrom))
{
PlayerData newPlayerData = receivedMessage.Data as PlayerData;
if (newPlayerData.HostHasResponded)
{
mUnconfirmedClients.Remove(epFrom);
if (epFrom != null)
{
Debug.Log("Client Confirmed, sending update: " + epFrom.ToString());
//Sent all known entitys to the new client so it's up to date
foreach (Guid key in GameManager.Entities.Keys)
{
SendEntityUpdate(GameManager.Entities[key].GetEntityData(), epFrom);
}
}
}
else
{
Debug.LogWarning("We sent client an confirmation but it is still sending us initial connection messages: " + epFrom.ToString());
}
}
else
{
Debug.LogWarning("Client Already connected but still sent connnection message: " + epFrom.ToString());
}
}
}
else
{
ReciveConnectionConfirmationFromHost(receivedMessage, epFrom);
}
}
public static void ProcessResponse(Message message)
{
PacketResponse response = (PacketResponse)message.Data;
mUnconfirmedPackets.Remove(response.MessageIDToConfirm);
}
public static void SendConnectionConfirmationToClient(EndPoint epFrom, PlayerData newPlayer)
{
//endpoint can be null if this is the server connecting to itself
if (epFrom != null)
{
//Tell client we have accepted its connection, and send back the player data with the updated team info
Message message = new Message(newPlayer, MessageType.Connect, GameManager.LocalPlayer.PlayerID);
//Send(message, epFrom);
byte[] data = Util.Serialize(message);
Packet newPacket = new Packet(data, message.MessageID, 1, 1, data.Length, epFrom, false);
ResponseQueue.Enqueue(newPacket);
}
}
public static void ReciveConnectionConfirmationFromHost(Message receivedMessage, EndPoint epFrom)
{
//Clear the loading win we had active
GameObject.Destroy(GameManager.LoadingWin);
PlayerData newPlayer = receivedMessage.Data as PlayerData;
GameManager.LocalPlayer.SetPlayerData(newPlayer);
IsConnected = true;
PingBack(epFrom);
}
public static void PingBack(EndPoint ep)
{
PlayerData localPlayer = GameManager.LocalPlayer.GetPlayerData();
Message message = new Message(localPlayer, MessageType.Connect, localPlayer.ID);
byte[] data = Util.Serialize(message);
Packet newPacket = new Packet(data, message.MessageID, 1, 1, data.Length, ep, false);
ResponseQueue.Enqueue(newPacket);
}
private static void SendMessageResponse(Guid packetIDToConfirm, EndPoint epFrom)
{
PacketResponse messageIDtoConfirm = new PacketResponse(packetIDToConfirm);
Message message = new Message(messageIDtoConfirm, MessageType.Response, GameManager.LocalPlayer.PlayerID);
byte[] data = Util.Serialize(message);
Packet newPacket = new Packet(data, message.MessageID, 1, 1, data.Length, epFrom, false);
ResponseQueue.Enqueue(newPacket);
}
}
[Edit]
So I continued to do research and refine my code. (code above has been updated to current version). One thing I was definitely doing wrong before was blowing up the MTU because I didn't realize the external limit was much smaller than the internal limit. Still getting the same problem though. Server can see the client message, uses the incoming endpoint to send a connection message back, and client never gets it. Currently the server will attempt to resend the connection confirmation indefinitely until the client responds back which never happens because none of the server responses ever reach the client.
My understanding of a UDP punch through is that both machines must first connect to a known external server. That external server then gives each incoming connection the others IP and port, allowing them to talk to each other directly. However, since I am manually feeding the client the IP and port of the game server, I shouldn't need an external server to do that handshake.
Client doesn't need to connect to an external server first to get the game server IP and port because I manual type in the IP and port of the game server and the game server network has the port already forwarded, so the servers address IS already known. I know for a fact that the client message is reaching the game server because server logs the incoming connection.
The game server gets the IP and port of the client from the incoming message, which is exactly what would be handed to it by the external server by the handshake. I have double checked the IP the server receives from the connection and it matches the external IP address of the client. I also verified that both machines firewalls are allowing the program through.
I would deeply appreciate some more help on figuring out why server is failing to deliver a message back to the client. Explanations or links to useful documents or research please. Telling me to do more research or look online for the answers isn't helpful. I am already doing that. The point of a questions and answers is to get help from people who already know the answers.
The problem is that there exists NAT between LAN and WLAN. You cannot just work through it so simple.
I have a TCPClient and Server , everything works fine on my pc,if i send client to other pc (in same network) , client connect to server and can send data but client dont read data or server dont send , i have no errors nothing...What is the cause of this ?
i tried port forwording
changing from tcp to sockets
exactly same thing
adding firewall exception
removing antivirus
server
using System.Threading;
using System.Net.Sockets;
using System.Text;
using System.Collections;
using System.Net;
namespace ConsoleApplication1
{
class Program
{
public static Hashtable clientsList = new Hashtable();
static void Main(string[] args)
{
TcpListener serverSocket = new TcpListener(IPAddress.Any,3306);
TcpClient clientSocket = default(TcpClient);
int counter = 0;
serverSocket.Start();
Console.WriteLine("Chat Server Started ....");
counter = 0;
while ((true))
{
try
{
counter += 1;
clientSocket = serverSocket.AcceptTcpClient();
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
NetworkStream networkStream = clientSocket.GetStream();
networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
clientsList.Add(dataFromClient, clientSocket);
broadcast(dataFromClient + " Joined ", dataFromClient, false);
Console.WriteLine(dataFromClient + " Joined chat room ");
handleClinet client = new handleClinet();
client.startClient(clientSocket, dataFromClient, clientsList);
}
catch { }
}
Console.ReadKey();
clientSocket.Close();
serverSocket.Stop();
Console.WriteLine("exit");
Console.ReadLine();
}
public static void broadcast(string msg, string uName, bool flag)
{
try
{
foreach (DictionaryEntry Item in clientsList)
{
TcpClient broadcastSocket;
broadcastSocket = (TcpClient)Item.Value;
NetworkStream broadcastStream = broadcastSocket.GetStream();
Byte[] broadcastBytes = null;
if (flag == true)
{
broadcastBytes = Encoding.ASCII.GetBytes(uName + " says : " + msg);
}
else
{
broadcastBytes = Encoding.ASCII.GetBytes(msg);
}
broadcastStream.Write(broadcastBytes, 0, broadcastBytes.Length);
broadcastStream.Flush();
}
}
catch { Console.ReadKey(); }
} //end broadcast function
}//end Main class
public class handleClinet
{
TcpClient clientSocket;
string clNo;
Hashtable clientsList;
public void startClient(TcpClient inClientSocket, string clineNo, Hashtable cList)
{
this.clientSocket = inClientSocket;
this.clNo = clineNo;
this.clientsList = cList;
Thread ctThread = new Thread(doChat);
ctThread.Start();
}
private void doChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
Byte[] sendBytes = null;
string serverResponse = null;
string rCount = null;
requestCount = 0;
while ((true))
{
try
{
requestCount = requestCount + 1;
NetworkStream networkStream = clientSocket.GetStream();
networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
if (dataFromClient == "closing")
{
clientsList.Remove(clNo);
Program.broadcast("Sa deconectat", clNo, true);
break;
}
Console.WriteLine("From client - " + clNo + " : " + dataFromClient);
rCount = Convert.ToString(requestCount);
Program.broadcast(dataFromClient, clNo, true);
}
catch (Exception ex)
{
Console.WriteLine("Opsy");
Console.ReadKey();
}
}//end while
}//end doChat
} //end class handleClinet
}//end namespace
I have no erros even if i remove try catch, i removed them from server and client and no errors...
Just took a quick look so not sure if this is the problem but you need to tell the program specific IP and ports to connect to when you're not testing on your own local system. You shouldn't need to worry about port forwarding as long as you are on the same subnet, but you should let the client know and server know what ports and addresses you want to use.
I am using jamaa-smpp to send sms. But I am not able to send more than 160 characters using the api. I am using the following link http://jamaasmpp.codeplex.com/
The code is as follows
SmppConnectionProperties properties = _client.Properties;
properties.SystemID = "test";
properties.Password = "test1";
properties.Port = 101; //IP port to use
properties.Host = "..."; //SMSC host name or IP Address
....
Is it possible to send more than 160 character using that API.
I found the solution it was there on the website discussion. I am posting it here so that one can find solution here.
I replaced the existing function in the TextMessage.cs (JamaaTech.Smpp.Net.Client).
The function name is IEnumerable<SendSmPDU> GetPDUs(DataCoding defaultEncoding)
//protected override IEnumerable<SendSmPDU> GetPDUs(DataCoding defaultEncoding)
//{
// //This smpp implementation does not support sending concatenated messages,
// //however, concatenated messages are supported on the receiving side.
// int maxLength = GetMaxMessageLength(defaultEncoding, false);
// byte[] bytes = SMPPEncodingUtil.GetBytesFromString(vText, defaultEncoding);
// //Check message size
// if(bytes.Length > maxLength)
// {
// throw new InvalidOperationException(string.Format(
// "Encoding '{0}' does not support messages of length greater than '{1}' charactors",
// defaultEncoding, maxLength));
// }
// SubmitSm sm = new SubmitSm();
// sm.SetMessageBytes(bytes);
// sm.SourceAddress.Address = vSourceAddress;
// sm.DestinationAddress.Address = vDestinatinoAddress;
// sm.DataCoding = defaultEncoding;
// if (vRegisterDeliveryNotification) { sm.RegisteredDelivery = RegisteredDelivery.DeliveryReceipt; }
// yield return sm;
//}
protected override IEnumerable<SendSmPDU> GetPDUs(DataCoding defaultEncoding)
{
SubmitSm sm = new SubmitSm();
sm.SourceAddress.Address = vSourceAddress;
sm.DestinationAddress.Address = vDestinatinoAddress;
sm.DataCoding = defaultEncoding;
if (vRegisterDeliveryNotification)
sm.RegisteredDelivery = RegisteredDelivery.DeliveryReceipt;
int maxLength = GetMaxMessageLength(defaultEncoding, false);
byte[] bytes = SMPPEncodingUtil.GetBytesFromString(vText, defaultEncoding);
if (bytes.Length > maxLength)
{
var SegID = new Random().Next(1000, 9999);
var messages = Split(vText, GetMaxMessageLength(defaultEncoding, true));
var totalSegments = messages.Count;
var udh = new Udh(SegID, totalSegments, 0);
for (int i = 0; i < totalSegments; i++)
{
udh.MessageSequence = i + 1;
sm.Header.SequenceNumber = PDUHeader.GetNextSequenceNumber();
sm.SetMessageText(messages[i], defaultEncoding, udh);
yield return sm;
}
}
else
{
sm.SetMessageBytes(bytes);
yield return sm;
}
}
private static List<String> Split(string message, int maxPartLength)
{
var result = new List<String>();
for (int i = 0; i < message.Length; i += maxPartLength)
{
var chunkSize = i + maxPartLength < message.Length ? maxPartLength : message.Length - i;
var chunk = new char[chunkSize];
message.CopyTo(i, chunk, 0, chunkSize);
result.Add(new string(chunk));
}
return result;
}
I created a simple TCP Listener to handle HL7 messages, I am receiving the messages correctly, and attempting to send an ACK message back. The server on the other end doesn't seem to be getting the responses though, do you see anything wrong with this set up?
I realize it needs refactored a little, right now I'm just trying to establish the connection.
class Server
{
private TcpListener tcpListener;
private Thread listenThread;
public Server()
{
this.tcpListener = new TcpListener(IPAddress.Parse("hidden"), 55555);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpListener.Start();
while (true)
{
TcpClient client = this.tcpListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
break;
}
if (bytesRead == 0)
{
break;
}
ASCIIEncoding encoder = new ASCIIEncoding();
string result = encoder.GetString(message, 0, bytesRead);
string[] Lines = result.Split('\n');
string id = "";
foreach (string line in Lines)
{
string[] values = line.Split('|');
if (values[0].Contains("MSH"))
{
id = values[9];
byte[] buffer = encoder.GetBytes("\\vMSH|^~\\&|Rhapsody|JCL|EpicADT|JCL-EPIC-TEST|||ACK|A" + id + "|P|2.4|\\nMSA|AA|" + id + "|");
Console.WriteLine("MSH|^~\\&|Rhapsody|Test|EpicADT|TEST|||ACK|A" + id + "|P|2.4|\\nMSA|AA|" + id + "|");
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
}
}
tcpClient.Close();
}
}
I do not see MLLP implemented while you are reading the message and writing response on socket.
Generally MLLP is necessary and most of the applications verify the MLLP block. Without MLLP, client just skip your data being written on socket.
Apparently, your application do not have any issue without MLLP. This answer is not valid if Client does not implement MLLP.
I have explained this in more details in my other answer.
Is it possible to encrypt data with multiple encryption algorithms using the BouncyCastle open source library? I have written the code below to pass the output from one cipher as input to the other cipher but I am always getting the same output bytes.
Thanks in advance,
Ahmad Yassin
Boolean ProcessFile(
PaddedBufferedBlockCipher aesCipher,
PaddedBufferedBlockCipher serpentCipher,
PaddedBufferedBlockCipher twofishCipher,
FileStream fin,
FileStream fout,
int addOnLength)
{
int inputSize = aesCipher.GetBlockSize();
int outputSize = aesCipher.GetOutputSize(inputSize);
byte[] inputBuffer = new byte[inputSize];
byte[] outBuffer = new byte[outputSize];
long fileLenght = fin.Length - addOnLength;
long progressStep = fileLenght >= 100 ? fileLenght / 100 : fileLenght;
long totalLength = 0;
int inLength = 0;
int outLength;
try
{
while ((inLength = fin.Read(inputBuffer, 0, inputSize)) != 0)
{
outLength = aesCipher.ProcessBytes(inputBuffer, 0, inLength, outBuffer, 0);
outLength = serpentCipher.ProcessBytes(outBuffer, 0, outLength, outBuffer, 0);
outLength = twofishCipher.ProcessBytes(outBuffer, 0, outLength, outBuffer, 0);
fout.Write(outBuffer, 0, outLength);
totalLength += inLength;
if (totalLength >= progressStep)
{
DoProgressChanged();
totalLength = totalLength % progressStep;
}
}
outLength = aesCipher.DoFinal(outBuffer, 0);
fout.Write(outBuffer, 0, outLength);
fout.Close();
fin.Close();
return true;
}
catch (IOException e)
{
throw new IOException(e.Message);
}
}
public event EventHandler ProgressChanged;
private void DoProgressChanged()
{
if (this.ProgressChanged != null)
this.ProgressChanged(this, new EventArgs());
}
enum Mode { Encription = 1, Decription = 0 }
enum Algorithm { AES = 1, Serpent = 2, Twofish = 3 }
PaddedBufferedBlockCipher GetCipher(Algorithm algorithm, Mode mode, byte[] key, byte[] iv)
{
IBlockCipher blockCipher;
ICipherParameters parameters = new ParametersWithIV(new KeyParameter(key), iv);
switch (algorithm)
{
case Algorithm.AES:
blockCipher = new AesFastEngine();
break;
case Algorithm.Serpent:
blockCipher = new SerpentEngine();
break;
case Algorithm.Twofish:
blockCipher = new TwofishEngine();
break;
default:
blockCipher = new AesFastEngine();
break;
}
IBlockCipher cbcblockCipher = new CbcBlockCipher(blockCipher);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbcblockCipher, new Pkcs7Padding());
cipher.Init(mode == Mode.Encription, parameters);
return cipher;
}
Of course it is possible, but I would strongly suggest you use some kind of connected streams, or perform three passes over the buffer (first AES, then Serpent, then TwoFish or any other order). Don't perform a block size encryption for every cipher as you do now, as block sizes and other parameters may differ.
Of course, the usefulness of using three separate encryption algorithms is debatable.