C# Socket網(wǎng)絡(luò)編程實(shí)例
本文實(shí)例講述了C# Socket網(wǎng)絡(luò)編程技巧。分享給大家供大家參考。具體分析如下:
客戶端要連接服務(wù)器:首先要知道服務(wù)器的IP地址。而服務(wù)器里有很多的應(yīng)用程序,每一個(gè)應(yīng)用程序?qū)?yīng)一個(gè)端口號(hào)
所以客戶端想要與服務(wù)器中的某個(gè)應(yīng)用程序進(jìn)行通信就必須要知道那個(gè)應(yīng)用程序的所在服務(wù)器的IP地址,及應(yīng)用程序所對(duì)應(yīng)的端口號(hào)
TCP協(xié)議:安全穩(wěn)定,一般不會(huì)發(fā)生數(shù)據(jù)丟失,但是效率低。利用TCP發(fā)生數(shù)據(jù)一般經(jīng)過3次握手(所有效率低,自己百度三次握手)
UDP協(xié)議:快速,效率高,但是不穩(wěn)定,容易發(fā)生數(shù)據(jù)丟失(沒有經(jīng)過三次握手,不管服務(wù)器有空沒空,信息全往服務(wù)器發(fā),所有效率搞,但服務(wù)器忙的時(shí)候就沒辦法處理你的數(shù)據(jù),容易造成數(shù)據(jù)丟失,不穩(wěn)定)
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace Socket通信
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.txtPort.Text = "5000";
this.txtIp.Text = "192.168.137.1";
}
private void btnStart_Click(object sender, EventArgs e)
{
//當(dāng)點(diǎn)擊開始監(jiān)聽的時(shí)候,在服務(wù)器端創(chuàng)建一個(gè)負(fù)責(zé)監(jiān)聽I(yíng)P地址跟端口號(hào)的Socket
Socket socketWatch = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
//Any:提供一個(gè) IP 地址,指示服務(wù)器應(yīng)偵聽所有網(wǎng)絡(luò)接口上的客戶端活動(dòng)。此字段為只讀。
IPAddress ip = IPAddress.Any;
//創(chuàng)建端口號(hào)對(duì)象;將txtPort.Text控件的值設(shè)為服務(wù)端的端口號(hào)
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
//監(jiān)聽
socketWatch.Bind(point);
ShowMsg("監(jiān)聽成功");
socketWatch.Listen(10);//連接隊(duì)列的最大長(zhǎng)度 ;即:一個(gè)時(shí)間點(diǎn)內(nèi)最大能讓幾個(gè)客戶端連接進(jìn)來,超過長(zhǎng)度就進(jìn)行排隊(duì)
//等待客戶端連接;Accept()這個(gè)方法能接收客戶端的連接,并為新連接創(chuàng)建一個(gè)負(fù)責(zé)通信的Socket
Thread th = new Thread(Listen); //被線程執(zhí)行的方法如果有參數(shù)的話,參數(shù)必須是object類型
Control.CheckForIllegalCrossThreadCalls = false; //因?yàn)?net不允許跨線程訪問的,所以這里取消跨線程的檢查。.net不檢查是否有跨線程訪問了,所有就不會(huì)報(bào): “從不是創(chuàng)建控件“txtLog”的線程訪問它” 這個(gè)錯(cuò)誤了,從而實(shí)現(xiàn)了跨線程訪問
th.IsBackground = true; //將th這個(gè)線程設(shè)為后臺(tái)線程。
//Start(object parameter); parameter:一個(gè)對(duì)象,包含線程執(zhí)行的方法要使用的數(shù)據(jù),即線程執(zhí)行Listen方法,Listen的參數(shù)
th.Start(socketWatch); //這個(gè)括號(hào)里的參數(shù)其實(shí)是Listen()方法的參數(shù)。因?yàn)門hread th = new Thread(Listen)這個(gè)括號(hào)里只能寫方法名(函數(shù)名) 但是Listen()方法是有參數(shù)的,所有就要用Start()方法將它的參數(shù)添加進(jìn)來
}
/// <summary>
/// 等待客戶端連接,如果監(jiān)控到有客戶端連接進(jìn)來就創(chuàng)建一個(gè)與之通信的Socket
/// </summary>
/// <param name="o"></param>
void Listen(object o) //這里為什么不直接傳遞Socket類型的參數(shù)呢? 原因是:被線程執(zhí)行的方法如果有參數(shù)的話,參數(shù)必須是object類型
{
Socket socketWatch = o as Socket;
while (true) //為什么這里要有個(gè)while循環(huán)?因?yàn)楫?dāng)一個(gè)人連接進(jìn)來的時(shí)候創(chuàng)建了與之通信的Socket后就程序就會(huì)往下執(zhí)行了,就不會(huì)再回來為第二個(gè)人的連接創(chuàng)建負(fù)責(zé)通信的Socket了。(應(yīng)該是每個(gè)人(每個(gè)客戶端)創(chuàng)建一個(gè)與之通信的Socket)所以要寫在循環(huán)里。
{
//等待客戶端連接;Accept()這個(gè)方法能接收客戶端的連接,并為新連接創(chuàng)建一個(gè)負(fù)責(zé)通信的Socket
Socket socketSend = socketWatch.Accept();
dic.Add(socketSend.RemoteEndPoint.ToString(), socketSend); //(根據(jù)客戶端的IP地址和端口號(hào)找負(fù)責(zé)通信的Socket,每個(gè)客戶端對(duì)應(yīng)一個(gè)負(fù)責(zé)通信的Socket),ip地址及端口號(hào)作為鍵,將負(fù)責(zé)通信的Socket作為值填充到dic鍵值對(duì)中。
//我們通過負(fù)責(zé)通信的這個(gè)socketSend對(duì)象的一個(gè)RemoteEndPoint屬性,能夠拿到遠(yuǎn)程連過來的客戶端的Ip地址跟端口號(hào)
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + "連接成功");//效果:192.168.1.32:連接成功
comboBox1.Items.Add(socketSend.RemoteEndPoint.ToString()); //將連接過來的每個(gè)客戶端都添加到combBox控件中。
//客戶端連接成功后,服務(wù)器應(yīng)該接收客戶端發(fā)來的消息。
Thread getdata = new Thread(GetData);
getdata.IsBackground = true;
getdata.Start(socketSend);
}
}
Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
/// <summary>
/// 不停的接收客戶端發(fā)送過來的消息
/// </summary>
/// <param name="o"></param>
void GetData(object o)
{
while (true)
{
Socket socketSend = o as Socket;
//將客戶端發(fā)過來的數(shù)據(jù)先放到一個(gè)字節(jié)數(shù)組里面去
byte[] buffer = new byte[1024 * 1024 * 2]; //創(chuàng)建一個(gè)字節(jié)數(shù)組,字節(jié)數(shù)組的長(zhǎng)度為2M
//實(shí)際接收到的有效字節(jié)數(shù); (利用Receive方法接收客戶端傳過來的數(shù)據(jù),然后把數(shù)據(jù)保存到buffer字節(jié)數(shù)組中,返回一個(gè)接收到的數(shù)據(jù)的長(zhǎng)度)
int r = socketSend.Receive(buffer);
if (r == 0) //如果接收到的有效字節(jié)數(shù)為0 說明客戶端已經(jīng)關(guān)閉了。這時(shí)候就跳出循環(huán)了。
{
//只有客戶端給用戶發(fā)消息,不可能是發(fā)0個(gè)長(zhǎng)度的字節(jié)。即便發(fā)空消息,空消息也是有過個(gè)長(zhǎng)度的。所有接收到的有效字節(jié)長(zhǎng)度為0就代表客戶端已經(jīng)關(guān)閉了
break;
}
//將buffer這個(gè)字節(jié)數(shù)組里面的數(shù)據(jù)按照UTF8的編碼,解碼成我們能夠讀懂的的string類型,因?yàn)閎uffer這個(gè)數(shù)組的實(shí)際存儲(chǔ)數(shù)據(jù)的長(zhǎng)度是r個(gè) ,所以從索引為0的字節(jié)開始解碼,總共解碼r個(gè)字節(jié)長(zhǎng)度。
string str = Encoding.UTF8.GetString(buffer, 0, r);
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str);
}
}
private void ShowMsg(string str)
{
txtLog.AppendText(str + "\r\n"); //將str這個(gè)字符串添加到txtLog這個(gè)文本框中。
}
/// <summary>
/// 服務(wù)端給客戶端發(fā)信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click_1(object sender, EventArgs e)
{
if (comboBox1.SelectedItem == null) //如果comboBox控件沒有選中值。就提示用戶選擇客戶端
{
MessageBox.Show("請(qǐng)選擇客戶端");
return;
}
string str = txtMes.Text; //獲取用戶輸入的內(nèi)容 (服務(wù)端要給客戶端發(fā)的信息)
byte[] strByte = Encoding.Default.GetBytes(str); //將信息轉(zhuǎn)換成二進(jìn)制字節(jié)數(shù)組
string getIp = comboBox1.SelectedItem as string; //comboBox存儲(chǔ)的是客戶端的(ip+端口號(hào))
Socket socketSend = dic[getIp] as Socket; //根據(jù)這個(gè)(ip及端口號(hào))去dic鍵值對(duì)中找對(duì)應(yīng) 賦值與客戶端通信的Socket【每個(gè)客戶端都有一個(gè)負(fù)責(zé)與之通信的Socket】
socketSend.Send(strByte); //將信息發(fā)送到客戶端
}
}
}
開打開始命令 cmd telnet 10.18.16.46 5000 即telnet 服務(wù)器ip地址 綁定的端口號(hào)
希望本文所述對(duì)大家的C#程序設(shè)計(jì)有所幫助。
相關(guān)文章
C#使用OpenCvSharp實(shí)現(xiàn)透視變換功能
這篇文章主要為大家詳細(xì)介紹了C#如何使用OpenCvSharp實(shí)現(xiàn)透視變換的功能,文中的示例代碼簡(jiǎn)潔易懂,具有一定的學(xué)習(xí)價(jià)值,需要的小伙伴可以參考下2023-11-11C# SendMail發(fā)送郵件功能實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了C# SendMail發(fā)送郵件功能實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06C#實(shí)現(xiàn)給DevExpress中GridView表格指定列添加進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)給DevExpress中GridView表格指定列添加進(jìn)度條顯示效果,感興趣的小伙伴可以嘗試一下2022-06-06C#利用FluentFTP實(shí)現(xiàn)FTP上傳下載功能詳解
FTP作為日常工作學(xué)習(xí)中,非常重要的一個(gè)文件傳輸存儲(chǔ)空間,想必大家都非常的熟悉了,那么如何快速的實(shí)現(xiàn)文件的上傳下載功能呢,本文以一個(gè)簡(jiǎn)單的小例子,簡(jiǎn)述如何通過FluentFTP實(shí)現(xiàn)文件的上傳和下載功能2023-02-02C#如何將List<string>轉(zhuǎn)換為L(zhǎng)ist<double>
這篇文章主要介紹了C#如何將List<string>轉(zhuǎn)換為L(zhǎng)ist<double>問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07解析美國(guó)東部時(shí)間與北京時(shí)間相互轉(zhuǎn)換的實(shí)現(xiàn)代碼
本篇文章是對(duì)美國(guó)東部時(shí)間與北京時(shí)間相互轉(zhuǎn)換的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05