C#和Python的Socket通信

目录

 

1.项目说明

2.Socket通信

2.1C#实现socket通信

2.2python实现socket通信

3.结果

3.1C#socket通信结果

3.2python和C# socket通信结果


1.项目说明

项目使用unity3D搭建环境,并通过python程序控制unity环境。在控制过程中,需要使用socket通信将python处理后的数据发送到unity环境中。由于第一次接触socket通信,因此,首先使用C#搭建了客户端和服务器端进行实验,然后使用python搭建服务器,C#搭建客户端进行实验,最后在unity上进行测试。由于unity和python程序都运行在同一台电脑上,我们使用IP地址为127.0.0.1。整个通信过程是,建立连接后,客户端向服务器端发送字符串“location”,服务器端接收到字符串后,将一个坐标发送到客户端。

2.Socket通信

Socket封装了TCP/IP协议,可以视为TCP/IP协议向程序员开发所提供的接口,从而简化了网络编程。Socket在网络通信中的位置如图所示。

C#和Python的Socket通信_第1张图片

2.1C#实现socket通信

我们使用的是异步socket通信,主要流程为建立socket连接,收发数据,关闭连接。

(1)引入命名空间

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

(2)建立socket对象和事件状态

由于异步通信,当socket进行通信时,主线程将会执行后面的内容。但后面的程序可能需要在 socket通信之后才能进行,因此需要事件状态来阻塞主线程的运行。

public Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        public static ManualResetEvent allDone = new ManualResetEvent(false);
        private static ManualResetEvent receiveDone =
        new ManualResetEvent(false);
        private static ManualResetEvent connectDone = new ManualResetEvent(false);
        private static ManualResetEvent sendDone =
        new ManualResetEvent(false);
        private static String response = String.Empty;
        static void Main(string[] args)

(3)建立socket连接

biginconnect对远程主机发送建立连接的异步请求,这里异步方法除了一般的参数外,还有两个参数,一个是回调函数,另一个是状态对象,状态对象向回调函数提供异步通信的状态,对应于回调函数中的参数IAsyncResult。IAsyncResult是接口,里面有几个属性用来查看可以查看异步操作的状态:AsyncState 获取用户定义的对象,它限定或包含关于异步操作的信息。回调函数主要对后续任务和通信的状态进行处理。回调函数中,应调用 EndConnect 方法。 当应用程序调用 BeginConnect 时,系统将使用单独的线程执行指定的回调方法,并在 EndConnect 上一直阻止到 Socket成功连接或引发异常为止。 此外,我们不希望在建立socket连接时,主线程执行其他任务,因此主线程中使用waitone进行阻塞。当需要原始线程继续执行时,在回调方法中调用ManualResetEvent 的 Set 方法。

string address = "127.0.0.1";
            IPAddress hostIP = IPAddress.Parse(address);
            int port = 50088;
 clientSocket.BeginConnect(hostIP, port, new AsyncCallback(connectCallback), clientSocket);
                connectDone.WaitOne();

private void connectCallback(IAsyncResult asyncConnect)
        {
            
            try
            {
                Socket client = (Socket)asyncConnect.AsyncState;
                client.EndConnect(asyncConnect);
                Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString());
                // Signal that the connection has been made.     
                connectDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
           
        }

(4)发送数据

在成功建立连接后,客户端向服务器发送字符串“location”,首先将字符串进行ASCII编码,并调用异步方法beginsend发送数据,该异步方法同样有回调函数,对发送状态进行处理,解除主线程的阻塞。

Send(clientSocket, content);
                sendDone.WaitOne();
 private static void Send(Socket handler, String data)
        {
            // Convert the string data to byte data using ASCII encoding.     
            byte[] byteData = Encoding.ASCII.GetBytes(data);
        
            // Begin sending the data to the remote device.     
            handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);
        }
        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.     
                Socket handler = (Socket)ar.AsyncState;
                // Complete sending the data to the remote device.     
                int bytesSent = handler.EndSend(ar);
                Console.WriteLine("Sent {0} bytes to server.", bytesSent);
                sendDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

(5)接收数据

服务器接收到“location”后,将坐标(12.3,13.6)发送给客户端。因此客户端开始接收数据。首先创建stateobject对象存放接收的数据对象,然后调用异步方法beginreceive。在回调函数中,由于传送的数据量可能大于buffer容量,因此需要多次读取,才能接收到全部的数据,因此使用if (state.sb.Length > 1)进行判断,如果满足条件,则再次调用beginrreceive方法。

 Receive(clientSocket);
                receiveDone.WaitOne();
