蓝桥的前一天,算法题实在写不下去了,太无聊了,拿起键盘记录一下之前学过的东西,本系列主要介绍基于C#/Socket的网络编程知识(告诉你们一个秘密:我学这个主要是为了游戏前后端通信)。
网络编程主要是基于传输层的两个协议:TCP和UDP,中文翻译过来,TCP叫做传输控制协议,UDP叫做用户数据报协议。
先简单介绍一下TCP:它是一种有连接,面向字节流,可靠的协议,为了保证可靠和效率问题,有一大堆机制在保障,什么
停止等待协议,Karn算法,Nagle算法,Clark算法,三次握手,四次挥手等,这里就不在详细介绍了。
UDP:它是一种无连接,面向报文段,不可靠的协议(我个人还是比较喜欢它的,它的传输速度比tcp快多了,而且没有TCP的Nagle算法等一堆提升效率的算法带来的屁事)。
公有ip地址:可以唯一标识一台主机
私有ip地址:可以在该主机所在的局域网中唯一标识这台主机
端口号:可以标识特定的进程,一个端口号只能被一个进程占用。
所以,公有ip + 端口号就可以标识特定主机的特定进程,可以基于这个原理进行网络通信(就是我给x主机的y进程发消息只需要知道x主机的公有ip和y进程绑定的端口号就可以了)。
首先是通TCP以同步的方式实现网络通信:(所谓同步就是指接收方调用相应接收方法的线程会一直被阻塞知道接收到相应信息或发生异常),注意TCP是要建立与客户端的连接的,它是有连接的,它是有连接的,它是有连接的,重要的事情说三遍
基本流程就是:建立端口(IPEndPoint对象),建立Socket对象,绑定端口,然后是Listen(),括号中填的是可以建立的连接数量,0表示不限制,然后开始Accept(),这时调用Accept的线程被阻塞,直到接收到客户端的连接后,该线程重新继续运行,Accept()返回的是与服务器端建立的连接(Socket类型,就是客户端的ip和端口,通过这个我们就直到我们的服务器端要和谁通信了),然后调用Send或Receive方法就可以进行收发消息了,注意Receive也会阻塞调用它的线程,注意:当收到的数据长度为0的时候就说明和客户端断开连接了
这里在建立Socket的时候,由于是TCP,所以要选Stream(字节流)和TCP
服务器端:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
namespace TCPServer
{
class Program
{
static void Main(string[] args)
{
int recv;//客户端发送信息的长度
byte[] data = new byte[1024];//缓存客户端发送的信息,必须以byte为单位
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 80);//本机预使用的ip和端口
Socket newsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
newsocket.Bind(ipep);
newsocket.Listen(10);
Console.WriteLine("wait for a client......");
Socket client = newsocket.Accept();//当客户端连接尝试时执行,并返回一个 新的socket,用于与客户端之间通信
IPEndPoint clientip = (IPEndPoint)client.RemoteEndPoint;
Console.WriteLine("connect with client" + clientip.Address + " at port:" + clientip.Port);
string welcome = "welcome here!";
data = Encoding.Unicode.GetBytes(welcome);
client.Send(data, data.Length, SocketFlags.None);
while(true)
{
data = new byte[1024];
recv = client.Receive(data);
Console.WriteLine("成功接收长度为" + recv+"的数据:");
if (recv == 0)
break;
Console.WriteLine(Encoding.Unicode.GetString(data, 0, recv));
client.Send(data, recv, SocketFlags.None);
}
Console.WriteLine("Disconnected from" + clientip.Address);
client.Close();
newsocket.Close();
}
}
}
客户端也差不多,只不过没有Listen,也不是Accept,而是Connect,进行连接
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace TCPClient
{
class Program
{
static void Main(string[] args)
{
byte[] data = new byte[1024];
Socket newclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine("Input the server ip here,please:");
string ipadd = Console.ReadLine();
Console.WriteLine();
Console.WriteLine("Input the server port:");
int port = Convert.ToInt32(Console.ReadLine());
IPEndPoint ie = new IPEndPoint(IPAddress.Parse(ipadd), port);//服务器的ip地址和端口
try
{
//客户端只是向服务器端发送信息的,不需要监听
newclient.Connect(ie);
}
catch(SocketException e)
{
Console.WriteLine("Unable to connect a server");
Console.WriteLine(e.ToString());
Console.ReadLine();
Console.ReadLine();
Console.ReadLine();
return;
}
int recv = newclient.Receive(data);
string stringdata = Encoding.Unicode.GetString(data, 0, recv);
Console.WriteLine(stringdata);
while(true)
{
string input = Console.ReadLine();
if (input == "exit")
break;
newclient.Send(Encoding.Unicode.GetBytes(input));
data = new byte[1024];
recv = newclient.Receive(data);
stringdata = Encoding.Unicode.GetString(data, 0, recv);
if (newclient.Poll(1000, SelectMode.SelectRead))
break;
Console.WriteLine("成功发送:"+stringdata);
}
Console.WriteLine("disconnect from server......");
newclient.Shutdown(SocketShutdown.Both);
newclient.Close();
}
}
}