飞道的博客

Socket实现TCP通信(C#实现)

595人阅读  评论(0)

Server和Client是一对好朋友,有一天Client想知道知道现在几点了就给Server打电话(emmmm就是想他了的意思)

Client把他的手机卡clientfd插到手机卡槽里,拨出了Server的手机号(ip+port):


  
  1. //连接
  2. Socket clientfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  3. clientfd .Connect( "192.168.196.157", 2000);

但是Server积极拒绝了Client,可能Server没有开机。

Server终于开机了,他在等电话,除此之外什么都不想做(Accpet()是堵塞方法,接收客户端的连接,返回一个连接上的Socket对象,才继续往下执行):


  
  1. //监听套接字
  2. Socket listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  3. //绑定ip和port
  4. IPAddress ipadr = IPAddress.Parse( "192.168.196.157");
  5. IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
  6. listenfd.Bind(ipep);
  7. //监听,0表示不限制连接个数
  8. listenfd.Listen( 0);
  9. //等待连接
  10. Socket clientfd = listenfd.Accept();

开心的是,Client又试着给Server打电话,这次接通了。然后Client问现在几点了:


  
  1. //发送数据
  2. sendstr = "现在几点了" + '\r';
  3. byte[] sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
  4. clientfd.Send(sendbuff);

Server听完了Client的问题后,看了看时间告诉了Client:


  
  1. //接收数据
  2. int count = clientfd.Receive(readbuff);
  3. readstr = System.Text.Encoding.Default.GetString(readbuff, 0, count);
  4. Console.WriteLine( "Client::"+readstr);
  5. //反馈
  6. sendstr = "Server:现在是 "+System.DateTime.Now.ToShortTimeString();
  7. Console.WriteLine(sendstr);
  8. sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
  9. clientfd.Send(sendbuff);

。。。。。正经分割。。。。。。。。

Server端:


  
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace SyncServer
  9. {
  10. class Program
  11. {
  12. static Socket listenfd;
  13. static Socket toclientfd;
  14. static byte[] readbuff = new byte[ 1024];
  15. static byte[] sendbuff = new byte[ 1024];
  16. static int count;
  17. static String readstr = "";
  18. static String sendstr = "";
  19. static void Main(string[] args)
  20. {
  21. //套接字
  22. listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  23. //绑定
  24. IPAddress ipadr = IPAddress.Parse( "192.168.196.157");
  25. IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
  26. listenfd.Bind(ipep);
  27. //监听
  28. listenfd.Listen( 0);
  29. Console.WriteLine( "Server已开机(服务器启动成功)");
  30. toclientfd = listenfd.Accept();
  31. Console.WriteLine( "接了Client的电话(服务器同意连接)");
  32. while ( true)
  33. {
  34. count = toclientfd.Receive(readbuff);
  35. readstr = System.Text.Encoding.Default.GetString(readbuff, 0, count);
  36. Console.WriteLine(readstr);
  37. sendstr = "Server:现在是 "+System.DateTime.Now.ToShortTimeString();
  38. Console.WriteLine(sendstr);
  39. sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
  40. toclientfd.Send(sendbuff);
  41. }
  42. }
  43. }
  44. }

Client端:


  
  1. using System;
  2. using System.Net.Sockets;
  3. using System.Windows.Forms;
  4. namespace SyncClient
  5. {
  6. public partial class Form1 : Form
  7. {
  8. public Form1()
  9. {
  10. InitializeComponent();
  11. }
  12. string sendstr = "";
  13. string readstr = "";
  14. Socket socket;
  15. byte[] readBuff = new byte[ 1024];
  16. private void Conn_btn_Click(object sender, EventArgs e)
  17. {
  18. socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  19. socket.Connect( "192.168.196.157", 2000);
  20. Dialog.Items.Add( "Server接电话啦(服务器已连接)");
  21. }
  22. private void Send_btn_Click(object sender, EventArgs e)
  23. {
  24. sendstr = "Client:" + textBox1.Text + '\r';
  25. byte[] sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
  26. socket.Send(sendbuff);
  27. Dialog.Items.Add( sendstr);
  28. int count = socket.Receive(readBuff);
  29. readstr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
  30. Dialog.Items.Add(readstr);
  31. }
  32. }
  33. }

。。。。。正经分割。。。。。。。。

但是,Client发现有时候Server不接电话,自己只能一直打一直打(Connect()是阻塞方法),有时候自己说错了,赶快重新说一次,但是Server愣住了没有反应(Receive()是阻塞方法),有时候别人跟Server聊着天,自己就只能等他们聊完,Server也不喜欢一直守着电话(Accept()是阻塞方法)、等别人说话回复别人卡死(Send()是阻塞方法)的感觉,于是他们换了一种打电话的方式。

Server开好机后,做些别的时间,没事看两眼有没有电话(BeginAccept(....)非阻塞方法,异步):


  
  1. //套接字
  2. Socket listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  3. //绑定
  4. IPAddress ipadr = IPAddress.Parse( "192.168.196.157");
  5. IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
  6. listenfd.Bind(ipep);
  7. //监听
  8. listenfd.Listen( 0);
  9. listenfd.BeginAccept(AcceptCallback, listenfd);
  10. public void AcceptCallback(IAsyncResult ar)
  11. {
  12. try
  13. {
  14. Socket listenfd = (Socket)ar.AsyncState;
  15. clientfd= listenfd.EndAccept(ar);
  16. } catch (Exception e) {
  17. Console.WriteLine(e.ToString());
  18. }
  19. }

Client也是,拨出(BeginConnect(...)非阻塞方法,异步)号码后把手边的东西清一清:


  
  1. private void Conn_btn_Click(object sender, EventArgs e)
  2. {
  3. clientfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  4. //连接同一局域网
  5. clientfd.BeginConnect( "192.168.196.157", 2000,ConnectCallback, clientfd);
  6. }
  7. public void ConnectCallback(IAsyncResult ar)
  8. {
  9. try
  10. {
  11. Socket socket = (Socket)ar.AsyncState;
  12. socket.EndConnect(ar);
  13. } catch(Exception e)
  14. {
  15. Console.WriteLine( "Socket Connect fail:" + e.ToString());
  16. }
  17. }

Server接了电话后,他们开始聊天了,不用等着对方说话,有声音的话就去听,没声音的时候可以做点别的。


  
  1. clientfd.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, clientfd);
  2. clientfd.BeginSend(sendBuff)
  3. public void ReceiveCallback(IAsyncResult ar)
  4. {
  5. try
  6. {
  7. Socket socket = (Socket)ar.AsyncState;
  8. int count = socket.EndReceive(ar);
  9. string readstr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
  10. socket.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, socket);
  11. } catch (Exception e)
  12. {
  13. Console.WriteLine( "Socket Reveive fail: " + e.ToString());
  14. }
  15. }
  16. public void SendCallback(IAsyncResult ar)
  17. {
  18. try
  19. {
  20. Socket socket = (Socket)ar.AsyncState;
  21. socket.EndSend(ar);
  22. } catch(Exception e)
  23. {
  24. Console.WriteLine(e.ToString());
  25. }
  26. }

 

