如果想开发一个基于TCP/IP协议的网络程序,应用程序之间则主要通过Socket交换数据
.NET Socket支持四种编程模式
1.居于阻塞模式的Socket编程
2.”非阻塞“模式的Socket编程
3.使用IAsyncResult的异步编程模式
4.使用EAP的异步编程模式
为什么要学Socket
开始写代码 :
Socket从信息的”发送“与”接收“角度,Socket分为两类:
Server端Socket:它在指定的端口上监听,等待客户端的连接请求,并且向客户端发送接收数据
Client端Socket:它尝试连接Server端的Socket,在连接成功之后,与Server端应用程序相互收发数据
首先开发服务端的程序:
准备:用于绑定的终结点IpEndPoint
//准备:用于绑定的终结点 IPAddress ipAddress = AddressHelper.GetLocalhostIPv4Address().First(); int LocalPort = AddressHelper.GetOneAvaiablePortInLocalhost(); IPEndPoint ipEnd = new IPEndPoint(ipAddress, LocalPort);
1.创建Socket对象
//1.创建Socket Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2.绑定终结点
//2. Socket绑定终结点 server.Bind(ipEnd);
3.监听
//3.监听 server.Listen(10); Console.WriteLine("主机 {0} 正在监听端口 {1} ,等待客户端连接……", ipAddress, ipEnd.Port);
4.响应连接请求:
//4. 如果有连接 响应连接请求 Socket client = server.Accept(); IPEndPoint clientIpEnd = client.RemoteEndPoint as IPEndPoint; Console.WriteLine("已接收客户端请求 客户端ip地址{0},端口号{1}", clientIpEnd.Address, clientIpEnd.Port); string welcome = "欢迎使用本网络服务,请输入字符串 输入exit退出"; //将欢迎信息发送给客户端 data = Encoding.UTF8.GetBytes(welcome); client.Send(data, data.Length, SocketFlags.None);
5.服务端Socket接收数据,处理数据,在客户端要求断开连接的时候,关闭socket
while (true) { //TODO 实现可以同时连接多个客户端的程序 实现两个客户端互相通信 //5.服务端Scoket开始接收客户端的数据,并将响应信息传回客户端 int recv = client.Receive(data); string message = Encoding.UTF8.GetString(data, 0, recv); if (message.Equals("exit")) { //关闭Socket Console.WriteLine("客户端{0}已断开连接",clientIpEnd.Address); client.Close(); break; } else { //将响应信息传给客户端 Console.WriteLine("客户端发来消息:{0}", message); string sendMsg = string.Format("服务器已经收到您的消息:{0}", message); client.Send(Encoding.UTF8.GetBytes(sendMsg)); } } }
2-5过程为了简化socket的操作,我用using(server){2-5的过程} 把2-5的代码给包起来了
using (server) { //2. Socket绑定终结点 //3.监听 //4. 如果有连接 响应连接请求 //5.服务端Scoket开始接收客户端的数据,并将响应信息传回客户端 }
服务端就是这样子了
下面是客户端Client
1.创建Socket对象
2.调用socket对象的Connect的方法创建连接
3.发送数据
整体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using MyNetworkLibrary; namespace MyClientApp { class Program { private const int BufferSize = 1024; static void Main(string[] args) { byte[] data = new byte[BufferSize]; IPEndPoint ipEnd = AddressHelper.GetRemoteMachineIPEndPoint(); //1.创建Socket对象 Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2.调用Connect创建连接 try { client.Connect(ipEnd.Address, ipEnd.Port); Console.WriteLine("服务器端已连接"); } catch (SocketException e) { Console.WriteLine("无法连接远程主机{0},原因{1},NativeErrorCode{2},SocketErrorCode{3}", ipEnd.Address, e.Message, e.NativeErrorCode, e.SocketErrorCode); Console.WriteLine("敲任意键退出"); Console.ReadKey(); return; } string recvData = ""; int recv = client.Receive(data); recvData = Encoding.UTF8.GetString(data, 0, recv); Console.WriteLine(recvData); //发送数据 string UserInput = ""; while (true) { UserInput = Console.ReadLine(); if (string.IsNullOrEmpty(UserInput)) continue; byte[] SendBytes = Encoding.UTF8.GetBytes(UserInput); client.Send(SendBytes); if (UserInput == "exit") { client.Shutdown(SocketShutdown.Both); client.Close(); Console.WriteLine("您选择断开与服务器的连接"); break; } recv = client.Receive(data); recvData = Encoding.UTF8.GetString(data, 0, recv); Console.WriteLine(recvData); } Console.WriteLine("敲任意键退出..."); Console.ReadKey(); } } }
这里,我将服务端和客户端需要的一些数据封装到一MyNetworkLibrary类库中去,方便调用,和以后的扩展
代码贴一下
1 using System; 2 using System.Collections.Generic; 3 using System.Net; 4 using System.Net.Sockets; 5 using System.Threading; 6 7 namespace MyNetworkLibrary 8 { 9 public static class AddressHelper 10 { 11 /// <summary> 12 /// 获取本地IPv4地址的集合 13 /// </summary> 14 /// <returns></returns> 15 public static IPAddress[] GetLocalhostIPv4Address() 16 { 17 string LocalhostName = Dns.GetHostName(); 18 IPHostEntry host = Dns.GetHostEntry(LocalhostName); 19 List<IPAddress> addresses = new List<IPAddress>(); 20 foreach (IPAddress ipAddress in host.AddressList) 21 { 22 if(ipAddress.AddressFamily == AddressFamily.InterNetwork) 23 addresses.Add(ipAddress); 24 } 25 return addresses.ToArray(); 26 } 27 /// <summary> 28 /// 以交互的方式生成有效的远程主机访问终结点,适用于控制台 29 /// </summary> 30 /// <returns></returns> 31 public static IPEndPoint GetRemoteMachineIPEndPoint() 32 { 33 IPEndPoint ipEnd = null; 34 try 35 { 36 Console.Write("请输入远程主机的ip地址:"); 37 IPAddress ipAddress = IPAddress.Parse(Console.ReadLine()); 38 Console.Write("请输入远程主机的端口号:"); 39 int port = Convert.ToInt32(Console.ReadLine()); 40 if (port >65535 || port < 1024) 41 { 42 throw new Exception("端口号为[1024,65535]之间整数"); 43 } 44 ipEnd = new IPEndPoint(ipAddress,port); 45 46 } 47 catch (ArgumentNullException) 48 { 49 Console.WriteLine("输入的数据有误!"); 50 51 } 52 catch (FormatException) 53 { 54 Console.WriteLine("输入的数据有误!"); 55 } 56 catch (Exception ex) 57 { 58 Console.WriteLine(ex.Message); 59 } 60 return ipEnd; 61 } 62 /// <summary> 63 /// 获取本机当前可用的端口号,此方法是线程安全的 64 /// </summary> 65 /// <returns></returns> 66 public static int GetOneAvaiablePortInLocalhost() 67 { 68 Mutex mtx = new Mutex(false, "MyNetworkLibrary.AddressHelper.GetOneAvailablePort"); 69 try 70 { 71 mtx.WaitOne(); 72 IPEndPoint ep = new IPEndPoint(IPAddress.Any,0); 73 using (Socket tempSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp)) 74 { 75 tempSocket.Bind(ep); 76 IPEndPoint ipEnd = tempSocket.LocalEndPoint as IPEndPoint; 77 return ipEnd.Port; 78 } 79 80 } 81 finally 82 { 83 mtx.ReleaseMutex(); 84 } 85 } 86 } 87 }
好啦 这样就写完一个简单的一对一的Socket网络程序了
看一下效果