参考:http://www.cnblogs.com/jzxx/p/5630516.html
一、原作者的这段话很好,先引用一下:
Socket的Send方法,并非大家想象中的从一个端口发送消息到另一个端口,它仅仅是拷贝数据到基础系统的发送缓冲区,然后由基础系统将发送缓冲区的数据到连接的另一端口。值得一说的是,这里的拷贝数据与异步发送消息的拷贝是不一样的,同步发送的拷贝,是直接拷贝数据到基础系统缓冲区,拷贝完成后返回,在拷贝的过程中,执行线程会IO等待, 此种拷贝与Socket自带的Buffer空间无关,但异步发送消息的拷贝,是将Socket自带的Buffer空间内的所有数据,拷贝到基础系统发送缓冲区,并立即返回,执行线程无需IO等待,所以异步发送在发送前必须执行SetBuffer方法,拷贝完成后,会触发你自定义回调函数ProcessSend,在ProcessSend方法中,调用SetBuffer方法,重新初始化Buffer空间。
二、代码如下:
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;using System.Net;using System.Net.Sockets;using System.IO;using System.Threading;namespace TcpClientTest{ public partial class FormMain : Form { public FormMain() { InitializeComponent(); } private void FormMain_Load(object sender, EventArgs e) { //初始化控件 txtSendMssg.Text = "测试数据"; //打开Listener开始监听 Thread thrListener = new Thread(new ThreadStart(Listen)); thrListener.Start(); } private void FormMain_FormClosing(object sender, FormClosingEventArgs e) { //强制关闭程序(强行终止Listener) Environment.Exit(0); } //发送数据 private void btnSend_Click(object sender, EventArgs e) { TcpClient tcpClient = new TcpClient(); //tcpClient.Connect(IPAddress.Parse("170.0.0.78"), 2014); tcpClient.Connect(IPAddress.Parse("127.0.0.1"), 2014); NetworkStream ntwStream = tcpClient.GetStream(); if (ntwStream.CanWrite) { Byte[] bytSend = Encoding.UTF8.GetBytes(txtSendMssg.Text); ntwStream.Write(bytSend, 0, bytSend.Length); } else { MessageBox.Show("无法写入数据流"); ntwStream.Close(); tcpClient.Close(); return; } ntwStream.Close(); tcpClient.Close(); } //监听数据 private void Listen() { Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); listener.Bind(new IPEndPoint(IPAddress.Any, 2014)); //不断监听端口 while (true) { listener.Listen(0); Socket socket = listener.Accept(); NetworkStream ntwStream = new NetworkStream(socket); StreamReader strmReader = new StreamReader(ntwStream); Invoke(new PrintRecvMssgDelegate(PrintRecvMssg), new object[] { strmReader.ReadToEnd() }); socket.Close(); } //程序的listener一直不关闭 //listener.Close(); } //线程内向文本框txtRecvMssg中添加字符串及委托 private delegate void PrintRecvMssgDelegate(string s); private void PrintRecvMssg(string info) { txtRecvMssg.Text += string.Format("[{0}]:{1}\r\n", DateTime.Now.ToLongTimeString(), info); } }}
需要在项目上加两个TextBox(名字分别为 txtSendMssg、txtRecvMssg)和一个Button(名字为 btnSend)
三、运行效果(效果图片见原作者文章)
在发送数据的文本框中分别输入“千山鸟飞绝”、“万径人踪灭”、“孤舟蓑笠翁”、“独钓寒江雪”四句话,输完一句话,单击一次“发送数据”按钮,就可以在接收数据里看到这四句话了。上面代码中,信息的发送时通过TcpClient连接到127.0.0.1的2014端口,信息的接收是通过Listen函数不断监听本机的2014端口实现的。从自己创建的线程中修改控件信息,用到了委托。
四、补充
1、运行时提示:由于目标计算机积极拒绝,无法连接。 127.0.0.1:2014
用 System.Net.Dns.GetHostAddresses("localhost")[1].ToString(); 取得的也是127.0.0.1
参考:https://blog.csdn.net/u010784236/article/details/51820284 也与我的情况不一样。
想想我是在局域网中,用ipconfig /all 找到自己的ip 192.168.3.5 替换 127.0.0.1 添加防火墙规则,仍不行。关闭防火墙,还不行。
(如何获得IP,还可参考:https://blog.csdn.net/fwj380891124/article/details/18214145)
正准备放弃时,看到 https://blog.csdn.net/fengzheng22/article/details/17266105 其中有一句:
需要你用tcpclient访问的IP的端口正在被监听,否则就会显示积极拒绝,不是看他是否被占用,要看他是否在监听
想想我是直接整体复制的代码,不是双击窗体后单独写的formMain_load代码。而服务端应该是在formMain_load时开始监听。
于是重新修改代码,使得formMain_load时先运行服务端监听的代码。重新生成并运行,正常。这个错误太低级。
如果在局域网两台电脑上分别运行客户端和服务端,要确保能ping通,检查防火墙规则。参考这里:https://jingyan.baidu.com/article/a65957f4f557cb24e67f9ba6.html
2、另外,这里还有个例子:https://www.jb51.net/article/130148.htm
3、参考:https://www.cnblogs.com/straight/articles/7660889.html
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
isten函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。
4、TCP协议三次握手过程分析 参考:http://www.cnblogs.com/rootq/articles/1377355.html
5、C# TCP多线程服务器示例 参考:https://www.cnblogs.com/zhangxiaoyong/p/6486311.html
6. C# socket端口复用-多主机头绑定 参考:https://www.cnblogs.com/viewcozy/p/4666137.html
7、定时执行、一对多 参考: http://www.cnblogs.com/chenxizhang/archive/2011/09/10/2172994.html
8、在同步模式中,在服务器上使用Accept方法接入连接请求,而在客户端则使用Connect方法来连接服务器。相对地,在异步模式下,服务器可以使用BeginAccept方法和EndAccept方法来完成连接到客户端的任务,在客户端则通过BeginConnect方法和EndConnect方法来实现与服务器的连接。 参考:http://www.cnblogs.com/sunev/archive/2012/08/07/2625688.html
9、C#的IPAddress IPEndPoint 参考 https://www.cnblogs.com/2Yous/p/5797592.html