C#网络编程(一)基础篇
简介:
C#网络编程API包含在System.Net和System.Net.Sockets命名空间下,大部分网络操作都可以在其中找到相应的类来实现;包括Socket的创建和连接,网络流收发方法的封装,而且还封装了服务端类和客户端类,提供创建服务端和客户端的快速通道;
(一)Socket类
Socket类在System.Net.Sockets命名空间下,是最基本的网络操作类,其中封装了网络连接的创建和关闭,数据的收发,以及网络状态监控等一系列有用的功能;
示例(TCP):
using System;
using System.Net;
using System.Net.Sockets;
using System.Text; class Test_Tcp
{
private Socket socket;
private void Server()
{
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 绑定服务端IP和端口,客户端通过这个地址连入
server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), ));
// 开启服务监听,参数为最大挂起队列的连接数(并发),已连接的不计
server.Listen();
// 这是一个阻塞方法,接收客户端接入,返回客户端连接Socket
socket = server.Accept();
Console.WriteLine("Local : {0}\nRemote : {1}", socket.LocalEndPoint.ToString(), socket.RemoteEndPoint.ToString());
// 数据接收:这是一个异步过程
ReceiveAsync();
// 数据发送:这是一个阻塞方法
Write();
// 关闭客户端连接Socket和服务Socket
socket.Close();
server.Close();
}
}
Server()是一个简易的基于TCP连接的服务端开启方法,使用这个方法需要用到System.Net.Sockets和System.Net两个命名空间;
服务端开启分为4步:创建Socket、绑定IP和端口Bind()、开启监听Listen()、接入客户端Accept();
其中还有两个自定义方法,ReceiveAsync()和Write(),它们分别是异步数据接收、数据发送,它们的定义在后面可以看到。
另外,Socket的Accept()接入客户端连接方法也有异步版本BeginAccept(),它的用法类似于后面的BeginReceive(),多客户端系统一般都是用这种异步接入方式,在BeginAccept方法的回调方法中,维护一个客户端连接容器;
class Test_Tcp
{
private void Client()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("127.0.0.1", );
Console.WriteLine("{0}\n{1}", socket.LocalEndPoint.ToString(), socket.RemoteEndPoint.ToString());
ReceiveAsync();
Write();
socket.Close();
}
}
Client()方法是相应的客户端开启方法,相对服务端来说代码更简单明了,而且不需绑定IP,端口也是自动分配,不过需注意Connect()方法中的IP和端口是服务端绑定的服务地址;ReceiveAysnc()、Write()和服务端中的两个方法是一样的;
using System.Text; class Test_Tcp
{
private const int BuffSize = ;
private void ReceiveAsync()
{
if (socket == null || !socket.Connected) return;
var Buff = new byte[BuffSize];
socket.BeginReceive(Buff, , BuffSize, SocketFlags.None, OnReceived, Buff);
} private void OnReceived(IAsyncResult result)
{
if (socket == null || !socket.Connected) return;
byte[] data = (byte[])result.AsyncState; int recLength = socket.EndReceive(result);
Console.WriteLine("Length = {0}", recLength); if (recLength <= ) return;
ReceiveAsync(); string msg = Encoding.Default.GetString(data, , recLength);
Console.WriteLine("Receive : {0}\n", msg);
} private void Write()
{
while (socket != null && socket.Connected)
{
string msg = Console.ReadLine();
if (msg == "exit") break;
byte[] buff = Encoding.Default.GetBytes(msg);
socket.Send(buff);
}
}
}
这3个方法是数据异步接收、数据接收回调、和聊天数据发送的方法,这里的字符串 - 字节序列转换,需要用到System.Text命名空间;
ReceiveAysnc()是开启异步数据接收方法,其中Socket.BeginReceive方法是Socket类中的异步接收方法,它需要一个字节缓冲区和一个回调方法作为参数;
OnRecieve()数据接收回调方法,在这个方法中,主要工作是开启新的异步接收方法RecieveAsync(),以及数据处理;这里的数据处理只是简单地转换为字符串,并打印到控制台,而一般在实际应用中,这里就收的数据data会用一个容器储存起来(一般是队列Queue),然后在其它地方从容器中取出数据,并进行复杂的处理;
Write()方法:在网络连接可用状态下,不断从控制台等待读取一行字符串,并将其转换为字节序列发送到socket,服务器在异步接收线程中会接收到数据,并触发回调方法OnRecieve(),控制台会看到打印的字符串;输入exit终结循环,Write()方法返回,接着关闭socket;
Socket.Send发送数据方法是一个阻塞方法,它同样也有异步版本BeginSend(),用法和BeginReceive()类似;
在实际项目应用中,数据的发送和接收会分别维护一个发送队列和接收队列,这样,应用层在调用数据发送方法时,只是把数据加入到发送队列,而不用等待发送完成,特别是数据量大的时候,等待发送的时间会影响到应用层性能;真正的数据发送用一个专门的线程不断从发送队列里面取数据并发送;数据接收也是类似维护一个接收队列。
程序入口:
class Test_Tcp
{
public void Run()
{
Console.WriteLine("Input 'c' or 's' :");
var key = Console.ReadKey();
Console.WriteLine();
if (key.Key == ConsoleKey.C)
{
Client();
}
else if (key.Key == ConsoleKey.S)
{
Server();
}
}
} class Program
{
static int Main(string[] args)
{
new Test_Tcp().Run();
return ;
}
}
(二)NetworkStream网络流
完全限定名System.Net.Sockets.NetworkStream网络流类,继承于Stream类,简单地理解就是对Socket读写网络数据的封装,用NetworkStream网络流封装Socket,可以简化数据的接收和发送;
NetworkStream的构造方法需要传入一个可用的网络Socket实例,然后就可以用流的方式替代Socket进行数据的读写;
var netStream = new NetworkStream(socket);
这里的socket必须网络连接成功(服务端连入客户端,客户端连接远程成功),才能用NetworkStream进行数据流的读写;
(三)UDP和心跳包
相比于TCP的可靠传输,UDP是一种非连接、不可靠的数据传输协议,它不需要建立连接,也就是说,服务端不需要监听Listen、接受连入Accept,而且Socket.Connected也不能用;Udp发送数据不需确定对方是否存在,网路是否可通,当然也无法确定对方是否收到(但可以手动发送返回包来通知对方),但是Udp相对Tcp的消耗也小;
Socket的构造方法第二、三个参数分别要设置为SocketType.Dgram、ProtocolType.Udp;
相比于TCP连接,UDP的客户端差别不大,但是在UDP服务端,由于没有客户端连接,数据的发送应该使用SendTo,这个方法要求传入一个客户端地址结构EndPoint表示目标终端,那么,在接收数据时,就应该保存好数据的来源地址;
那么,接收数据也应该用另一个版本ReceiveFrom,这个方法可以得到一个数据来源地址EndPoint,这时就可以保存要用到的EndPoint,这个EndPoint可以用一个容器来维护;
这两个方法在可靠连接Tcp中也可以用(但一般不这么用),另外它们都也有各自的异步版本,Begin开头的便是;
心跳包,顾名思义是一种在通信双方,定时发送一个特定的数据序列,一般3-10s,用来通知对方网络通信是正常的,一般这个数据序列短小、固定的;心跳包在Udp非连接协议中非常地必要,因为Udp没办法在没有数据接收的情况下确定网络状态;在Tcp中心跳包不是必要的,但是要求较高的项目中依然会应用心跳包;心跳超时(一般大于2倍的间隔时间),就表示网络通信失联;
C#网络编程(一)基础篇的更多相关文章
- python网络编程——socket基础篇
python的网络编程比c语言简单许多, 封装许多底层的实现细节, 方便程序员使用的同时, 也使程序员比较难了解一些底层的东西. 1 TCP/IP 要想理解socket,首先得熟悉一下TCP/IP协议 ...
- python六十七课——网络编程(基础知识了解)
网络编程: 什么是网络编程? 网络:它是一种隐形的媒介:可以将多台计算机使用(将它们连接到一起) 网络编程:将多台计算机之间可以相互通信了(做数据交互) 一旦涉及到网络编程,划分为两个方向存在,一方我 ...
- java第九节 网络编程的基础知识
/** * * 网络编程的基础知识 * 网络协议与TCP/IP * IP地址和Port(端口号) * 本地回路的IP地址:127.0.0.1 * 端口号的范围为0-65535之间,0-1023之间的端 ...
- Linux 网络协议栈开发基础篇—— 网桥br0
一.桥接的概念 简单来说,桥接就是把一台机器上的若干个网络接口"连接"起来.其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去.以使得网口之间的报文能够互相转发. 交换机 ...
- (转)Android高性能编程(1)--基础篇
关于专题 本专题将深入研究Android的高性能编程方面,其中涉及到的内容会有Android内存优化,算法优化,Android的界面优化,Android指令级优化,以及Android应用内存占 ...
- Linux虚拟网络:Docker网络知识之基础篇
我们在工作中应用了docker容器化技术,服务的部署.维护和扩展都方便了很多.然而,近期在私有化部署过程中,由于不同服务器环境的复杂多变,常常遇到网络方面的问题,现象为容器服务运行正常,但宿主机.容器 ...
- 01网络编程(基础知识+OSI七层协议+TCP与UDP)
目录 01 网络编程 一.软件开发架构 1.1 CS架构 1.2 BS架构 二.网络理论前戏 2.1 简介 2.2 常见硬件 三.OSI七层协议(五层) 3.1 七层协议 3.2 五层协议 3.3 知 ...
- TCP/UDP网络编程的基础知识与基本示例(windows和Linux)
一.TCP编程的一般步骤 服务器端: 1.创建一个socket,用函数socket() 2.绑定IP地址.端口等信息到socket上,用函数bind() 3.开启监听,用函数listen() 4.接收 ...
- Linux系统编程:socket网络编程(操作篇)
一.问题思考 问1.网络通信应用在什么场合?通信的前提是什么? 答1.主要应用在不同主机进程间的互相通信,同一主机的进程也可以使用网络进行通信.通信的前提是如何标识通信进程的唯一,由于不同主机的进程极 ...
- 【Java_多线程并发编程】基础篇——synchronized关键字
1. synchronized同步锁的原理 当我们调用某对象的synchronized方法或代码块时,就获取了该对象的同步锁.例如,synchronized(obj)就获取了“obj这个对象”的同步锁 ...
随机推荐
- PHP之string之implode()函数使用
implode (PHP 4, PHP 5, PHP 7) implode - Join array elements with a string implode - 将一个一维数组的值转化为字符串 ...
- 定时删除elasticsearch索引
从去年搭建了日志系统后,就没有去管它了,最近发现大半年各种日志的index也蛮多的,就想着写个脚本定时清理一下,把一些太久的日志清理掉. 脚本思路:通过获取index的尾部时间与我们设定的过期时间进行 ...
- JavaScript -- 定义二维数组
方法一:直接定义并且初始化,这种遇到数量少的情况可以用var _TheArray = [["0-1","0-2"],["1-1"," ...
- Scrum 冲刺博客第七篇
一.当天站立式会议照片一张 二.每个人的工作 (有work item 的ID),并将其记录在码云项目管理中 昨天已完成的工作 对排行榜的界面和功能进行初步设计 今天计划完成的工作 重新对界面进行美化 ...
- jQueryEasyUI 学习笔记
jQuery EasyUI是什么? jQuery EasyUI是一组基于jQuery的UI插件集合体,而jQuery EasyUI的目标就是帮助web开发者更轻松的打造出功能丰富并且美观的UI界面.开 ...
- Codeforces 985G. Team Players
Description 有 \(n\) 个人 , \(m\) 对人有冲突 , 你要从这 \(n\) 个人中选出三个人成为一组 , 使得同一组的人不存在一对有冲突 题面 Solution 容斥 答案=总 ...
- [转]SSRS: Checking for Divide By Zero Using Custom Code
本文转自:http://salvoz.com/blog/2011/11/25/ssrs-checking-for-divide-by-zero-using-custom-code/ I encount ...
- ASP.NET中让图片以二进制的形式存储在数据库中
今早有个网友问到我这问题,以前我都是直接在数据库中存文件名的,还没有试过存储整张图片到数据库中,上网搜索了一下,自己又测试了一番,代码如下:建立保存图片的表的SQL语句: USE [niunantes ...
- [javaSE] GUI(菜单)
菜单MenuBar Menu MenuItem 调用Frame对象的setMenuBar()方法,设置菜单,参数:MenuBar对象 import java.awt.FlowLayout; impo ...
- [javaSE] 集合框架(Map概述)
Map集合,将key对象映射到value对象 三个主要的子类:Hashtable,HashMap,TreeMap Hashtable:底层是哈希表数据结构,不允许使用null值,线程同步 HashMa ...