文章目錄
- 一、UDP介紹
- 1.Socket
- 2.TCP
- 3.UDP
- 二、C#實作HelloWorld
- 三、C#控制臺程式,利用UDP套接字實作訊息的發送
- 四、C#視窗程式,利用TCP套接字實作訊息的發送
- 五、使用Wireshark查看
- 六、小結
- 七、參考資料
本程序使用的工具:
Visual Studio 2019
Wireshark
一、UDP介紹
1.Socket
套接字是支持TCP/IP協議的網路通信的基本操作單元,可以將套接字看作不同主機間的行程進行雙向通信的端點,它構成了單個主機內及整個網路間的編程界面,
套接字的作業原理:
通過互聯網進行通信,至少需要一對套接字,其中一個運行于客戶機端,稱之為ClientSocket,另一個運行于服務器端,稱之為ServerSocket,
套接字之間的連接程序可以分為三個步驟:服務器監聽,客戶端請求,連接確認,
2.TCP
TCP協議提供的是端到端服務,TCP協議所提供的端到端的服務是保證資訊一定能夠到達目的地址,它是一種面向連接的協議,
TCP編程的服務器端一般步驟
①創建一個socket,用函式socket()
②系結IP地址、埠等資訊到socket上,用函式bind()
③開啟監聽,用函式listen()
④接收客戶端上來的連接,用函式accept()
⑤收發資料,用函式send()和recv(),或者read()和write()
⑥關閉網路連接;
⑦關閉監聽;
TCP編程的客戶端一般步驟
①創建一個socket,用函式socket()
②設定要連接的對方的IP地址和埠等屬性
③連接服務器,用函式connect()
④收發資料,用函式send()和recv(),或者read()和write()
⑤關閉網路連接

3.UDP
UDP協議提供了一種不同于TCP協議的端到端服務,UDP協議所提供的端到端傳輸服務是盡力而為(best-effort)的,即UDP套接字將盡可能地傳送資訊,但并不保證資訊一定能成功到達目的地址,而且資訊到達的順序與其發送順序不一定一致,
UDP編程的服務器端一般步驟
①創建一個socket,用函式socket()
②系結IP地址、埠等資訊到socket上,用函式bind()
③回圈接收資料,用函式recvfrom()
④關閉網路連接
UDP編程的客戶端一般步驟
①創建一個socket,用函式socket()
②設定對方的IP地址和埠等屬性
③發送資料,用函式sendto()
④關閉網路連接

通過兩種協議的介紹,可以看出對于UDP協議要比TCP簡單一些,但是,UDP不能夠保證資料傳輸一定到達目的地址,
二、C#實作HelloWorld
- C#實作控制臺helloworld
①專案創建

②撰寫代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HelloWorldConsole
{
class Program
{
static void Main(string[] args)
{
//實作helloworld陳述句的輸出,相當于C語言中的printf
Console.WriteLine("Hello World!");
}
}
}
③效果

- C#實作視窗helloworld
①專案創建

