使用套接字将值从服务器发送到客户端 [英] Sending a value from server to client with sockets

查看:59
本文介绍了使用套接字将值从服务器发送到客户端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用以下项目,以便在








测试代码:



服务器

 使用系统; 
使用System.Collections.Generic;
使用System.Linq;
使用System.Net;
使用System.Net.Sockets;
使用System.Text;
使用System.Threading.Tasks;

命名空间TcpListenerConsoleApplication {
class Program {
const int PORT_NO = 2201;
const string SERVER_IP = 127.0.0.1;
静态套接字serverSocket;
静态void Main(string [] args){
//-在指定的IP和端口号上监听。-
Console.WriteLine( Listening ...) ;
serverSocket =新的Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
serverSocket.Bind(new IPEndPoint(IPAddress.Any,PORT_NO));
serverSocket.Listen(4); //最大待定客户端,根据需要定义
serverSocket.BeginAccept(new AsyncCallback(acceptCallback),null);
string result =;
do {
result = Console.ReadLine();
} while(result.ToLower()。Trim()!=退出);
}

private const int BUFFER_SIZE = 4096;
私有静态字节[]缓冲区=新字节[BUFFER_SIZE]; ///每条消息的缓冲区大小限制为BUFFER_SIZE
private static void acceptCallback(IAsyncResult result){//如果缓冲区较旧,则可能已经有东西了...
Socket socket = null;
try {
socket = serverSocket.EndAccept(result); // objectDisposedException将出现在这里...因此,这是可以预料的!
//根据客户接受的需要执行一些操作
socket.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,new AsyncCallback(receiveCallback),socket);
serverSocket.BeginAccept(new AsyncCallback(acceptCallback),null); // //接收另一个客户
} catch(Exception e){//当处理 this时将发生此异常...
//在此处执行操作
Console.WriteLine( e.ToString());
}
}

const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0; //显然,这并非万无一失,因为实际上您必须为多个客户端拥有多个副本,但是为了简单起见,我将此
私有静态void receiveCallback(IAsyncResult result){
Socket socket = null;
try {
socket =(Socket)result.AsyncState; // //如果(socket.Connected){//简单检查
,则获取发送者
int receive = socket.EndReceive(result);
if(received> 0){
byte [] data = new byte [received]; //数据采用字节[]格式,而不是字符串!
Buffer.BlockCopy(buffer,0,data,0,data.Length); //根据https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c的一般说明,有几种方法可以实现,System.Buffer.memcpyimpl是最快的
//对数据int字节做些什么[]!义哈!
Console.WriteLine(Encoding.UTF8.GetString(data)); //这里我只是打印出来,但是您需要做其他事情

//消息检索部分
//假设您只想声明您从一个客户端接收数据到该客户端
string msg =我在以下位置收到您的消息: + DateTime.Now;
socket.Send(Encoding.ASCII.GetBytes(msg)); ///请注意,您实际上是以字节[]
Console.WriteLine(此消息发送给客户端的消息: + msg);发送数据

receiveAttempt = 0; //重置接收尝试
socket.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,new AsyncCallback(receiveCallback),socket); // repeat beginReceive
}否则,如果(receiveAttempt< MAX_RECEIVE_ATTEMPT){//失败但不超过最大尝试次数,则重复
++ receiveAttempt; //增加接收尝试;
socket.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,new AsyncCallback(receiveCallback),socket);复制代码//重复beginReceive
} else {//完全失败!
Console.WriteLine( receiveCallback失败!); //不要重复beginReceive
receiveAttempt = 0; //将其重置为下一个连接
}
}
} catch(Exception e){//处理 this时将发生此异常...
控制台.WriteLine( receiveCallback失败,异常! + e.ToString());
}
}

}
}

客户

 使用系统; 
使用System.Collections.Generic;
使用System.Linq;
使用System.Net;
使用System.Net.Sockets;
使用System.Text;
使用System.Threading.Tasks;

