觉得很好的一句话:学习网络编程的重点不是记住这些概念,而是要学会如何去使用它们。
网络编程一般定义为实现在两个进程之间交换信息的过程,分为数据的发送方和接收方,发送方将数据进行编码组装,然后通过物理线路发送出去;接收方收到数据后需要对其进行解码,然后读取数据的内容。
Socket编程的重点是IP地址、端口和通信协议,端口数值为0到65535,共65536个。一般在使用端口号的时候不使用0,0表示任意端口;同理,IP地址一般不使用0.0.0.0,全为0的IP地址表示任意地址。Internet网络采用的协议是Tcp/IP协议,Tcp协议是一种以固接连线为基础的协议,可提供两台计算机间可靠的数据传送,称为“传输控制协议”。UDP协议是无连接通信协议,不保证可靠的数据传输,但能向若干个目标发送数据,接收发自若干个源的数据。
Socket类
网络程序中的套接字(Socket)用于将程序与网络连接起来。套接字是一个假想的连接装置,就像用于连接电器与电线的插座。C#将套接字抽象为类,程序设计者只需要创建Socket类对象,即可使用套接字。网络通信至少有两个通信终端,监听并接受连接的一端称为服务器,而请求连接的一方称为客户端。
对于服务器,Socket类的使用步骤如下:
- 实例化Socket对象
- 调用Bind方法绑定本地终结点,需要指定一个本地IP地址和一个本地端口,Socket将在该终结点上监听传入的客户端连接。
- 动用Listen方法开始监听客户端连接
- 调用Accept相关方法接受连接,并返回一个用于与客户端进行通信的Socket实例。
- 通过Send和Receive方法进行收发数据。
- 用Close关闭Socket对象。
对于客户端,Socket类的使用步骤如下:
1 . 实例化Socket对象
2 . 调用Connect方法进行连接,需要指定服务器的地址和端口,服务器的端口必须与服务器端绑定的监听端 口一致。
3.通过Send和Receive方法进行收发数据。
4.用Close关闭Socket对象。
实现服务器程序
步骤如下:
(1)引入以下命名空间
using System.Net;
using System.Net.Sockets;
(2)实例化一个用于在服务器上监听和接受连接的Scoket对象。
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//因为使用TCP进行通信,所以第三个参数使用ProtocolType.Tcp
(3)绑定本地终结点,监听程序将监听此总结点上的连接。
IPEndPoint类包含应用程序连接到主机上的服务所需要的主机和本地或远程端口信息。
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 1222);//IpAddress.Any表示IP地址为0.0.0.0/1222为端口号
server.Bind(endpoint);
(4)绑定本地终结点后,开始监听连接
server.Listen(15);
(5)接受客户端,此次使用异步的方式来连接。
server.BeginAccept(new AsyncCallback(AccpCallback),server);
private static void AccpCallback(IAsyncResult ar)
{
Socket server = (Socket)ar.AsyncState;
//放回表示客户端连接的Socket
Socket client = server.EndAccept(ar);
Console.WriteLine("已接受客户端{0}的连接。", client.EmoteEndPoint.ToString());
//向客户端发送一条消息
byte[] data = Encoding.UTF8.GetByte("服务器以接受连接");
//先发送内容的长度
int len = data.Length;
client.Send(BitConverter.GetByte(len));
//发送内容正文
client.Send(data);
//关闭Socket
client.Close();
//继续接受连接
server.BeginAccept(new AsyncCallback(AccpCallback),server );
}
客户端程序
(1)引入以下命名空间
using System.Net;
using System.Net.Sockets;
(2)实例化Scoket对象。
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//因为使用TCP进行通信,所以第三个参数使用ProtocolType.Tcp
(3)连接服务器并接受服务器发来的消息
try
{
client.Connect("127.0.0.1",1332);//端口号要与服务器的端口号一致
byte[] buffer = new byte[4];
client.Receive(buffer);
int len = BitConverter.ToInt(buffer,0);
//开始接收正文
buffer = new byte[len];
client.Receive(buffer);
string msg = Encoding.UTF8.GetString(buffer);
Console.WriteLine("从服务器接收到的消息:\n"+msg);
}
catch
{
}
IP地址封装
IP地址是一种低级协议,TCP协议和DUP协议都是在他的基础上构建。
C#提供了IP地址相关的类,包括Dns类,IPAddress类、IPHostEntry类等。位于sysytem.NET命名空间。
访问本地主机名称和ip地址
string IP, name, localip = "10.15.0.85/24";
string localname = Dns.GetHostName();
IPAddress[] ips = Dns.GetHostAddresses(localname);
foreach (IPAddress ip in ips)
{
if (!ip.IsIPv6SiteLocal)
localip = ip.ToString();
}
label1.Text += "本机名" + localname + " 本机IP地址: " + localip;
TCP程序设计
TCP传输控制协议是一种面向连接的、可靠的,基于字节流的传输层通信协议。在C#中,TCP程序设计是指利用Socket、TcpClient和TcpListener类编写的网络通信程序,这3个类都位于system.net。sockets命名空间。利用TCP协议进行通信的两个应用程序是有主次之分的,一个称为服务器端程序,另一个称为客户端程序。
Socket类为网络通信提供了一套丰富的方法和属性,TCPClient和UDPClient等类在内部使用该类
TcpClient类用于在同步阻止模式下通过网络来连接、发送和接收流数据。
为使TcpClient连接并交换数据,TcpClient类或Socket实例必须侦听是否有传入的连接请求。
TCP程序实例:客户端和服务器端的交互
1.服务器端:在Main方法中创建TCP连接对象;然后监听客户端接入,并读取接入的客户端IP地址和传入的消息;
static void Main(string[] args)
{
int port = 888; //端口
TcpClient tcpClient; //创建TCP连接对象
IPAddress[] serverIP = Dns.GetHostAddresses("127.0.0.1");//定义IP地址 fe80::2138:d3d0:f56:d209%11
IPAddress loacalADDress = serverIP[0]; //IP地址
TcpListener tcpListener = new TcpListener(loacalADDress, port);//监听套接字
tcpListener.Start(); //开始监听
Console.WriteLine("服务器启动成功,等待用户接入...");
while (true)
{
try
{
tcpClient = tcpListener.AcceptTcpClient();//每接收到一个客户端则生成一个TCPClient
NetworkStream networkStream = tcpClient.GetStream();//获取网络数据流
BinaryReader reader = new BinaryReader(networkStream);//定义流数据读取对象
BinaryWriter writer = new BinaryWriter(networkStream);//定义流数据写入对象
while (true)
{
try
{
string strReader = reader.ReadString();//接收消息
string [] strReaders = strReader.Split(new char[]{ ' ' });//截取客户端消息
Console.WriteLine("有客户端接入,客户IP:" + strReaders[0]);
Console.WriteLine("来自客户端的消息:" + strReaders[1]);
string strWriter = "我是服务器,欢迎光临";
writer.Write(strWriter);//向对方发送消息
}
catch
{
break;
}
}
}
catch
{
break;
}
}
}
2.客户端
在Main方法中创建TCP连接对象,以指定的地址和端口连接服务器;然后向服务器端发送数据和接收服务器端传输的数据。
static void Main(string[] args)
{
//创建一个TCPClient对象,自动分配IP地址和端口号
TcpClient tcpClient = new TcpClient();
//连接服务器,其IP端口号为127.0.0.1和888
tcpClient.Connect("127.0.0.1",888);
//判断连接是否成功
if (tcpClient != null)
{
Console.WriteLine("服务器连接成功");
NetworkStream networkStream = tcpClient.GetStream();//获取数据流
BinaryReader reader = new BinaryReader(networkStream);//定义流数据读取对象
BinaryWriter writer = new BinaryWriter(networkStream);//第一数据流写对象
string localip = "127.0.0.1";//存储本机ip,默认值为127.0.0.1
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());//获取所有ip地址
foreach (IPAddress ip in ips)
{
if (!ip.IsIPv6SiteLocal)//如果不是ipv6地址
localip = ip.ToString();//获取本机ip地址
}
writer.Write(localip+" 你好服务器,我是客户端");//向服务器发送数据
while (true)
{
try
{
string strReader = reader.ReadString();//接收服务器发送的数据
if (strReader != null)
{
Console.WriteLine("来自服务器的消息是:"+strReader);
}
}
catch
{
break;
}
}
}
Console.WriteLine("连接服务器失败");
}
UDP程序设计
用户数据报协议,它是网络信息传输的另一种形式。UDP通信和TCP通信不同,基于UDP的信息传输更快,但不提供可靠的保证。
UdpClient类用于在同步阻止模式下发送和接收无连接的UDP数据报。因为UDP是无连接传输协议,所以不需要再发送和接收数据前建立远程主机连接,但可以选择使用下面两种方法之一建立默认远程主机。
1.使用远程主机名和端口号作为参数创建UdpClient类的实例。
2.创建UdpClient类的实例,然后调用Connect方法。
UDP网络程序实例,广播数据报程序
(1)创建广播主机项目Server,在Main方法中创建DUP连接;然后通过DUP不断向外发送广播信息。
class Program
{
static UdpClient udp = new UdpClient();//创建UdpClient对象
static void Main(string[] args)
{
udp.Connect("127.0.0.1", 888);//调用UdpClient对象的Connect方法建立默认远程主机
while (true)
{
Thread thread = new Thread(new ThreadStart(Way1));
thread.Start();
}
}
static public void Way1()
{
while (true)
{
try
{
Byte[] sendBytes = Encoding.Default.GetBytes("(" + DateTime.Now.ToLongTimeString() + ")节目预报:8点有大型晚会,请收听");//定义一个字节数组,用来存放发送到远程主机的信息
Console.WriteLine("(" + DateTime.Now.ToLongTimeString() + ")节目预报:8点有大型晚会,请收听");
udp.Send(sendBytes, sendBytes.Length);//调用UdpClient对象的Send方法将UDP数据发送到远程主机上
Thread.Sleep(5000);
}
catch (Exception EX)
{
Console.WriteLine(EX.Message);
}
}
}
}
(2)创建接收广播项目Client,单击“开始接收”按钮,系统开始接收主机播出的信息;单击“停止接收”按钮,系统会停止接收广播播出的信息。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;//在其他线程中可以调用主窗体控件
}
bool flag=true;
UdpClient udp;
Thread thread;
IPEndPoint ipendpoint;
private void button1_Click(object sender, EventArgs e)
{
udp = new UdpClient(888);//使用端口号创建UDP连接对象
flag = true;
ipendpoint = new IPEndPoint(IPAddress.Any, 888);//创建IPEndPoint对象,用来响应主机的标识
// Thread thread = new Thread(new ThreadStart(Way2));
thread = new Thread(Way2);
thread.Start();
}
public void Way2()
{
while (flag)
{
try
{
if (udp.Available <= 0) continue;//判断是否有网络数据;遇到continue后,不执行continue后面的代码,执行下一次循环。
if (udp.Client == null) return;//判断连接是否为空、// return 直接跳过main函数,立即结束本次方法的执行
byte[] bytes = udp.Receive(ref ipendpoint);
//将获得的UDP数据转换为字符串形式
string str = Encoding.Default.GetString(bytes);
textBox2.Text = "正在接收的信息:"+str;
textBox1.Text += "\n" + str;//显示接收的所有数据
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Thread.Sleep(2000);
}
}
private void button2_Click(object sender, EventArgs e)
{
flag = false;
if (thread.ThreadState == ThreadState.Running)
thread.Abort();
udp.Close();
}
}