Saturday, February 20, 2010

[C#]利用Socket送傳class or struct序列化資料

一般利用Sockert都傳送文字字串.

那如果要傳送class or struct的資料怎麼辦呢.

重點就是先將class or struct序列化.

直接看範例吧.

共用Class or Struct

person.cs

01 using System;
02 using System.Collections.Generic;
03 using System.Text;
04  
05 namespace ClassLibrary2
06 {
07     [Serializable]
08     public class class_person
09     {
10         public string id;
11         public string name;
12     }
13  
14     [Serializable]
15     public struct struct_person
16     {
17         public string id;
18         public string name;
19     }
20 }

Client端,要using共用的dll

Form1.cs

01 using System;
02 using System.Collections.Generic;
03 using System.ComponentModel;
04 using System.Data;
05 using System.Drawing;
06 using System.Text;
07 using System.Windows.Forms;
08 using System.Net.Sockets;
09 using System.Net;
10 using System.Threading;
11 using ClassLibrary2;
12 using System.Runtime.Serialization.Formatters.Binary;
13 using System.IO;
14  
15 namespace WindowsApplication5
16 {
17     public partial class Form1 : Form
18     {
19         public Form1()
20         {
21             InitializeComponent();
22         }
23  
24         private void btnSendClassData_Click(object sender, EventArgs e)
25         {
26             try
27             {
28                 IPEndPoint hostEP = new IPEndPoint(IPAddress.Parse(this.tbIp.Text), int.Parse(this.tbPort.Text));
29                 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
30                 socket.Connect(hostEP);
31                 byte[] bytesSend = new byte[1024];
32  
33                 //序列化
34                 BinaryFormatter bf = new BinaryFormatter();
35                 MemoryStream stream = new MemoryStream();
36                 class_person person = new class_person();
37                 person.id = this.tbId.Text;
38                 person.name = this.tbName.Text;
39                 bf.Serialize(stream, person);
40  
41                 bytesSend = stream.ToArray();
42                 socket.SendTo(bytesSend, 0, hostEP);
43                 socket.Shutdown(SocketShutdown.Both);
44                 socket.Close();
45             }
46             catch (Exception ex)
47             {
48                 MessageBox.Show(ex.ToString());
49             }
50         }
51     }
52 }


Server端,要using共用的dll

Form1.cs

001 using System;
002 using System.Collections.Generic;
003 using System.ComponentModel;
004 using System.Data;
005 using System.Drawing;
006 using System.Text;
007 using System.Windows.Forms;
008 using System.Net.Sockets;
009 using System.Net;
010 using System.Threading;
011 using ClassLibrary2;
012 using System.Runtime.Serialization.Formatters.Binary;
013 using System.IO;
014  
015 namespace WindowsApplication4
016 {
017     public partial class Form1 : Form
018     {
019         delegate void SetTextCallback(byte[] data);
020         delegate void GetTextCallback();
021  
022         Thread startThread;
023         Socket socket;
024  
025         public Form1()
026         {
027             InitializeComponent();
028         }
029  
030         private void btnOpenListen_Click(object sender, EventArgs e)
031         {
032             startThread = new Thread(new ParameterizedThreadStart(start));
033             startThread.Start();
034             this.btnOpenListen.Enabled = false;
035             this.btnCloseListen.Enabled = true;
036         }
037  
038         private void btnCloseListen_Click(object sender, EventArgs e)
039         {
040             socket.Close();
041             startThread.Abort();
042             this.btnOpenListen.Enabled = true;
043             this.btnCloseListen.Enabled = false;
044         }
045  
046         private void start(object TransMsg)
047         {
048             IPEndPoint hostEP = new IPEndPoint(IPAddress.Parse(this.tbIp.Text), int.Parse(this.tbPort.Text));
049             socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
050             socket.Bind(hostEP);
051  
052             while (true)
053             {
054                 try
055                 {
056                     socket.Listen(50);
057                     Socket NewSocket = socket.Accept();
058                     byte[] bytesReceive = new byte[1024];
059                     NewSocket.Receive(bytesReceive);
060                     SetText(bytesReceive);
061                     NewSocket.Shutdown(SocketShutdown.Both);
062                     NewSocket.Close();
063                 }
064                 catch (Exception ex)
065                 {
066                     MessageBox.Show(ex.ToString());
067                 }
068             }
069         }
070  
071         private void SetText(byte[] data)
072         {
073             //反序列化
074             BinaryFormatter bf = new BinaryFormatter();
075             MemoryStream stream = new MemoryStream(data);
076             class_person person = (class_person)bf.Deserialize(stream);
077  
078             if (this.tbId.InvokeRequired)
079             {
080                 SetTextCallback d = new SetTextCallback(SetText);
081                 this.Invoke(d, new object[] { data });
082             }
083             else
084             {
085                 this.tbId.Text = person.id;
086             }
087  
088             if (this.tbName.InvokeRequired)
089             {
090                 SetTextCallback d = new SetTextCallback(SetText);
091                 this.Invoke(d, new object[] { data });
092             }
093             else
094             {
095                 this.tbName.Text = person.name;
096             }
097  
098         }
099     }
100 }


執行結果:

參考網址:

http://www.blueshop.com.tw/board/show.asp?subcde=BRD20090402141557OY6&fumcde=FUM20050124192253INM
http://topic.csdn.net/t/20031128/10/2504360.html
http://hi.baidu.com/ysdonet/blog/item/2915d2f4ebb2b56bddc47418.html

C# Tutorial - Simple Threaded TCP Server

In this tutorial I'm going to show you how to build a threaded tcp server with C#. If you've ever worked with Window's sockets, you know how difficult this can sometimes be. However, thanks to the .NET framework, making one is a lot easier than it used to be.

What we'll be building today is a very simple server that accepts client connections and can send and receive data. The server spawns a thread for each client and can, in theory, accept as many connections as you want (although in practice this is limited because you can only spawn so many threads before Windows will get upset).

Let's just jump into some code. Below is the basic setup for our TCP server class.

using System;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace TCPServerTutorial
{
  class Server
  {
    private TcpListener tcpListener;
    private Thread listenThread;

    public Server()
    {
      this.tcpListener = new TcpListener(IPAddress.Any, 3000);
      this.listenThread = new Thread(new ThreadStart(ListenForClients));
      this.listenThread.Start();
    }
  }
}

So here's a basic server class - without the guts. We've got a TcpListener which does a good job of wrapping up the underlying socket communication, and a Thread which will be listening for client connections. You might have noticed the function ListenForClients that is used for our ThreadStart delegate. Let's see what that looks like.

private void ListenForClients()
{
  this.tcpListener.Start();

  while (true)
  {
    //blocks until a client has connected to the server
    TcpClient client = this.tcpListener.AcceptTcpClient();

    //create a thread to handle communication 
    //with connected client
    Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
    clientThread.Start(client);
  }
}

This function is pretty simple. First it starts our TcpListener and then sits in a loop accepting connections. The call toAcceptTcpClient will block until a client has connected, at which point we fire off a thread to handle communication with our new client. I used a ParameterizedThreadStart delegate so I could pass the TcpClientobject returned by the AcceptTcpClient call to our new thread.

The function I used for the ParameterizedThreadStart is called HandleClientComm. This function is responsible for reading data from the client. Let's have a look at it.

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
    {
      //blocks until a client sends a message
      bytesRead = clientStream.Read(message, 0, 4096);
    }
    catch
    {
      //a socket error has occured
      break;
    }

    if (bytesRead == 0)
    {
      //the client has disconnected from the server
      break;
    }

    //message has successfully been received
    ASCIIEncoding encoder = new ASCIIEncoding();
    System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
  }

  tcpClient.Close();
}

