1.基于Tcp协议的Socket通讯
服务端
使用Tcp协议通讯需要具备以下几个条件:
(1).建立一个套接字(Socket) (固定写法,第一个参数是因特网,第二个参数是用流来进行传输,第三个参数是协议的类型。这里选TCP协议)
Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
(2).绑定服务器端IP地址及端口号(输入你自己电脑的ip)
TcpSever.Bind(new IPEndPoint(IPAddress.Parse("10.198.1.237"), 9900));
IPEndPoint(第一个参数是IP地址,第二个参数是端口号,这个端口号是自己设定的。尽量设定大一点的端口号(也不要太大,4位数就好了),防止与电脑的其他应用程序冲突。)
实用工具类-IPAddress
IPAddress ipAddress = IPAddress.Parse("234.34.5.3");
byte[] address = ipAddress.GetAddressBytes();//得到四个位置上的具体值
string ipString = ipAddress.ToString();//得到ip的字符串
(3).利用Listen()方法开启监听,设置客户端的最大连接数量。
TcpSever.Listen(100);
(4).利用Accept()方法尝试与客户端建立一个连接,返回一个连接的客户端的Socket.可以得到连接的客户端的IP和端口
Socket clientSocket = TcpSever.Accept();
(5).利用Send()方法向建立连接的主机发送消息 ,要发送的消息要转为字节数组,才能发送。
string sendMessage = "Welcome to this Sever!";
//转格式API
byte[] SendData = Encoding.UTF8.GetBytes(sendMessage);
clientSocket.Send(SendData);
(6).利用Recive()方法接受来自建立连接的主机的消息
//接受客户端的消息
//新建一个字节数组容器,容量按实际情况设定,测试的话1024足够用了
byte[] ReceiveData = new byte[1024];
//接收的数据的长度
int Length = clientSocket.Receive(ReceiveData);
//接收的字节数组转为字符串格式
string ReceivedMessage = Encoding.UTF8.GetString(ReceiveData, 0, Length);
//打印从客户端接收到的消息
Console.WriteLine(ReceivedMessage);
客户端
(1).建立一个套接字(Socket)
Socket ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
(2).利用Connect()方法与服务器建立连接(填主机的IP和端口)
ClientSocket.Connect(new IPEndPoint(IPAddress.Parse("10.198.1.237"), 9900));
(3).利用Send()方法向建立连接的主机发送消息
//发送消息到服务端
string SendMessage = Console.ReadLine();
byte[] SendData = Encoding.UTF8.GetBytes(SendMessage);
ClientSocket.Send(SendData);
(4).利用Recive()方法接受来自建立连接的主机的消息
//接收服务端的消息
byte[] ReceivedData = new byte[1024];
int length = ClientSocket.Receive(ReceivedData);
string ReceivedMessage = Encoding.UTF8.GetString(ReceivedData, 0, length);
Console.WriteLine(ReceivedMessage);
服务器端完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
usingSystem.Net;
usingSystem.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace TCP_Sever
{
class Program
{
static List<ClientedSocket> ClientList= new List<ClientedSocket>();
static void Main(string[] args)
{
//一:新建一个Socket
Socket TcpSever = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//一:绑定ip
TcpSever.Bind(new IPEndPoint(IPAddress.Parse("10.198.1.237"), 9900));
//设置客户端最大连接数量
TcpSever.Listen(100);
Console.WriteLine("Sever is running-----");
Socket clientSocket = TcpSever.Accept();
//发送给客户端
string sendMessage = "Welcome to this Sever!";
byte[] SendData = Encoding.UTF8.GetBytes(sendMessage);
clientSocket.Send(SendData);
//接受客户端的消息
byte[] ReceiveData = new byte[1024];
int Length = clientSocket.Receive(ReceiveData);
string ReceivedMessage = Encoding.UTF8.GetString(ReceiveData, 0, Length);
Console.WriteLine(ReceivedMessage);
Console.ReadKey();
}
}
}
客户端完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
usingSystem.Net;
usingSystem.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TCP_Client
{
class Program
{
private static Socket ClientSocket;
static void Main(string[] args)
{
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ClientSocket.Connect(new IPEndPoint(IPAddress.Parse("10.198.1.12"), 9900));
//接收服务端的消息
byte[] ReceivedData = new byte[1024];
int length = ClientSocket.Receive(ReceivedData);
string ReceivedMessage = Encoding.UTF8.GetString(ReceivedData, 0, length);
Console.WriteLine(ReceivedMessage);
//发送消息到服务端
string SendMessage = Console.ReadLine();
byte[] SendData = Encoding.UTF8.GetBytes(SendMessage);
ClientSocket.Send(SendData);
Console.ReadKey();
}
}
}
测试结果:
现在客户端和服务器只能互相收发一次消息,要怎么才能互相发送和接收多次消息呢?
服务器代码改进:现在服务器只负责连接客户端。数据的发送和接收交给连接进来的那个Socket
using System;
using System.Collections.Generic;
using System.Linq;
usingSystem.Net;
usingSystem.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace TCP_Sever
{
class Program
{
static List<ClientedSocket> ClientList= new List<ClientedSocket>();
static void Main(string[] args)
{
//一:新建一个Socket
Socket TcpSever = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//一:绑定ip
TcpSever.Bind(new IPEndPoint(IPAddress.Parse("10.198.1.12"), 9900));
//设置客户端最大连接数量
TcpSever.Listen(100);
Console.WriteLine("Sever is running-----");
while (true)
{
Socket clientSocket = TcpSever.Accept();
Console.WriteLine("a client is connect");
//新建一个类,用来处理连接进来的客户端的逻辑
ClientedSocket Client = new ClientedSocket(clientSocket);
Client.SendMessage(" you are connected");
// 把连接的Socket存起来,为了以后做数据分发
ClientList.Add(Client);
}
}
//数据分发的方法,就是给客户端发送消息
public static void BroadCastMessage(string msg)
{
//新建一个用来保存客户端连接断开的Socket
var NotConnectList = new List<ClientedSocket>();
//遍历找到所以已经连接服务器的客户端
foreach (var Client in ClientList)
{ //如果连接没有断开,就发送消息
if(Client.Connect())
{
Client.SendMessage(msg);
}
else//否则加入到没有连接的客户端的列表里
{
NotConnectList.Add(Client);
}
}
//遍历断开的列表,从连接的列表里移除已经断开的Socket
foreach (var item in NotConnectList)
{
ClientList.Remove(item);
}
}
}
}
新建一个类,把接收的客户端的Socket存起来
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
usingSystem.Net;
usingSystem.Net.Sockets;
using System.Threading;
namespace TCP_Sever
{
class ClientedSocket
{
private Socket clientedSocket;
private Thread t;
private byte[] ReceivedData = new byte[1024];
//这个类在构造的时候就传入一个连接客户端的SOCKET。我们在这里循环的接收客户端发来的消息
public ClientedSocket(Socket ClientedSocket)
{
this.clientedSocket = ClientedSocket;
//启动一个线程,处理客户端的数据接收
t = new Thread(ReceiveMessage);
t.Start();
}
private void ReceiveMessage()
{
try
{
//一直接收
while (true)
{
//在接收数据之前 判断一下Socket连接是否断开
if (clientedSocket.Poll(10, SelectMode.SelectRead))
{
clientedSocket.Shutdown(SocketShutdown.Both);
clientedSocket.Close();
break;
}
int length = clientedSocket.Receive(ReceivedData);
string ReceivedMessage = Encoding.UTF8.GetString(ReceivedData, 0, length);
Console.WriteLine("收到了消息:" + ReceivedMessage);
//数据分发:把客户端的发来的消息分发给所有已经连接的客户端,这样的就可以实现数据的同步了
Program.BroadCastMessage(ReceivedMessage);
}
}
catch(Exception e)
{
//Console.WriteLine(e);
clientedSocket.Shutdown(SocketShutdown.Both);
clientedSocket.Close();
}
}
//封装的一个发送消息的方法
public void SendMessage(string msg)
{
byte[] SendData = Encoding.UTF8.GetBytes(msg);
clientedSocket.Send(SendData);
}
//这个方法返回自己的SOCKET是否连接。用来在分发消息的时候做判断
public bool Connect()
{
return clientedSocket.Connected;
}
}
}
客户端代码改进:
using System;
using System.Collections.Generic;
using System.Linq;
usingSystem.Net;
usingSystem.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TCP_Client
{
class Program
{
private static Socket ClientSocket;
static void Main(string[] args)
{
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ClientSocket.Connect(new IPEndPoint(IPAddress.Parse("10.198.1.12"), 9900));
//开一个线程用来专门接收消息
Thread t = new Thread(ReceiveMessage);
t.Start();
//主线程用While循环来接收输入并发送
while (true)
{
//发送消息到服务端
string SendMessage = Console.ReadLine();
byte[] SendData = Encoding.UTF8.GetBytes(SendMessage);
ClientSocket.Send(SendData);
}
}
//这是用来接收消息的方法
private static void ReceiveMessage()
{
//用一个while循环不停的接收
while (true)
{
//判断一下,如果连接断开了就不接收了
if (ClientSocket.Connected == false)
{
break;
}
//接收服务端的消息
byte[] ReceivedData = new byte[1024];
int length = ClientSocket.Receive(ReceivedData);
string ReceivedMessage = Encoding.UTF8.GetString(ReceivedData, 0, length);
Console.WriteLine(ReceivedMessage);
}
}
}
}
测试结果:
2.基于Udp协议是无连接模式通讯
占用资源少,响应速度快,延时低。至于可靠性,可通过应用层的控制来满足。(不可靠连接)
服务器端:
(1).建立一个套接字(Socket)
udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
(2).绑定服务器端IP地址及端口号–服务器端
udpClient.Bind(new IPEndPoint(IPAddress.Parse("10.198.1.237"), 7070));
(3).通过SendTo()方法向指定主机发送消息 (需提供主机IP地址及端口)
string Message = Console.ReadLine();
byte[] SendData = Encoding.UTF8.GetBytes(Message);
udpClient.SendTo(SendData, SeverEndPoint);
4).通过ReciveFrom()方法接收指定主机发送的消息 (需提供主机IP地址及端口)
//接收数据,接收所以ip地址发来的消息。端口写0是为了让他自己初始化
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
// 新建一个数据容器
byte[] ReceiveData = new byte[1024];
// 接收消息,返回一个长度
int length = udpClient.ReceiveFrom(ReceiveData, ref remoteEndPoint);
// 把接收的长度的数据转换成字符串
string ReceivedMessage = Encoding.UTF8.GetString(ReceiveData, 0, length);
Console.WriteLine(ReceivedMessage);
客户端:
(1).建立一个套接字(Socket)
(2).绑定自己的IP地址及端口号
(3).通过SendTo()方法向指定主机发送消息 (需提供主机IP地址及端口)
(4).通过ReciveFrom()方法接收指定主机发送的消息 (需提供主机IP地址及端口)
可能你们已经看出来了。udp协议即是客户端也是服务端 因为他不需要连接。只需要对方的ip和端口就能发送发送了和接收消息了
服务器完整代码:我把发来消息的客户端的socket存起来了。
using System;
using System.Collections.Generic;
using System.Linq;
usingSystem.Net;
usingSystem.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace UDP_Sever
{
class Program
{
private static Thread t;
private static Socket udp_sever;
private static List<EndPoint> Client_list=new List<EndPoint>();
private static IPEndPoint NoendPoint;
static void Main(string[] args)
{
NoendPoint=new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0);
//创建Socket
udp_sever = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//绑定IP和端口
udp_sever.Bind(new IPEndPoint(IPAddress.Parse("10.198.1.237"), 8080));
Console.WriteLine("sever is running------");
t = new Thread(ReceiveMessage);
t.Start();
Console.ReadKey();
}
private static void ReceiveMessage()
{
while(true)
{
//接收数据
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] ReceiveData = new byte[1024];
int length = udp_sever.ReceiveFrom(ReceiveData, ref remoteEndPoint);
string ReceivedMessage = Encoding.UTF8.GetString(ReceiveData, 0, length);
Console.WriteLine("从ip: " + (remoteEndPoint as IPEndPoint).Address.ToString() + ": "
+ (remoteEndPoint as IPEndPoint).Port.ToString() + "发来消息: " + ReceivedMessage);
// byte[] SendData = Encoding.UTF8.GetBytes(ReceivedMessage);
if (!Client_list.Contains(remoteEndPoint)&& (remoteEndPoint as IPEndPoint).Address.ToString()!="0.0.0.0")
{
Client_list.Add(remoteEndPoint);
}
// udp_sever.SendTo(SendData, remoteEndPoint);
broadcastMessage(ReceivedMessage);
}
}
private static void broadcastMessage(string msg)
{
byte[] SendData = Encoding.UTF8.GetBytes(msg);
foreach (EndPoint item in Client_list)
{
// Console.WriteLine("item " + item);
udp_sever.SendTo(SendData, item);
}
}
}
}
客户端完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
usingSystem.Net;
usingSystem.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace UDP_Client
{
class Program
{
private static Socket udpClient;
private static Thread T;
private static EndPoint SeverEndPoint;
static void Main(string[] args)
{
//创建Socket
udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//绑定自己的IP和端口
udpClient.Bind(new IPEndPoint(IPAddress.Parse("10.198.1.237"), 7070));
//发送数据
SeverEndPoint = new IPEndPoint(IPAddress.Parse("10.198.1.237"), 8080);
T = new Thread(ReceivedMessage);
T.Start();
while (true)
{
string Message = Console.ReadLine();
byte[] SendData = Encoding.UTF8.GetBytes(Message);
udpClient.SendTo(SendData, SeverEndPoint);
}
}
private static void ReceivedMessage()
{
while(true)
{
//接收数据
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] ReceiveData = new byte[1024];
int length = udpClient.ReceiveFrom(ReceiveData, ref remoteEndPoint);
string ReceivedMessage = Encoding.UTF8.GetString(ReceiveData, 0, length);
Console.WriteLine(ReceivedMessage);
}
}
}
}
测试结果,和tcp连接效果一样:
3.Tcp和Udp的区别
1.基于连接与无连接;
2.对系统资源的要求(TCP较多,UDP少);
3.UDP程序结构较简单;
4.流模式与数据报模式 ;
5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。