命名空间TcpClientConsoleApplication {
class Program {
const int PORT_NO = 2201;
const string SERVER_IP = 127.0.0.1;
静态套接字clientSocket; //在这里放入
static void Main(string [] args){
//类似地,一开始就开始定义客户端套接字。
clientSocket =新的Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
loopConnect(3,3); //用于失败处理
string result =;
do {
result = Console.ReadLine(); //you need to change this part
if (result.ToLower().Trim() != \"exit\") {
byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
//do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
clientSocket.Send(bytes);
}
} while (result.ToLower().Trim() != \"exit\");
}

static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
int attempts = 0;
while (!clientSocket.Connected && attempts < noOfRetry) {
try {
++attempts;
IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnectCallback, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
} catch (Exception e) {
Console.WriteLine(\"Error: \" + e.ToString());
}
}
if (!clientSocket.Connected) {
Console.WriteLine(\"Connection attempt is unsuccessful!\");
的回报;
}
}

private const int BUFFER_SIZE = 4096;
private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
private static void endConnectCallback(IAsyncResult ar) {
try {
clientSocket.EndConnect(ar);
if (clientSocket.Connected) {
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
} else {
Console.WriteLine(\"End of connection attempt, fail to connect...\");
}
} catch (Exception e) {
Console.WriteLine(\"End-connection attempt is unsuccessful! \" + e.ToString());
}
}

const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0;
private static void receiveCallback(IAsyncResult result) {
System.Net.Sockets.Socket socket = null;
try {
socket = (System.Net.Sockets.Socket)result.AsyncState;
if (socket.Connected) {
int received = socket.EndReceive(result);
if (received > 0) {
receiveAttempt = 0;
byte[] data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
//DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
//Notice that your data is not string! It is actually byte[]
//For now I will just print it out
Console.WriteLine(\"Server: \" + Encoding.UTF8.GetString(data));
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
++receiveAttempt;
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else { //completely fails!
Console.WriteLine(\"receiveCallback is failed!\");
receiveAttempt = 0;
clientSocket.Close();
}
}
} catch (Exception e) { // this exception will happen when \"this\" is be disposed...
Console.WriteLine(\"receiveCallback is failed! \" + e.ToString());
}
}
}
}






Last Remarks (Edit)



Since the code above is run using Console Application it must be run with static main void keyword. And thus client Socket defined above is of static type. This may prevent the client Socket to be defined multiple times as each time it is \"defined\", since it is of the same class named Program, it will refer to the same Socket (though this may not always the case, at least according to the OP’s experiment: he can run multiple clients successfully in the same computer).



Nevertheless, to overcome this is not that hard. Simply port the client application to the platform which is non-initiated as static class (such as WinForms) and all the above code would still run as per normal. Alternatively, if it must be run using the Console Applications, and the problem occurs, simply copy the client application and re-define it using different namespace or different class name to avoid defining identical Socket due to identical namespace or class.



But the most important part on this problem solving is the use of Async and Sync wisely to solve the given issue.






Continuation of this topic can be found here


I am using the following projects in order to create an asynchronous communication between server and client sockets. When I am running those projects I am sending a message from the client to the server thus I got the message:

Data : recording EOF, Sent 14 bytes to client.

What I want to achieve is to send a boolean variable from the server to the client with the sockets. Is it doable to do so, I am wondering since in the code I have the server which waiting and listens and the client which sends data, can I do the opposite? In general what I want is to send a boolean to several clients. Why am I need the End of File in order to send a string? Is it necessary to convert everything to string?

EDIT: In general what I want is to send a variable from one computer to two others in order a process to begin simultaneously in all computers. In fact to create a switcher that gives a signal to begin a process in 2-3 machines in the same time.

I tried to use the following code for server:

class Program
{
    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static void Main(string[] args)
    {
        //---listen at the specified IP and port no.---
        IPAddress localAdd = IPAddress.Parse(SERVER_IP);
        TcpListener listener = new TcpListener(localAdd, PORT_NO);
        Console.WriteLine("Listening...");
        listener.Start();
        //---incoming client connected---
        TcpClient client = listener.AcceptTcpClient();
        //---get the incoming data through a network stream---
        NetworkStream nwStream = client.GetStream();
        byte[] buffer = new byte[client.ReceiveBufferSize];
        //---read incoming stream---
        int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
        //---convert the data received into a string---
        string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
        Console.WriteLine("Received : " + dataReceived);
        //---write back the text to the client---
        Console.WriteLine("Sending back : " + dataReceived);
        nwStream.Write(buffer, 0, bytesRead);
        client.Close();
        listener.Stop();
        Console.ReadLine();
    }
}

and for client:

class Program
{
    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static void Main(string[] args)
    {
        //---data to send to the server---
        string textToSend = DateTime.Now.ToString();
        //---create a TCPClient object at the IP and port no.---
        TcpClient client = new TcpClient(SERVER_IP, PORT_NO);
        NetworkStream nwStream = client.GetStream();
        byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(textToSend);
        //---send the text---
        Console.WriteLine("Sending : " + textToSend);
        nwStream.Write(bytesToSend, 0, bytesToSend.Length);
        //---read back the text---
        byte[] bytesToRead = new byte[client.ReceiveBufferSize];
        int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
        Console.WriteLine("Received : " + Encoding.ASCII.GetString(bytesToRead, 0, bytesRead));
        Console.ReadLine();
        client.Close();
    }
}

Just in the case I am working in the same machine. I will have in total 4 machines and I want the one of them to give a singal to the rest of them to begin recording rgb stream. Thus the server should send signal to the clients to begin recording. What should I do to change the behavior of the server to send data and not listen. Is it possible to have several machines listening and waiting for a signal to be given?

EDIT:

private void mouseClick1(object sender, MouseEventArgs e)
    {

        Thread thread = new Thread(() => StartServer());
        thread.Start();

        if (e.Button == MouseButtons.Left)
        {
            button5.Enabled = false;
            button3.Enabled = true;

            try
            {
                obj = new Capturer();
            }
            catch (Exception e1)
            {
                Console.WriteLine("The process failed: {0}", e1.ToString());
            }
        }
    }

    private void mouseClick2(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Right)
        {
            obj.flag2 = true;
        }
    }

My code as it is now with left click calls startServer() function with a new thread which is the main code in @Ians implementation and afterthat I call my object. When I click right click I change a flag and the capturer stops. How can I stop also the server or pause in order to open it again with the left click?

解决方案

Answers to the questions first:

Q: Is it necessary to convert everything to string?...In general what I want is to send a variable from one computer to two others in order a process to begin simultaneously in all computers.

A: No, it is not necessary to convert everything to string when sending using Socket. You may send byte[] which you most probably want.

Q: What I want to achieve is to send a boolean variable from the server to the client with the sockets

A: Do you mean boolean or byte? Because the basic variable type which you will get from the Socket is byte. You could always change byte to bool from the sender/receiver side by doing like

bool val = byteToCheck > 0;

A2: And since your server is Console Application, I recommend to take a look on hex string to byte[] conversion. This way, you could write something in string but interprets it as byte[]. Check this. The whole idea here is pretty simple. That is: you type in string, but it will be sent as byte[]. And since it is byte[] you can have any value in it.

And here I present my solution to handle your (1) multiple clients, (2) Async connect & accept & receive, but having (3) send sync, as well as (4) conversion from hex string to byte[] (the structure & idea), and last but not least (5) working code with user input (for you to change this part) for testing!

I would solve such problem using simple Socket class, since it is the solution I am most familiar with. But you could always do similarly if you use your TcpListener.Server (which is the underlying network of Socket class). And, as you desire, I would do that with ASync.

There are several steps needed to achieve what you want in both your server and your client:


Server

  1. Make your Socket as class field rather than method field, since you will use if everywhere and you need multiple methods to achieve what you want. And initialize it as soon as you started your main routine.

    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static Socket serverSocket; //put here as static
    static void Main(string[] args) {
        //---listen at the specified IP and port no.---
        Console.WriteLine("Listening...");
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
        serverSocket.Listen(4); //the maximum pending client, define as you wish
        //your next main routine
    }
    

  2. Since the server will serve many clients, I will recommend you to use ASync rather than Sync for the process. Initialize your Socket by using BeginAccept rather than using Accept, put acceptCallback in your BeginAccept

    static void Main(string[] args) {
        //---listen at the specified IP and port no.---
        Console.WriteLine("Listening...");
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
        serverSocket.Listen(4); //the maximum pending client, define as you wish
        serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);      
        //other stuffs
    }
    

  3. Define acceptCallback, which is where you will go when you accept a Socket. Put EndAccept there.

    private void acceptCallback(IAsyncResult result) { //if the buffer is old, then there might already be something there...
        System.Net.Sockets.Socket socket = null;
        try {
            socket = serverSocket.EndAccept(result); // To get your client socket
            //do something later
        } catch (Exception e) { // this exception will happen when "this" is be disposed...        
            //do something later
        }
    }
    

  4. I would typically list my client sockets, and do something on client disposal (that is unlisted it) - but this depends on the need. In this case, you seem to need it. And don't forget to create buffers, etc... This is for buffering the incoming data.

  5. Start to accept something received from the client, using another ASync BeginReceive on the client Socket (and now you need receiveCallback). Then, very important, repeat your BeginAccept to accept other clients!

    private const int BUFFER_SIZE = 4096;
    private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
    private static List<Socket> clientSockets = new List<Socket>(); //may be needed by you
    private static void acceptCallback(IAsyncResult result) { //if the buffer is old, then there might already be something there...
        Socket socket = null;
        try {
            socket = serverSocket.EndAccept(result); // The objectDisposedException will come here... thus, it is to be expected!
            //Do something as you see it needs on client acceptance such as listing
            clientSockets.Add(socket); //may be needed later
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
            serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
        } catch (Exception e) { // this exception will happen when "this" is be disposed...        
            //Do something here
            Console.WriteLine(e.ToString());
        }
    }
    

  6. Define your receiveCallback, that is, when you receive something from your client. This part could be quite tricky because of failures! But basically, what you need for now is simply EndReceive and again, very important, to repeat the BeginReceive from the same client such that you can receive its next message!

    const int MAX_RECEIVE_ATTEMPT = 10;
    static int receiveAttempt = 0; //this is not fool proof, obviously, since actually you must have multiple of this for multiple clients, but for the sake of simplicity I put this
    private static void receiveCallback(IAsyncResult result) {
        Socket socket = null;
        try {
            socket = (Socket)result.AsyncState; //this is to get the sender
            if (socket.Connected) { //simple checking
                int received = socket.EndReceive(result);
                if (received > 0) {
                    byte[] data = new byte[received]; //the data is in the byte[] format, not string!
                    Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                    //DO SOMETHING ON THE DATA IN byte[] data!! Yihaa!!
                    Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else
                    receiveAttempt = 0; //reset receive attempt
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //fail but not exceeding max attempt, repeats
                    ++receiveAttempt; //increase receive attempt;
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                } else { //completely fails!
                    Console.WriteLine("receiveCallback fails!"); //don't repeat beginReceive
                    receiveAttempt = 0; //reset this for the next connection
                }
            }
        } catch (Exception e) { // this exception will happen when "this" is be disposed...
            Console.WriteLine("receiveCallback fails with exception! " + e.ToString());
        }
    }
    

  7. And suppose you want to reply your sender after you receive the message, simply do this in the if (received > 0) part:

    if (received > 0) {
        byte[] data = new byte[received]; //the data is in the byte[] format, not string!
        //DO SOMETHING ON THE DATA int byte[]!! Yihaa!!
        Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else                     
    
        //Message retrieval part
        //Suppose you only want to declare that you receive data from a client to that client
        string msg = "I receive your message on: " + DateTime.Now;
        socket.Send(Encoding.ASCII.GetBytes(msg)); //Note that you actually send data in byte[]
        Console.WriteLine("I sent this message to the client: " + msg);
    
        receiveAttempt = 0; //reset receive attempt
        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
    }
    

  8. And after putting a little more things in your main routine, you are done(!) - IF you do not ask for sending to client as byte[]

  9. And now, if you want to send something to all your clients as byte[] you simply need to list all your client (see step 4-5). See this and convert the result string above (remember to type it in hex string format as required) to byte[] then send it to all the clients using your client socket list (here is where it is needed!):

    static void Main(string[] args) {
        //---listen at the specified IP and port no.---
        Console.WriteLine("Listening...");
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
        serverSocket.Listen(4); //the maximum pending client, define as you wish
        serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);      
    
        //normally, there isn't anything else needed here
        string result = "";
        do {
            result = Console.ReadLine();
            if (result.ToLower().Trim() != "exit") {
                byte[] bytes = null;
                //you can use `result` and change it to `bytes` by any mechanism which you want
                //the mechanism which suits you is probably the hex string to byte[]
                //this is the reason why you may want to list the client sockets
                foreach(Socket socket in clientSockets)
                    socket.Send(bytes); //send everything to all clients as bytes
            }
        } while (result.ToLower().Trim() != "exit");
    }
    

And here, you are more or less done with your server. Next is your client


Client:

  1. Similarly, put the Socket class in the class context rather than method context and initialize it as soon as you start your program

    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static Socket clientSocket; //put here
    static void Main(string[] args) {
        //Similarly, start defining your client socket as soon as you start. 
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
        //your other main routines
    }
    

  2. Then start to connect by ASync BeginConnect. I would normally go further by LoopConnect just for failure handling like this.

    static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
        int attempts = 0;
        while (!clientSocket.Connected && attempts < noOfRetry) {
            try {
                ++attempts;
                IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnect, null);
                result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
                System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
            } catch (Exception e) {
                Console.WriteLine("Error: " + e.ToString());
            }
        }
        if (!clientSocket.Connected) {
            Console.WriteLine("Connection attempt is unsuccessful!");
            return;
        }
    }
    

  3. Similar concept to what you do to the server BeginAccept you need to define endConnectCallback for the ASync BeginConnect you use. But here, unlike server which needs to re-calling BeginAccept, once you are connected, you do not need to do any new BeginConnect since you only need to be connected once.

  4. You may want to declare buffer etc. Then, after you connect, don't forget the next ASync BeginReceive to handle the message retrieval part (similar with the server)

    private const int BUFFER_SIZE = 4096;
    private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
    private static void endConnectCallback(IAsyncResult ar) {
        try {
            clientSocket.EndConnect(ar);
            if (clientSocket.Connected) {
                clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
            } else {
                Console.WriteLine("End of connection attempt, fail to connect...");
            }
        } catch (Exception e) {
            Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
        }
    }
    

  5. Naturally, you need to define your receiveCallback, just like what you did for the server. And yes, it is as you have guessed, it is almost identical to what you did for the server!

  6. You can do anything you want with your data. Note that the data you receive is actually in byte[], not string. So you can do anything with it. But for example's sake, I will just use string to display.

    const int MAX_RECEIVE_ATTEMPT = 10;
    static int receiveAttempt = 0;
    private static void receiveCallback(IAsyncResult result) {
        System.Net.Sockets.Socket socket = null;
        try {
            socket = (System.Net.Sockets.Socket)result.AsyncState;
            if (socket.Connected) {
                int received = socket.EndReceive(result);
                if (received > 0) {
                    receiveAttempt = 0;
                    byte[] data = new byte[received];
                    Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //copy the data from your buffer
                    //DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
                    //Notice that your data is not string! It is actually byte[]
                    //For now I will just print it out
                    Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
                    ++receiveAttempt;
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                } else { //completely fails!
                    Console.WriteLine("receiveCallback is failed!");
                    receiveAttempt = 0;
                    clientSocket.Close();
                }
            }
        } catch (Exception e) { // this exception will happen when "this" is be disposed...
            Console.WriteLine("receiveCallback is failed! " + e.ToString());
        }
    }
    

  7. And at the very very last... Yes, again, as you have already guessed, you just need to do something on your main routine - suppose you want to use it to send data. Because you use Console but you want it to send things as byte[], you need to do the conversion (see the explanation in server 9.). And afterwards you are completely done!!

    static void Main(string[] args) {
        //Similarly, start defining your client socket as soon as you start. 
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        loopConnect(3, 3); //for failure handling
        string result = "";
        do {
            result = Console.ReadLine(); //you need to change this part
            if (result.ToLower().Trim() != "exit") {
                byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
                //do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
                clientSocket.Send(bytes);
            }
        } while (result.ToLower().Trim() != "exit");
    }
    


Results:

Here you go! I tested it by sending string for display, but I already put up what is needed when you want to change it to byte[]


Code for your test:

Server

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TcpListenerConsoleApplication {
    class Program {
        const int PORT_NO = 2201;
        const string SERVER_IP = "127.0.0.1";
        static Socket serverSocket;
        static void Main(string[] args) {
            //---listen at the specified IP and port no.---
            Console.WriteLine("Listening...");
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
            serverSocket.Listen(4); //the maximum pending client, define as you wish
            serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);      
            string result = "";
            do {
                result = Console.ReadLine();
            } while (result.ToLower().Trim() != "exit");
        }

        private const int BUFFER_SIZE = 4096;
        private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
        private static void acceptCallback(IAsyncResult result) { //if the buffer is old, then there might already be something there...
            Socket socket = null;
            try {
                socket = serverSocket.EndAccept(result); // The objectDisposedException will come here... thus, it is to be expected!
                //Do something as you see it needs on client acceptance
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
            } catch (Exception e) { // this exception will happen when "this" is be disposed...        
                //Do something here             
                Console.WriteLine(e.ToString());
            }
        }

        const int MAX_RECEIVE_ATTEMPT = 10;
        static int receiveAttempt = 0; //this is not fool proof, obviously, since actually you must have multiple of this for multiple clients, but for the sake of simplicity I put this
        private static void receiveCallback(IAsyncResult result) {
            Socket socket = null;
            try {
                socket = (Socket)result.AsyncState; //this is to get the sender
                if (socket.Connected) { //simple checking
                    int received = socket.EndReceive(result);
                    if (received > 0) {
                        byte[] data = new byte[received]; //the data is in the byte[] format, not string!
                        Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                        //DO SOMETHING ON THE DATA int byte[]!! Yihaa!!
                        Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else                     

                        //Message retrieval part
                        //Suppose you only want to declare that you receive data from a client to that client
                        string msg = "I receive your message on: " + DateTime.Now;                      
                        socket.Send(Encoding.ASCII.GetBytes(msg)); //Note that you actually send data in byte[]
                        Console.WriteLine("I sent this message to the client: " + msg);

                        receiveAttempt = 0; //reset receive attempt
                        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                    } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //fail but not exceeding max attempt, repeats
                        ++receiveAttempt; //increase receive attempt;
                        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                    } else { //completely fails!
                        Console.WriteLine("receiveCallback fails!"); //don't repeat beginReceive
                        receiveAttempt = 0; //reset this for the next connection
                    }
                }
            } catch (Exception e) { // this exception will happen when "this" is be disposed...
                Console.WriteLine("receiveCallback fails with exception! " + e.ToString());
            }
        }

    }
}

Client

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TcpClientConsoleApplication {
    class Program {
        const int PORT_NO = 2201;
        const string SERVER_IP = "127.0.0.1";
        static Socket clientSocket; //put here
        static void Main(string[] args) {
            //Similarly, start defining your client socket as soon as you start. 
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            loopConnect(3, 3); //for failure handling
            string result = "";
            do {
                result = Console.ReadLine(); //you need to change this part
                if (result.ToLower().Trim() != "exit") {
                    byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
                    //do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
                    clientSocket.Send(bytes);
                }
            } while (result.ToLower().Trim() != "exit");
        }

        static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
            int attempts = 0;
            while (!clientSocket.Connected && attempts < noOfRetry) {
                try {
                    ++attempts;
                    IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnectCallback, null);
                    result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
                    System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
                } catch (Exception e) {
                    Console.WriteLine("Error: " + e.ToString());
                }
            }
            if (!clientSocket.Connected) {
                Console.WriteLine("Connection attempt is unsuccessful!");
                return;
            }
        }

        private const int BUFFER_SIZE = 4096;
        private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
        private static void endConnectCallback(IAsyncResult ar) {
            try {
                clientSocket.EndConnect(ar);
                if (clientSocket.Connected) {
                    clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
                } else {
                    Console.WriteLine("End of connection attempt, fail to connect...");
                }
            } catch (Exception e) {
                Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
            }
        }

        const int MAX_RECEIVE_ATTEMPT = 10;
        static int receiveAttempt = 0;
        private static void receiveCallback(IAsyncResult result) {
            System.Net.Sockets.Socket socket = null;
            try {
                socket = (System.Net.Sockets.Socket)result.AsyncState;
                if (socket.Connected) {
                    int received = socket.EndReceive(result);
                    if (received > 0) {
                        receiveAttempt = 0;
                        byte[] data = new byte[received];
                        Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                        //DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
                        //Notice that your data is not string! It is actually byte[]
                        //For now I will just print it out
                        Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
                        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                    } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
                        ++receiveAttempt;
                        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                    } else { //completely fails!
                        Console.WriteLine("receiveCallback is failed!");
                        receiveAttempt = 0;
                        clientSocket.Close();
                    }
                }
            } catch (Exception e) { // this exception will happen when "this" is be disposed...
                Console.WriteLine("receiveCallback is failed! " + e.ToString());
            }
        }
    }
}


Last Remarks (Edit)

Since the code above is run using Console Application it must be run with static main void keyword. And thus client Socket defined above is of static type. This may prevent the client Socket to be defined multiple times as each time it is "defined", since it is of the same class named Program, it will refer to the same Socket (though this may not always the case, at least according to the OP's experiment: he can run multiple clients successfully in the same computer).

Nevertheless, to overcome this is not that hard. Simply port the client application to the platform which is non-initiated as static class (such as WinForms) and all the above code would still run as per normal. Alternatively, if it must be run using the Console Applications, and the problem occurs, simply copy the client application and re-define it using different namespace or different class name to avoid defining identical Socket due to identical namespace or class.

But the most important part on this problem solving is the use of Async and Sync wisely to solve the given issue.


Continuation of this topic can be found here

这篇关于使用套接字将值从服务器发送到客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