。。。。。正经分割。。。。。。。。

Server端:


  
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using System.Windows.Forms;
  12. namespace EchoServer
  13. {
  14. public partial class ServerWin : Form
  15. {
  16. public ServerWin()
  17. {
  18. InitializeComponent();
  19. }
  20. Socket listenfd;
  21. Socket clientfd;
  22. byte[] readBuff = new byte[ 1024];
  23. byte[] sendbuff = new byte[ 1024];
  24. private void Listen_btn_Click(object sender, EventArgs e)
  25. {
  26. //套接字
  27. listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  28. //绑定
  29. IPAddress ipadr = IPAddress.Parse( "192.168.196.157");
  30. IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
  31. listenfd.Bind(ipep);
  32. //监听
  33. listenfd.Listen( 0);
  34. Dialog.Items.Add( "Server已开机(服务器启动成功)");
  35. listenfd.BeginAccept(AcceptCallback, listenfd);
  36. }
  37. private void Send_btn_Click(object sender, EventArgs e)
  38. {
  39. byte[] sendbuff = System.Text.Encoding.Default.GetBytes(textBox1.Text);
  40. clientfd.BeginSend(sendbuff, 0,sendbuff.Length, 0,SendCallback, clientfd);
  41. SetProgressDelegate setprogress = new SetProgressDelegate(SetProgress);
  42. this.Invoke(setprogress, new object[] { "Server:"+ textBox1.Text, "" });
  43. }
  44. public delegate void SetProgressDelegate(String str,String name);
  45. public void SetProgress(String str, String name)
  46. {
  47. Dialog.Items.Add(str);
  48. if(name!= null)
  49. Online.Items.Add(name);
  50. }
  51. public void AcceptCallback(IAsyncResult ar)
  52. {
  53. try
  54. {
  55. Socket listenfd = (Socket)ar.AsyncState;
  56. clientfd= listenfd.EndAccept(ar);
  57. // Dialog.Items.Add("Client上线啦");
  58. SetProgressDelegate setprogress = new SetProgressDelegate(SetProgress);
  59. this.Invoke(setprogress, new object[] { "Client上线啦", "Client" });
  60. sendbuff = System.Text.Encoding.Default.GetBytes( "接了Client的电话(服务器同意连接)");
  61. clientfd.Send(sendbuff);
  62. clientfd.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, clientfd);
  63. }
  64. catch (Exception e)
  65. {
  66. Console.WriteLine(e.ToString());
  67. }
  68. }
  69. public void ReceiveCallback(IAsyncResult ar)
  70. {
  71. try
  72. {
  73. Socket socket = (Socket)ar.AsyncState;
  74. int count = socket.EndReceive(ar);
  75. string readstr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
  76. //Dialog.Items.Add(name+":"+ readstr);
  77. SetProgressDelegate setprogress = new SetProgressDelegate(SetProgress);
  78. this.Invoke(setprogress, new object[] { "Client:" + readstr, "" });
  79. socket.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, socket);
  80. }
  81. catch (Exception e)
  82. {
  83. Console.WriteLine( "Socket Reveive fail: " + e.ToString());
  84. }
  85. }
  86. public void SendCallback(IAsyncResult ar)
  87. {
  88. try
  89. {
  90. Socket socket = (Socket)ar.AsyncState;
  91. socket.EndSend(ar);
  92. } catch(Exception e)
  93. {
  94. Console.WriteLine(e.ToString());
  95. }
  96. }
  97. }
  98. }

