傳輸層協(xié)議
TCP協(xié)議 和 UDP協(xié)議 屬于傳輸層協(xié)議

TCP(Transmission Control Protocol 傳輸控制協(xié)議)
“面向連接”就是在正式通信前必須要與對方建立起連接。比如你給別人打電話,必須等線路接通了、對方拿起話筒才能相互通話。
TCP是基于連接的協(xié)議,也就是說,在正式收發(fā)數(shù)據(jù)前,必須和對方建立可靠的連接。一個TCP連接必須要經(jīng)過三次“對話”才能建立起來,其中的過程非常復(fù)雜,我們這里只做簡單、形象的介紹,你只要做到能夠理解這個過程即可。我們來看看這三次對話的簡單過程:[主機]A向主機B發(fā)出連接請求數(shù)據(jù)包:“我想給你發(fā)數(shù)據(jù),可以嗎?”,這是第一次對話;主機B向主機A發(fā)送同意連接和要求同步(同步就是兩臺主機一個在發(fā)送,一個在接收,協(xié)調(diào)工作)的數(shù)據(jù)包:“可以,你什么時候發(fā)?”,這是第二次對話;主機A再發(fā)出一個數(shù)據(jù)包確認主機B的要求同步:“我現(xiàn)在就發(fā),你接著吧!”,這是第三次對話。三次“對話”的目的是使數(shù)據(jù)包的發(fā)送和接收同步,經(jīng)過三次“對話”之后,[主機]A才向主機B正式發(fā)送數(shù)據(jù)。
UDP(User Data Protocol 用戶數(shù)據(jù)報協(xié)議 )
UDP是與TCP相對應(yīng)的協(xié)議。它是[面向非連接]的協(xié)議,在正式通信前不必與對方先建立連接,不管對方狀態(tài)就直接發(fā)送。與手機短信非常相似:你在發(fā)短信的時候,只需要輸入對方手機號就OK了。它不與對方建立連接,而是直接就把[數(shù)據(jù)包]發(fā)送過去!
UDP適用于一次只傳送少量數(shù)據(jù)、對可靠性要求不高的應(yīng)用環(huán)境。比如,我們經(jīng)常使用“ping”命令來測試兩臺[主機]之間TCP/IP通信是否正常,其實“ping”命令的原理就是向?qū)Ψ街鳈C發(fā)送ICMP數(shù)據(jù)包,然后對方主機確認收到數(shù)據(jù)包,如果數(shù)據(jù)包是否到達的消息及時反饋回來,那么網(wǎng)絡(luò)就是通的。例如,在默認狀態(tài)下,一次“ping”操作發(fā)送4個數(shù)據(jù)包(如圖所示)。大家可以看到,發(fā)送的數(shù)據(jù)包數(shù)量是4包,收到的也是4包(因為對方主機收到后會發(fā)回一個確認收到的數(shù)據(jù)包)。這充分說明了UDP協(xié)議是[面向非連接]的協(xié)議,沒有建立連接的過程。正因為UDP協(xié)議沒有連接的過程,所以它的通信效率高;但也正因為如此,它的可靠性不如TCP協(xié)議高。QQ就使用UDP發(fā)消息,因此有時會出現(xiàn)收不到消息的情況。
Socket(套接字)
建立網(wǎng)絡(luò)通信連接至少要一對端口號(socket)。socket本質(zhì)是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數(shù)據(jù)的具體形式;Socket是發(fā)動機,提供了網(wǎng)絡(luò)通信的能力。
應(yīng)用層通過傳輸層進行數(shù)據(jù)通信時,TCP會遇到同時為多個應(yīng)用程序進程提供并發(fā)服務(wù)的問題。多個TCP連接或多個應(yīng)用程序進程可能需要通過同一個 TCP協(xié)議端口傳輸數(shù)據(jù)。為了區(qū)別不同的應(yīng)用程序進程和連接,許多計算機操作系統(tǒng)為應(yīng)用程序與TCP/IP協(xié)議交互提供了套接字(Socket)接口。應(yīng)用層可以和傳輸層通過Socket接口,區(qū)分來自不同應(yīng)用程序進程或網(wǎng)絡(luò)連接的通信,實現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。
套接字(socket)是通信的基石,是支持TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元。它是網(wǎng)絡(luò)通信過程中端點的抽象表示,包含進行網(wǎng)絡(luò)通信必須的五種信息:連接使用的協(xié)議,本地主機的IP地址,本地進程的協(xié)議端口,遠地主機的IP地址,遠地進程的協(xié)議端口。
建立socket連接
建立Socket連接至少需要一對套接字,其中一個運行于客戶端,稱為ClientSocket ,另一個運行于服務(wù)器端,稱為ServerSocket 。
套接字之間的連接過程分為三個步驟:服務(wù)器監(jiān)聽,客戶端請求,連接確認。
服務(wù)器監(jiān)聽:服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài),實時監(jiān)控網(wǎng)絡(luò)狀態(tài),等待客戶端的連接請求。
客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務(wù)器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務(wù)器的套接字,指出服務(wù)器端套接字的地址和端口號,然后就向服務(wù)器端套接字提出連接請求。
連接確認:當(dāng)服務(wù)器端套接字監(jiān)聽到或者說接收到客戶端套接字的連接請求時,就響應(yīng)客戶端套接字的請求,建立一個新的線程,把服務(wù)器端套接字的描述發(fā)給客戶端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽狀態(tài),繼續(xù)接收其他客戶端套接字的連接請求。
TCP示例:
服務(wù)器
using System;
using System.Text;
using System.Net.Sockets;
using System.Net;
class Program
{
static void Main(string[] args)
{
IPAddress address = IPAddress.Parse("192.168.11.3");
int port = 8500;
TcpListener listener = new TcpListener(address, port);
listener.Start();
Console.WriteLine("開始偵聽");
while (true)
{
TcpClient client = listener.AcceptTcpClient();
//接收到客戶端后,交給RemoteCient去處理
RemoteClient remote = new RemoteClient(client);
}
}
}
class RemoteClient
{
public const int BuffseSize = 8192;
TcpClient tcpClient;
NetworkStream stream;
byte[] buffer;
public RemoteClient(TcpClient client)
{
this.tcpClient = client;
Console.WriteLine(client.Client.RemoteEndPoint + " -> 連接成功");
stream = client.GetStream();
buffer = new byte[BuffseSize];
//開啟異步讀取消息
stream.BeginRead(buffer, 0, BuffseSize, ReadAsync, null);
}
void ReadAsync(IAsyncResult result)
{
try
{
int readCount = stream.EndRead(result);
if (readCount == 0) throw new Exception("讀取到0字節(jié)");
string msg = Encoding.UTF8.GetString(buffer, 0, readCount);
Console.WriteLine("接收到消息 -> " + msg);
lock (stream) //再次開啟讀取
{
Array.Clear(buffer, 0, buffer.Length);
stream.BeginRead(buffer, 0, BuffseSize, ReadAsync, null);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
//發(fā)送給客戶端消息
void SendMessage(string msg)
{
byte[] temp = Encoding.UTF8.GetBytes(msg);
stream.Write(temp, 0, temp.Length);
Console.WriteLine("回復(fù)客戶端 -> " + msg);
}
}
客戶端
using System;
using System.Text;
using System.Net.Sockets;
using System.Net;
class Program
{
static void Main(string[] args)
{
SocketClient client = new SocketClient();
//發(fā)送消息
while (true)
{
string msg = Console.ReadLine();
client.SendMessage(msg);
}
}
}
public class SocketClient
{
string ip = "192.168.11.3";
int port = 8500;
NetworkStream stream;
byte[] buffer;
int BuffseSize = 8192;
public SocketClient()
{
TcpClient client = new TcpClient();
client.Connect(ip, port);
Console.WriteLine("連接服務(wù)器 -> " + client.Client.RemoteEndPoint);
stream = client.GetStream();
buffer = new byte[8192];
stream.BeginRead(buffer,0,buffer.Length, ReadAsync,null);
}
void ReadAsync(IAsyncResult result)
{
try
{
int readCount = stream.EndRead(result);
if (readCount == 0) throw new Exception("讀取到0字節(jié)");
string msg = Encoding.UTF8.GetString(buffer, 0, readCount);
Console.WriteLine("接收到消息 -> " + msg);
lock (stream) //再次開啟讀取
{
Array.Clear(buffer, 0, buffer.Length);
stream.BeginRead(buffer, 0, BuffseSize, ReadAsync, null);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
//發(fā)送消息到服務(wù)器
public void SendMessage(string msg)
{
byte[] temp = Encoding.UTF8.GetBytes(msg);
stream.Write(temp, 0, temp.Length);
Console.WriteLine("發(fā)送消息 -> " + msg);
}
}
UDP示例
服務(wù)器
using System;
class Program
{
static void Main(string[] args)
{
SocketClient client = new SocketClient();
while (true)
{
string input = Console.ReadLine();
client.Send(input);
}
}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SocketClient
{
private UdpClient udpClient;
private const int localPort = 5500;
private const string remoteIP = "127.0.0.1";
private const int remotePort = 6600;
private IPEndPoint remotePoint;
public SocketClient()
{
remotePoint = new IPEndPoint(IPAddress.Parse(remoteIP), remotePort);
udpClient = new UdpClient(localPort);
udpClient.Connect(IPAddress.Parse(remoteIP), remotePort);
udpClient.BeginReceive(OnReceive, null);
}
void OnReceive(IAsyncResult result)
{
try
{
byte[] buffer = udpClient.EndReceive(result, ref remotePoint);
OnMessage(buffer);
lock (udpClient)
{
udpClient.BeginReceive(OnReceive, null);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
void OnMessage(byte[] buffer)
{
string message = Encoding.Default.GetString(buffer);
Console.WriteLine("Receive Message:{0}", message);
}
public void Send(string message)
{
byte[] sendData = Encoding.Default.GetBytes(message);
udpClient.Send(sendData, sendData.Length);
Console.WriteLine("Send Message:{0}", message);
}
}
客戶端
using System;
class Program
{
static void Main(string[] args)
{
SocketClient client = new SocketClient();
while (true)
{
string input = Console.ReadLine();
client.Send(input);
}
}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SocketClient
{
private UdpClient udpClient;
private const int localPort = 6600;
private const string remoteIP = "127.0.0.1";
private const int remotePort = 5500;
private IPEndPoint remotePoint;
public SocketClient()
{
remotePoint = new IPEndPoint(IPAddress.Parse(remoteIP), remotePort);
udpClient = new UdpClient(localPort);
udpClient.Connect(IPAddress.Parse(remoteIP), remotePort);
udpClient.BeginReceive(OnReceive, null);
}
void OnReceive(IAsyncResult result)
{
try
{
byte[] buffer = udpClient.EndReceive(result, ref remotePoint);
OnMessage(buffer);
lock (udpClient)
{
udpClient.BeginReceive(OnReceive, null);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
void OnMessage(byte[] buffer)
{
string message = Encoding.Default.GetString(buffer);
Console.WriteLine("Receive Message:{0}", message);
}
public void Send(string message)
{
byte[] sendData = Encoding.Default.GetBytes(message);
udpClient.Send(sendData, sendData.Length);
Console.WriteLine("Send Message:{0}", message);
}
}