C# Socket编程(4)初识Socket和数据流
来源:互联网 发布:新手淘宝开店教程视频 编辑:程序博客网 时间:2024/03/29 16:11
阅读目录:
1.Socket的类型
2.第一个Socket程序
2.1 服务端程序
2.2 客户端程序
2.3 程序运行效果
3.网络流和内存流
3.1 网络流
3.2 内存流
4.进一步阅读和参考资料
5.示例下载
1.Socket的类型
Socket的中文释义称为套接字,是支撑TCP/IP通信最基本的操作单元。可以将Socket看做不同主机之间的进程进行双向通信的端点,在一个双方都可以通信的Socket实例中,既保存了对方的IP地址和端口,也保持了双方通信采用的协议等信息。Socket有三种不同的类型:
- ①.流套接字:实现面向连接的TCP通信
- ②.数据报套接字:实现无连接的UDP通信
- ③.原始套接字:实现IP数据包的通信(这里不做讨论)
三种类型的套接字的对象均可使用Socket类来构造:
1 /// <summary>2 /// Socket 构造函数3 /// </summary>4 /// <param name="addressFamily">网络类型</param>5 /// <param name="socketType">Socket类型</param>6 /// <param name="protocolType">Socket使用的协议</param>7 public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType)
当我们编写基于TCP和UDP的应用程序时,既可以使用对套接字进行进一步封装的TcpListener类、TCPClient类和UdpClient类,也可以直接使用Socket类来实现,如果没有特殊需求应该使用进一步封装过的类,由于Socket类是他们实现的基础,所有在这里我们先从学习Socket类入手。
2.第一个Socket程序
在C# Socket编程(1)基本的术语和概念这篇博客中我们知道:IP协议层之上是传输层(transport layer),它提供了两种可选的协议:TCP协议和UDP协议,它们分别是面向连接和无连接的两种协议。在面向连接的Socket中,使用TCP来建立两个地址端点的会话。一旦建立这种连接,就可以在设备之间进行可靠的数据传输。在进行跟深入的学习前我们先巩固一个简单的例子(TCP)来对Socket编程建立一个直观的印象。
TCP Socket连接的过程可以简单的分为:①.服务端监听 ②.客户端请求 ③.建立连接。在使用面向连接的Socket进行通信之前,两个应用程序之间首先要建立一个TCP连接,这涉及两台相互通信的主机的TCP部件间完成的握手消息(handshake message)的交换。下面我们通过直接使用Socket类来构建一个简单的Socket应用程序(这里先从同步Socket入手,实际项目要比这复杂,有许多需要考虑的问题:如消息边界问题、端口号是否冲突、消息命令的解析等等)。在这里我们为了和每一个客户端进行通信建立两个线程:一个是接受客户端连接的线程,一个是接受客户端数据的线程,下面是分别是示例程序的服务端和客户端的代码:
2.1 服务器端代码:
1 private static byte[] m_DataBuffer = new byte[1024]; 2 //设置端口号 3 private static int m_Port = 8099; 4 static Socket serverSocket; 5 static void Main(string[] args) 6 { 7 //为了方便在本机上同时运行Client和server,使用回环地址为服务的监听地址 8 IPAddress ip = IPAddress.Loopback; 9 //实例化一个Socket对象,确定网络类型、Socket类型、协议类型10 serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);11 //Socket对象绑定IP和端口号12 serverSocket.Bind(new IPEndPoint(ip, m_Port));13 //挂起连接队列的最大长度为15,启动监听14 serverSocket.Listen(15);15 16 Console.WriteLine("启动监听{0}成功", serverSocket.LocalEndPoint.ToString());17 //一个客户端连接服务器时创建一个新的线程18 Thread myThread = new Thread(ListenClientConnect);19 myThread.Start();20 }21 22 /// <summary>23 /// 接收连接24 /// </summary>25 private static void ListenClientConnect()26 {27 while (true)28 {29 //运行到Accept()方法是会阻塞程序(同步Socket),30 //收到客户端请求创建一个新的Socket Client对象继续执行31 Socket clientSocket = serverSocket.Accept();32 clientSocket.Send(Encoding.UTF8.GetBytes("Server说:Client 你好!"));33 //创建一个接受客户端发送消息的线程34 Thread reciveThread = new Thread(ReciveMessage);35 reciveThread.Start(clientSocket);36 }37 }38 39 /// <summary>40 /// 接收信息41 /// </summary>42 /// <param name="clientSocket">包含客户端信息的套接字</param>43 private static void ReciveMessage(Object clientSocket)44 {45 if (clientSocket != null)46 {47 Socket m_ClientSocket = clientSocket as Socket;48 while (true)49 {50 try51 {52 //通过clientSocket接收数据53 int reciverNumber = m_ClientSocket.Receive(m_DataBuffer);54 Console.WriteLine("接收客户端:{0}消息:{1}", m_ClientSocket.RemoteEndPoint.ToString(), Encoding.UTF8.GetString(m_DataBuffer, 0, reciverNumber));55 }56 catch (Exception ex)57 {58 Console.WriteLine(ex.Message);59 m_ClientSocket.Shutdown(SocketShutdown.Both);60 m_ClientSocket.Close();61 break;62 }63 }64 }65 }66 }
2.2 客户端代码:
1 //创建一个数据缓冲区 2 private static byte[] m_DataBuffer = new byte[1024]; 3 static void Main(string[] args) 4 { 5 IPAddress ip = IPAddress.Loopback; 6 Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 7 8 try 9 {10 clientSocket.Connect(new IPEndPoint(ip, 8099));11 Console.WriteLine("连接服务器成功");12 }13 catch (Exception ex)14 {15 Console.WriteLine("连接服务器失败,按回车键退出");16 Console.WriteLine(ex.Message);17 return;18 }19 //通过clientSocket接收数据20 int receiveLength = clientSocket.Receive(m_DataBuffer);21 Console.WriteLine("接受服务器消息:{0}", Encoding.UTF8.GetString(m_DataBuffer, 0, receiveLength));22 //通过clientSocket发送数据23 for (int i = 0; i < 10; i++)24 {25 try26 {27 Thread.Sleep(1000);28 string sendMessage = string.Format("{0} {1}", "Server 你好!", DateTime.Now.ToString());29 clientSocket.Send(Encoding.UTF8.GetBytes(sendMessage));30 Console.WriteLine("向服务器发送消息:{0}", sendMessage);31 }32 catch33 {34 clientSocket.Shutdown(SocketShutdown.Both);35 clientSocket.Close();36 break;37 }38 }39 Console.WriteLine("发送完毕,按回车键退出");40 Console.Read();41 }42 }
2.3 运行示例程序
首先运行服务端程序:
接着运行客户端程序,向服务端发送消息后
这时候我们可以看到服务端已经收到了客户端发送的消息
3.网络流和内存流
通过网络传输数据,或者对文件数据进行操作的时候都需要先将数据转换为数据流。典型的数据流是和某个外部数据源相关,数据源可以是文件、外部设备、内存、网络套接字等。.NET提供多个从Stream类派生的子类来对不同的数据源提供支持,每个类都代表了一种具体的数据流类型。例如和磁盘文件相关的文件流FileStream和Socket相关的NetworkStream,和内存相关的MemoryStream等,在Socket编程中我们只需了解NetworkStream和MemoryStream(具体文件IO可以参考博文:.NET I/O 学习笔记:文件的读和写),一个用来网络数据的传输,另一个用作数据缓冲区。
3.1 网络流(NetworkStream)
数据在网络的各个位置之间是以连续的字节形式传输的,我们使用NetworkStream类来发送和接收网络数据。和其他的的流类型不同NetworkStream 类是在System.Net.Sockets 命名空间中的,该类实现专门用于网络资源的 Stream 类。NetworkStream 选件类和其他流之间的主要差异在于NetworkStream 没有当前位置的概念,因此不支持查找功能,并且NetworkStream仅支持面向连接(TCP)的Socket。
对于NetworkStream来说,写入操作是指将数据源内存缓冲区到网络上的数据传输;读取操作是从网络上到接收端内存缓冲区的数据传输。
创建NetworkStream对象
我们可以通过TcpClient对象的GetStream()方法获取该对象发送和接收数据的NetworkStream 对象:
1 TcpClient client = new TcpClient();2 client.Connect("www.baidu.com", 8099);3 NetworkStream nStream = client.GetStream();
也可以通过使用Socket来获取 NetworkStream 对象:
1 NetworkStream myNetworkStream = new NetworkStream(mySocket);
通过NetworkStream对象获取数据
接收数据端通过调用Read方法将数据从接收缓冲区中读取到进程缓冲区中,完成读取操作。可以通过调用DataAvailable属性来确定是否还有数据可供读取,如下:
1 TcpClient client = new TcpClient(); 2 client.Connect("www.baidu.com", 8099); 3 NetworkStream nStream = client.GetStream(); 4 //是否有数据可读 5 if (nStream.CanRead) 6 { 7 //接受数据的缓冲区 8 byte[] myReadBuffer = new byte[1024]; 9 StringBuilder completeMessage = new StringBuilder();10 int numberOfBytesRead = 0;11 //准备接收的信息也有可能大于1024所以使用循环12 do13 {14 numberOfBytesRead = nStream.Read(myReadBuffer,0,myReadBuffer.Length);15 completeMessage.AppendFormat("{0}",Encoding.UTF8.GetString(myReadBuffer,0,numberOfBytesRead);16 }while(nStream.DataAvailable);17 Console.WriteLine("接受的信息为:"+completeMessage);18 }19 else20 {21 Console.WriteLine("当前没有可供读取的数据。");22 }
3.2 内存流(MemoryStream)
MemoryStream表示保存在内存中的数据流,有该类封装的数据可以直接在内存中访问。内存流一般用于暂时缓存数据已降低应用程序对临时缓冲区和临时文件的需要。内存流相对于字节数组容量可以自动增长,并且在需要对数据进行加密以及对数据长度不定的数据进行缓存时,使用内存流比较方便。MemoryStream支持对数据流的查找和随机访问,当该类对象的CanSeek属性值为True时,程序可以通过范围Position属性获取内存流当前的位置。下面我们通过一个简单的小示例学习如何具体使用内存流:
1 static void Main(string[] args) 2 { 3 //构造MemoryStream实例 4 MemoryStream m_Stream = new MemoryStream(); 5 Console.WriteLine("初始化分配容量:{0}", m_Stream.Capacity); 6 Console.WriteLine("初始使用量:{0}", m_Stream.Length); 7 8 //将待写入数据从字符串转换为字节数组 9 UnicodeEncoding encoder = new UnicodeEncoding();10 byte[] bytes = encoder.GetBytes("新增数据");11 12 //向内存流中写入数据13 for (int i = 0; i < 4; i++)14 {15 Console.WriteLine("第{0}写入新数据", i);16 m_Stream.Write(bytes, 0, bytes.Length);17 }18 19 //写入数据后MemoryStream实例的容量和使用量的大小20 Console.WriteLine("当前分配容量:{0}", m_Stream.Capacity);21 Console.WriteLine("当前使用量:{0}", m_Stream.Length);22 23 Console.Read();24 }
- C# Socket编程(4)初识Socket和数据流
- C# Socket编程(4)初识Socket和数据流
- C# Socket编程(4)初识Socket和数据流
- C# Socket编程(4)初识Socket和数据流
- 初识Socket编程
- 0、初识Socket编程
- Java Socket编程(3)初识TCP Socket
- Java Socket编程(3)初识TCP Socket
- Java网络编程-初识Socket
- C# Socket编程(3)编码和解码
- C# Socket编程(3)编码和解码
- C# Socket编程(3)编码和解码
- Raw Socket和Socket编程
- Raw Socket和Socket编程
- Raw Socket和Socket编程
- Raw Socket和Socket编程
- Raw Socket和Socket编程
- Raw Socket和Socket编程
- 修复完浏览器后每次打开ie8都会自动弹出管理加载项,请问怎样才能不弹出
- QpushButton 去除外侧的黑色边框
- 机架服务器 远程电源管理 备忘
- openstack 数据库实例删除脚本
- 在linux下设置开机自动启动程序的方法
- C# Socket编程(4)初识Socket和数据流
- Google code jam: Problem A. Minimum Scalar Product
- 确实要移动只读文件“lpk.dll”吗?
- 如何了解事件中回调函数的参数
- 关于 C++ 和 C 的混编
- 菜鸟的springMVC的入门
- C# Socket编程(5)使用TCP Socket
- 在Linux下用netstat查看网络状态、端口状态
- MSP430端口与中断,键盘程序的模板