Client端:


  
  1. using System;
  2. using System.Net.Sockets;
  3. using System.Windows.Forms;
  4. namespace SyncClient
  5. {
  6. public partial class Form1 : Form
  7. {
  8. public Form1()
  9. {
  10. InitializeComponent();
  11. }
  12. string sendstr = "";
  13. string readstr = "";
  14. Socket socket;
  15. byte[] readBuff = new byte[ 1024];
  16. private void Conn_btn_Click(object sender, EventArgs e)
  17. {
  18. socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  19. socket.Connect( "192.168.196.157", 2000);
  20. Dialog.Items.Add( "Server接电话啦(服务器已连接)");
  21. }
  22. private void Send_btn_Click(object sender, EventArgs e)
  23. {
  24. sendstr = "Client:" + textBox1.Text + '\r';
  25. byte[] sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
  26. socket.Send(sendbuff);
  27. Dialog.Items.Add( sendstr);
  28. int count = socket.Receive(readBuff);
  29. readstr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
  30. Dialog.Items.Add(readstr);
  31. }
  32. }
  33. }

。。。。。正经分割。。。。。。。。

现在他们可以愉快的聊天啦。

 

。。。。。。。。。。。。。。。。。。。。。正经强分割。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

怎么连接的:

Server端的重点(同步的方法时):