private static void Receive(Socket client)
        {
            try
            {
                // Create the state object.     
                StateObject state = new StateObject();
                state.workSocket = client;
                // Begin receiving the data from the remote device.     
                Console.WriteLine("async receive location");
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static void ReceiveCallback(IAsyncResult ar)
        {

            try
            {
                // Retrieve the state object and the client socket     
                // from the asynchronous state object.     
                
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;
                // Read data from the remote device.     
                int bytesRead = client.EndReceive(ar);
                
                if (bytesRead > 0)
                {
                    // There might be more data, so store the data received so far.     

                    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
                    // Get the rest of the data.     
                    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                }
                else
                {
                    // All the data has arrived; put it in response.     
                    if (state.sb.Length > 1)
                    {
                        response = state.sb.ToString();
                        Console.WriteLine(response.ToString());
                    }
                    // Signal that all bytes have been received.     
                    receiveDone.Set();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

(6) 关闭套接字连接

项目中仅需要进行坐标的传递,在接收到坐标后即可关闭套接字连接。clientSocket.Shutdown通知服务器端或客户端停止接收和发送数据,参数SocketShutdown.Both意味着客服端和服务器端都停止。

clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();

(7)服务器端

以上操作都是针对客户端的,而服务器端收发数据的操作也大致相同,服务器端和客户端的套接字对应相同的IP和端口,其余主要的区别是套接字建立连接时的操作不同。服务器端需要将套接字和IP端口绑定。BeginAccept开始一个异步操作来接受一个传入的连接尝试。回调函数中,套接字连接成功后,从客户端接收数据,接收到“location”时,再将坐标发送过。

IPAddress local = IPAddress.Parse("127.0.0.1");
                IPEndPoint iep = new IPEndPoint(local, 50088);
                server.Bind(iep);
                server.Listen(100);
                while (true)
                {
                    allDone.Reset();
                    Console.WriteLine("Waiting for a connection...");
                    server.BeginAccept(new AsyncCallback(Acceptcallback), server);
                    allDone.WaitOne();
                   
                }
public static void Acceptcallback(IAsyncResult iar)
        {
            // Signal the main thread to continue.     
            
            //还原传入的原始套接字
            Socket listener = (Socket)iar.AsyncState;
            //在原始套接字上调用EndAccept方法,返回新的套接字
            Socket handler = listener.EndAccept(iar);

            // Create the state object.     
            StateObject state = new StateObject();
            state.workSocket = handler;
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
            receiveDone.WaitOne();
            allDone.Set();

            // Create the state object.     
            //StateObject state = new StateObject();
            //state.workSocket = handler;
            //handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
        }

(8)C#代码链接

https://github.com/wzyzyw/Socket

2.2python实现socket通信

python主要介绍关于服务器端的创建。

(1)创建socket对象并绑定IP端口

s=socket.socket()
s.bind(('127.0.0.1',50088))
s.listen()

(2)接收客户端的请求连接

accept()接受一个客户端的连接请求,并返回一个新的套接字,与客户端通信是通过这个新的套接字上发送和接收数据来完成的。

c, addr = s.accept()

 

(3)从套接字读取数据并发送数据

c.recv(1024).decode('ascii')

c.send(str_content.encode('ascii'))

(4)完整python代码

import socket

s=socket.socket()
s.bind(('127.0.0.1',50088))
s.listen()
sync_robot_loc=[12.3,13.6]
def def_socket_thread():
    # global loop
    # loop = asyncio.get_event_loop()

    try:
        while True:
            c, addr = s.accept()
            content = read_from_client(c)
            if content.find('location') > -1:
                global sync_robot_loc
                print('receive request')
                print('sycn position=', sync_robot_loc)
                x = sync_robot_loc[0]
                y = sync_robot_loc[1]
                str_content = 'x=' + str(x) + ',y=' + str(y)
                c.send(str_content.encode('ascii'))
                print('finish location send')
            else:
                print('no request')
    except IOError as e:
        print(e.strerror)
    print('start socket thread!!!')


def read_from_client(c):
    try:
        return c.recv(1024).decode('ascii')
    except IOError as e:
        # 如果异常的话可能就是会话中断 那么直接删除
        print(e.strerror)

if __name__=='__main__':
    def_socket_thread()

3.结果

3.1C#socket通信结果

consoleapp1是客户端显示的结果,consoleapp2是服务器端显示的结果,意味着成功实现了socket通信。

C#和Python的Socket通信_第2张图片

3.2python和C# socket通信结果

下图是python服务器端的结果

下图是C#客户端的结果

C#和Python的Socket通信_第3张图片

意味着成功实现了socket通信。

 

你可能感兴趣的:(Python,C#,web,c#,python,socket)