The first thing we need to do is cast client as a TcpClient object since the ParameterizedThreadStart delegate can only accept object types. Next, we get the NetworkStream from the TcpClient, which we'll be using to do our reading. After that we simply sit in a while true loop reading information from the client. The Read call will block indefinitely until a message from the client has been received. If you read zero bytes from the client, you know the client has disconnected. Otherwise, a message has been successfully received from the server. In my example code, I simply convert the byte array to a string and push it to the debug console. You will, of course, do something more interesting with the data - I hope. If the socket has an error or the client disconnects, you should call Close on the TcpClient object to free up any resources it was using.

Believe it or not, that's pretty much all you need to do to create a threaded server that accepts connections and reads data from clients. However, a server isn't very useful if it can't send data back, so let's look at how to send data to one of our connected clients.

NetworkStream clientStream = tcpClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("Hello Client!");

clientStream.Write(buffer, 0 , buffer.Length);
clientStream.Flush();

Do you remember the TcpClient object that was returned from the call AcceptTcpClient? Well, that's the object we'll be using to send data back to that client. That being said, you'll probably want to keep those objects around somewhere in your server. I usually keep a collection of TcpClient objects that I can use later. Sending data to connected clients is very simple. All you have to do is call Write on the the client's NetworkStream object and pass it the byte array you'd like to send.

Your TCP server is now finished. The hard part is defining a good protocol to use for sending information between the client and server. Application level protocols are generally unique for application, so I'm not going to go into any details - you'll just have to invent you're own.

But what use is a server without a client to connect to it? This tutorial is mainly about the server, but here's a quick piece of code that shows you how to set up a basic TCP connection and send it a piece of data.

TcpClient client = new TcpClient();

IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000);

client.Connect(serverEndPoint);

NetworkStream clientStream = client.GetStream();

ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("Hello Server!");

clientStream.Write(buffer, 0 , buffer.Length);
clientStream.Flush();

The first thing we need to do is get the client connected to the server. We use the TcpClient.Connect method to do this. It needs the IPEndPoint of our server to make the connection - in this case I connect it to localhost on port 3000. I then simply send the server the string "Hello Server!".

One very important thing to remember is that one write from the client or server does not always equal one read on the receiving end. For instance, your client could send 10 bytes to the server, but the server may not get all 10 bytes the first time it reads. Using TCP, you're pretty much guaranteed to eventually get all 10 bytes, but it might take more than one read. You should keep that in mind when designing your protocol.

That's it! Now get out there and clog the tubes with your fancy new C# TCP servers. As always, comments and questions are welcome.

WSAStartup Function

The WSAStartup function initiates use of the Winsock DLL by a process.

Syntax

C++
int WSAStartup(   __in   WORD wVersionRequested,   __out  LPWSADATA lpWSAData ); 

Parameters

wVersionRequested [in]

The highest version of Windows Sockets specification that the caller can use. The high-order byte specifies the minor version number; the low-order byte specifies the major version number.

lpWSAData [out]

A pointer to the WSADATA data structure that is to receive details of the Windows Sockets implementation.

Return Value

If successful, the WSAStartup function returns zero. Otherwise, it returns one of the error codes listed below.

The WSAStartup function directly returns the extended error code in the return value for this function. A call to theWSAGetLastError function is not needed and should not be used.

 

Error code Meaning
WSASYSNOTREADY

The underlying network subsystem is not ready for network communication.

WSAVERNOTSUPPORTED

The version of Windows Sockets support requested is not provided by this particular Windows Sockets implementation.

WSAEINPROGRESS

A blocking Windows Sockets 1.1 operation is in progress.

WSAEPROCLIM

A limit on the number of tasks supported by the Windows Sockets implementation has been reached.

WSAEFAULT

The lpWSAData parameter is not a valid pointer.