1)创建服务端的套接字, 实例初始化 System.Net.Sockets.Socket 类使用指定的地址族、 套接字类型和协议。其中AddressFamily的InterNetwork指ipv4地址,SocketType.stream指定字节流,ProtocolType.Tcp指定使用TCP协议传输。

Socket listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

2)Bind绑定地址ip和端口port


  
  1. //绑定
  2. IPAddress ipadr = IPAddress.Parse( "192.168.196.157");
  3. IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
  4. listenfd.Bind(ipep);

3)Listen开启监听,socket.Listen(backlog),backlog限定队列中最多容纳等待接收的连接数,0表示不限制


  
  1. //监听
  2. listenfd.Listen( 0);

4)同意连接Accept(),属于阻塞方法,当没有客户端的连接申请时,服务端阻塞在这里,直到接收到客户的连接,返回一个客户端的socket对象。

Socket clientfd= listenfd.Accept();

5)可以看出,服务端有两种套接字,一个监听套接字,用来监听绑定的地址和端口上时候有客户端的申请连接,一个对应客户端的套接字,用于接收和发送指定对象数据。

Client端重点(同步的方法时):

1)创建客户端套接字,同服务端

Socket clientfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

2)申请连接,Connect(ip,port),属于阻塞方法,会一直卡住等待服务器接收,超时或拒绝

clientfd.Connect("192.168.196.157", 2000);

3)发送数据Send,接收数据Receive,都属于阻塞方法


  
  1. clientfd.Send(sendBuff)
  2. clientfd.Receive(readBuff)

同步->异步

同步方法 异步方法

Accept

BeginAccept

开始一个异步操作来接收连接请求

EndAccept

异步连接

Connect

BeginConnect

开始一个异步操作来申请连接

EndConnect

结束挂起的异步连接请求

Receive

BeginReceive

开始一个异步操作来接收数据

EndReceive

将数据异步发送到连接套接字

Send

BeginSend

开始一个异步操作来发送数据

EndSend

结束挂起的异步发送

 

异步的核心接口:IAsyncResult


  
  1. #region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
  2. // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
  3. #endregion
  4. using System.Runtime.InteropServices;
  5. using System.Threading;
  6. namespace System
  7. {
  8. //
  9. // 摘要:
  10. // 表示异步操作的状态。
  11. [ ComVisible(true)]
  12. public interface IAsyncResult
  13. {
  14. //
  15. // 摘要:
  16. // 获取一个值,该值指示异步操作是否已完成。
  17. //
  18. // 返回结果:
  19. // 如果操作已完成,则为 true;否则为 false。
  20. bool IsCompleted { get; }
  21. //
  22. // 摘要:
  23. // 获取用于等待异步操作完成的 System.Threading.WaitHandle。
  24. //
  25. // 返回结果:
  26. // 用于等待异步操作完成的 System.Threading.WaitHandle。
  27. WaitHandle AsyncWaitHandle { get; }
  28. //
  29. // 摘要:
  30. // 获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。
  31. //
  32. // 返回结果:
  33. // 一个用户定义的对象,限定或包含有关异步操作的信息。
  34. object AsyncState { get; }
  35. //
  36. // 摘要:
  37. // 获取一个值,该值指示异步操作是否同步完成。
  38. //
  39. // 返回结果:
  40. // 如果异步操作同步完成,则为 true;否则为 false。
  41. bool CompletedSynchronously { get; }
  42. }
  43. }

实现IAsyncResult的回调方法是多线程的方法,以达到异步的目的。IAsyncResult中的AsyncState对象就是当前线程中操作的客户/服务端套接字。

委托机制 : delegate

回调方法中不能进行UI操作,上述异步例程中使用委托的方式更新UI,将异步接收的数据显示在对话框中,具体不作描述。

----------------------------------------------------------------------------------------------------------------------------------------------------------------

写在最后:花花冲鸭!

 

 

 

 

 

 


转载:https://blog.csdn.net/weixin_38247439/article/details/88529909
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场