②撰寫代碼
using System;
using System.Windows.Forms;
namespace HelloworldWindow
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//通過點擊實際完成資料的添加顯示
showMsg();
}
void showMsg()
{
//向文本控制元件中添加HelloWorld
textBox1.AppendText("Hello World!" + "\t\n");
}
}
}
三、C#控制臺程式,利用UDP套接字實作訊息的發送
- 服務器端代碼
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace UDP
{
class Program
{
static void Main(string[] args)
{
int recv;
byte[] data = new byte[1024];
//得到本機IP,設定TCP埠號
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 8001);
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//系結網路地址
newsock.Bind(ip);
Console.WriteLine("This is a Server, host name is {0}", Dns.GetHostName());
//等待客戶機連接
Console.WriteLine("Waiting for a client");
//得到客戶機IP
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine("Message received from {0}: ", Remote.ToString());
Console.WriteLine(Encoding.UTF8.GetString(data, 0, recv));
//客戶機連接成功后,發送資訊
string welcome = "你好 ! ";
//字串與位元組陣列相互轉換
data = Encoding.UTF8.GetBytes(welcome);
//發送資訊
newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
while (true)
{
data = new byte[1024];
//接收資訊
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine(Encoding.UTF8.GetString(data, 0, recv));
//newsock.SendTo(data, recv, SocketFlags.None, Remote);
}
}
}
}
步驟:
①實體化并設定socket實體物件
創建ip地址和埠:
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 8001);
系結監聽地址:
newsock.Bind(ip);
②連接
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);
獲取客戶端的ip,來建立聯系
③接收客戶端的發送過來的訊息
recv = newsock.ReceiveFrom(data, ref Remote);
- 客戶端代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace UDPClient
{
class Program
{
static void Main(string[] args)
{
byte[] data = new byte[1024];
string input, stringData;
//構建TCP 服務器
Console.WriteLine("This is a Client, host name is {0}", Dns.GetHostName());
//設定服務IP(這個IP地址是服務器的IP),設定TCP埠號
IPEndPoint ip = new IPEndPoint(IPAddress.Parse("192.168.43.98"), 8001);
//定義網路型別,資料連接型別和網路協議UDP
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
string welcome = "你好! ";
data = Encoding.UTF8.GetBytes(welcome);
server.SendTo(data, data.Length, SocketFlags.None, ip);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)sender;
data = new byte[1024];
//對于不存在的IP地址,加入此行代碼后,可以在指定時間內解除阻塞模式限制
int recv = server.ReceiveFrom(data, ref Remote);
Console.WriteLine("Message received from {0}: ", Remote.ToString());
Console.WriteLine(Encoding.UTF8.GetString(data, 0, recv));
int i = 0;
while (true)
{
string s = "hello cqjtu!重交物聯2018級"+i;
Console.WriteLine(s);
server.SendTo(Encoding.UTF8.GetBytes(s),Remote);
if(i==50)
{
break;
}
i++;
}
Console.WriteLine("Stopping Client.");
server.Close();
}
}
}
客戶端程式與服務器程式非常類似,由于客戶機不需要在指定的UDP埠等待流入的資料,所以不使用Bind()方法來系結監聽地址,而是在資料發送時使用系統隨機指定的一個UDP埠,
- 實作客戶端向服務器發送訊息效果(訊息一共50條,只截取部分內容)
服務器接收到客戶端發送的訊息:

客戶端發送訊息內容:

四、C#視窗程式,利用TCP套接字實作訊息的發送
- 單執行緒
服務器代碼:
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Threading;
using System.IO;
namespace WindowsServer2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
//點擊開始偵聽的時候,服務器創建一個負責監聽IP地址跟埠號的Socket
Socket socketwatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Any;
//創建埠物件
IPEndPoint point = new IPEndPoint(ip, 8001);
//系結
socketwatch.Bind(point);
showMsg("監聽成功!");
socketwatch.Listen(10);
//等待客戶端的連接
Socket socketSend=socketwatch.Accept();
showMsg(socketSend.RemoteEndPoint.ToString() + ":" + "連接成功!");
}
catch
{
showMsg("監聽失敗");
}
}
void showMsg(string str)
{
textBox1.AppendText(str + "\r\n");
}
}
}
測驗是否能夠連接到客戶端
方法:在Windows下,打開cmd,輸入telnet 你自己服務器IP地址 埠號

