C# Socket网络编程实例
作者:shichen2014 发布时间:2023-03-18 05:09:28
标签:C#,Socket
本文实例讲述了C# Socket网络编程技巧。分享给大家供大家参考。具体分析如下:
客户端要连接服务器:首先要知道服务器的IP地址。而服务器里有很多的应用程序,每一个应用程序对应一个端口号
所以客户端想要与服务器中的某个应用程序进行通信就必须要知道那个应用程序的所在服务器的IP地址,及应用程序所对应的端口号
TCP协议:安全稳定,一般不会发生数据丢失,但是效率低。利用TCP发生数据一般经过3次握手(所有效率低,自己百度三次握手)
UDP协议:快速,效率高,但是不稳定,容易发生数据丢失(没有经过三次握手,不管服务器有空没空,信息全往服务器发,所有效率搞,但服务器忙的时候就没办法处理你的数据,容易造成数据丢失,不稳定)
using System;
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)
{
//当点击开始监听的时候,在服务器端创建一个负责监听IP地址跟端口号的Socket
Socket socketWatch = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
//Any:提供一个 IP 地址,指示服务器应侦听所有网络接口上的客户端活动。此字段为只读。
IPAddress ip = IPAddress.Any;
//创建端口号对象;将txtPort.Text控件的值设为服务端的端口号
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
//监听
socketWatch.Bind(point);
ShowMsg("监听成功");
socketWatch.Listen(10);//连接队列的最大长度 ;即:一个时间点内最大能让几个客户端连接进来,超过长度就进行排队
//等待客户端连接;Accept()这个方法能接收客户端的连接,并为新连接创建一个负责通信的Socket
Thread th = new Thread(Listen); //被线程执行的方法如果有参数的话,参数必须是object类型
Control.CheckForIllegalCrossThreadCalls = false; //因为.net不允许跨线程访问的,所以这里取消跨线程的检查。.net不检查是否有跨线程访问了,所有就不会报: “从不是创建控件“txtLog”的线程访问它” 这个错误了,从而实现了跨线程访问
th.IsBackground = true; //将th这个线程设为后台线程。
//Start(object parameter); parameter:一个对象,包含线程执行的方法要使用的数据,即线程执行Listen方法,Listen的参数
th.Start(socketWatch); //这个括号里的参数其实是Listen()方法的参数。因为Thread th = new Thread(Listen)这个括号里只能写方法名(函数名) 但是Listen()方法是有参数的,所有就要用Start()方法将它的参数添加进来
}
/// <summary>
/// 等待客户端连接,如果监控到有客户端连接进来就创建一个与之通信的Socket
/// </summary>
/// <param name="o"></param>
void Listen(object o) //这里为什么不直接传递Socket类型的参数呢? 原因是:被线程执行的方法如果有参数的话,参数必须是object类型
{
Socket socketWatch = o as Socket;
while (true) //为什么这里要有个while循环?因为当一个人连接进来的时候创建了与之通信的Socket后就程序就会往下执行了,就不会再回来为第二个人的连接创建负责通信的Socket了。(应该是每个人(每个客户端)创建一个与之通信的Socket)所以要写在循环里。
{
//等待客户端连接;Accept()这个方法能接收客户端的连接,并为新连接创建一个负责通信的Socket
Socket socketSend = socketWatch.Accept();
dic.Add(socketSend.RemoteEndPoint.ToString(), socketSend); //(根据客户端的IP地址和端口号找负责通信的Socket,每个客户端对应一个负责通信的Socket),ip地址及端口号作为键,将负责通信的Socket作为值填充到dic键值对中。
//我们通过负责通信的这个socketSend对象的一个RemoteEndPoint属性,能够拿到远程连过来的客户端的Ip地址跟端口号
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + "连接成功");//效果:192.168.1.32:连接成功
comboBox1.Items.Add(socketSend.RemoteEndPoint.ToString()); //将连接过来的每个客户端都添加到combBox控件中。
//客户端连接成功后,服务器应该接收客户端发来的消息。
Thread getdata = new Thread(GetData);
getdata.IsBackground = true;
getdata.Start(socketSend);
}
}
Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
/// <summary>
/// 不停的接收客户端发送过来的消息
/// </summary>
/// <param name="o"></param>
void GetData(object o)
{
while (true)
{
Socket socketSend = o as Socket;
//将客户端发过来的数据先放到一个字节数组里面去
byte[] buffer = new byte[1024 * 1024 * 2]; //创建一个字节数组,字节数组的长度为2M
//实际接收到的有效字节数; (利用Receive方法接收客户端传过来的数据,然后把数据保存到buffer字节数组中,返回一个接收到的数据的长度)
int r = socketSend.Receive(buffer);
if (r == 0) //如果接收到的有效字节数为0 说明客户端已经关闭了。这时候就跳出循环了。
{
//只有客户端给用户发消息,不可能是发0个长度的字节。即便发空消息,空消息也是有过个长度的。所有接收到的有效字节长度为0就代表客户端已经关闭了
break;
}
//将buffer这个字节数组里面的数据按照UTF8的编码,解码成我们能够读懂的的string类型,因为buffer这个数组的实际存储数据的长度是r个 ,所以从索引为0的字节开始解码,总共解码r个字节长度。
string str = Encoding.UTF8.GetString(buffer, 0, r);
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str);
}
}
private void ShowMsg(string str)
{
txtLog.AppendText(str + "\r\n"); //将str这个字符串添加到txtLog这个文本框中。
}
/// <summary>
/// 服务端给客户端发信息
/// </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("请选择客户端");
return;
}
string str = txtMes.Text; //获取用户输入的内容 (服务端要给客户端发的信息)
byte[] strByte = Encoding.Default.GetBytes(str); //将信息转换成二进制字节数组
string getIp = comboBox1.SelectedItem as string; //comboBox存储的是客户端的(ip+端口号)
Socket socketSend = dic[getIp] as Socket; //根据这个(ip及端口号)去dic键值对中找对应 赋值与客户端通信的Socket【每个客户端都有一个负责与之通信的Socket】
socketSend.Send(strByte); //将信息发送到客户端
}
}
}
开打开始命令 cmd telnet 10.18.16.46 5000 即telnet 服务器ip地址 绑定的端口号
希望本文所述对大家的C#程序设计有所帮助。