但是這樣只能連接一次客戶端,并且會發生卡住的情況,
- 多執行緒
服務器代碼:
using System;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Threading;
namespace WindowsServer2
{
public partial class server : Form
{
public server()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
//點擊開始偵聽的時候,服務器創建一個負責監聽IP地址跟埠號的Socket
Socket socketwatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Any;
//創建埠物件
IPEndPoint point = new IPEndPoint(ip, 8001);
//系結
socketwatch.Bind(point);
showMsg("監聽成功!");
socketwatch.Listen(10);
//創建一個執行緒
Thread th = new Thread(Listen);
th.IsBackground = true;
th.Start(socketwatch);
}
catch
{
showMsg("監聽失敗");
}
}
Socket socketSend;
//等待客戶端的連接
void Listen(Object o)
{
try
{
Socket socketwatch = o as Socket;
while (true)
{
//等待客戶端的連接
socketSend = socketwatch.Accept();
showMsg(socketSend.RemoteEndPoint.ToString() + ":" + "連接成功!");
//開啟一個新的執行緒不斷的接收客戶端資訊
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start(socketSend);
}
}
catch
{ }
}
//接收客戶端發送的資訊
void Receive(Object o)
{
try
{
Socket socketSend = o as Socket;
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 2];
int r = socketSend.Receive(buffer);
if (r == 0)
{
break;
}
string str = Encoding.UTF8.GetString(buffer, 0, r);
showMsg(socketSend.RemoteEndPoint + ":"+str);
}
}
catch
{ }
}
void showMsg(string str)
{
textBox1.AppendText(str + "\r\n");
}
//服務器給客戶端發送訊息
private void button2_Click(object sender, EventArgs e)
{
string str = textBox2.Text;
Console.WriteLine(str);
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
socketSend.Send(buffer);
}
}
}
客戶端代碼:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Windowsclient2
{
public partial class client : Form
{
public client()
{
InitializeComponent();
}
Socket socketSend;
private void button1_Click(object sender, EventArgs e)
{
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint point =new IPEndPoint(IPAddress.Parse("192.168.43.98"), 8001);
socketSend.Connect(point);
showMsg("連接成功!");
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start();
}
//客戶端接收服務器發送的訊息
void Receive()
{
try
{
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 2];
int r = socketSend.Receive(buffer);
if (r == 0)
{
break;
}
string str = Encoding.UTF8.GetString(buffer, 0, r);
showMsg(socketSend.RemoteEndPoint + ":"+str);
}
}
catch
{ }
}
void showMsg(string s)
{
textBox1.AppendText(s + "\r\n");
}
//客戶端向服務器發送訊息
private void button2_Click(object sender, EventArgs e)
{
string str = textBox2.Text;
int j;
for (j = 0; j < 50; j++)
{
string str1 = str + j;
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str1);
socketSend.Send(buffer);
}
}
}
}
效果展示(本次展示是在同一臺電腦上完成的):

客戶端與服務器連接成功后,客戶端向服務器發送hello world后,服務器就會接收到50條hello world,這個程序,可以將服務器代碼放在其他電腦上,將客戶端中獲取服務器的IP地址改為對于電腦的IP地址,并且埠號要與服務器上的埠號一致,或者將客戶端的代碼放到其他電腦上,代碼不需要進行改變,
說明:
該程序需要先運行服務器端的代碼,在運行客戶端的代碼,否則,將會出現問題,代碼中的IP地址是我電腦的IP地址,如果你使用,需要修改為你電腦對應的IP地址,UDP協議只需要將對應的Socket物件的協議修改成UDP,SocketType改為Dgram,以及接收和發送修改為ReceiveFrom和SendTo,并使用對應的引數即可,(這是筆者的想法)
五、使用Wireshark查看

通過圖片我們可以看出來在網路層面IP頭部包的各個部分,其中最為重要的便是目的IP和源IP,資料就是通過這兩個資訊來查找到路徑,進行資料的傳輸,采用的協議是TCP協議,同時,還可以查看幀格式,在幀格式上包含目的MAC和源MAC,看到資料型別為IP,
六、小結
在這次C#應用兩種UDP和TCP兩種協議進行編程,可以看出兩種協議具有各自的特點,在使用Socket的程序上,參考了比較多的資料,已經比較清楚知道該怎么使用Socket,本來后面視窗的代碼是應該應用UDP來實作,但是,由于自己找到的資料是講解的TCP的使用,所以在這里就以TCP來實作的,對比上面使用UDP的控制臺程式,可以看出對于視窗的UDP來說,只是將整個輸出的顯示方式改變,對于視窗程式只需要將界面設計好后,將代碼中輸出提示資訊和接收訊息后進行顯示的陳述句修改為以視窗控制元件的形式來進行展示,這個只是筆者自己的想法,還沒有進行驗證,有興趣的可以嘗試一下,
七、參考資料
- c# 實作socket 聊天程式 互發訊息0
- C#網路編程(二):Socket編程
- C# Socket實作簡單控制臺案例
- 基于C#的UDP協議的同步實作
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/198962.html
標籤:其他