猜你喜欢
- 【开发环境】物理机版本:Win7旗舰版(64位)Android Studio版本:2.1正式版【Android Studio的优势】•基于G
- 说明:在填写表数据时当输入完一个文本框后,输入下一个文本框时需要用Tab键切换,但是有的人喜欢用Enter键切换下一个,此方法是Enter取
- 错误信息Exception in thread "main" java.lang.ClassCastException:
- 今天来给大家介绍一个非常有用的Studio Tips,有些时候我们在一个方法内部写了过多的代码,然后想要把一些代码提取出来再放在一个单独的方
- 1 线程池的优势总体来说,线程池有如下的优势:(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。(2)提高响应速度。
- Intellij IDEA 公司 JetBrains 推出了一种新字体:JetBrains Mono,它是专为开发人员设计的。为什么说它是专
- 为了实现毛玻璃效果,我们需要一组compute kernels(.rs文件中编写),及一组用于控制renderScript相关的Javaap
- java volatile关键字在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多
- 本文实例讲述了C#使用windows服务开启应用程序的方法。分享给大家供大家参考。具体如下:使用windows服务开启应用程序,会遇到如下问
- 对象类型转换分为向上转型和向下转型(强制对象转型)。 向上转型是子对象向父对象转型的过程,例如猫类转换为动物类;向下转型是强制转型实现的,是
- SpringBoot中的过滤器 * 操作与springmvc中的几乎一样所以这里也不过多介绍了,下面举两
- springboot 启动项目打印接口列表环境springboot 2.3.2.RELEASE修改配置文件logging: le
- 一、分布式锁介绍分布式锁主要用于在分布式环境中保护跨进程、跨主机、跨网络的共享资源实现互斥访问,以达到保证数据的一致性。二、架构介绍&nbs
- 问题注意:本人使用的Spring Boot 2.0.2, 对1.5.x系列未必有用。官方文档在这里直接解决办法0, 移除spring-boo
- 先明确几个概念的区别: padding margin都是边距的含义,关键问题得明白是什么相对什么的边距. padding是控件的内容相对控件
- Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,它有两种参数形式: public static
- 死锁在多线程的情况下,会出现数据不同步情况, 而为了避免这种情况,之前也说了:界区实现方法有两种,一种是用synchronized,一种是用
- 本文实例讲述了Java实现文件和base64流的相互转换功能。分享给大家供大家参考,具体如下:import java.io.FileInpu
- 本文实例为大家分享了Unity封装延时调用定时器的具体代码,供大家参考,具体内容如下封装一个延时调用定时器类using System.Col
- 一、deleteById 和 delete为什么要把这两个方法放在一起呢?我们先看源码再说deleteById(Id id)(通过id进行删