添加using指令集:
using System.Net.Sockets;
using System.Threading;
using System.Net;
添加定义(IP地址 IP接口 berkeley套接字接口):
private IPAddress serverIP = IPAddress.Parse("127.0.00.1");
private IPEndPoint serverFullAddr;
private Socket sock;
断开连接:
//断开连接
private void btnClose_Click(object sender, EventArgs e)
{
sock.Close();
btnConn.Enabled = true ;
}
//连接服务器端
private void btnConn_Click(object sender, EventArgs e)
{
btnConn.Enabled = false;
serverIP = IPAddress.Parse(tbxIP.Text);
try
{
serverFullAddr = new IPEndPoint(serverIP, int.Parse(tbxPort.Text));//设置IP,端口
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//指定本地主机地址和端口号
sock.Connect(serverFullAddr);
//btnConn.Enabled = false;
lblError.Text = "连接服务器成功。。。。";
btnClose.Enabled = true;
sock.Close();
}
catch (Exception ee)
{
btnConn.Enabled = true;
lblError.Text = "连接服务器失败。。。请仔细检查服务器是否开启"+ ee;
}
}
发送消息:
//发送消息
private void btnSend_Click(object sender, EventArgs e)
{
serverFullAddr = new IPEndPoint(serverIP, int.Parse(tbxPort.Text));//设置IP,端口
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//指定本地主机地址和端口号
sock.Connect(serverFullAddr);
byte[] byteSend = System.Text.Encoding.Default.GetBytes(this.tbxMessage.Text);
byte[] message = new byte[1024];
string mess = "";
int bytes = 0;
try
{
//发送数据
sock.Send(byteSend);
bytes = sock.Receive(message);//接收数据
mess = mess + Encoding.Default.GetString(message,0,bytes);//编码(当接收的字节大于1024的时候 这应该是循环接收,测试就没有那样写了)
//do
//{
// bytes = newSocket.Receive(message, message.Length, 0);
// mess = mess + Encoding.ASCII.GetString(message, 0, bytes);
//}
//while (bytes > 0);
tbxMessage.Text = mess;
}
catch (Exception ex)
{
lblError.Text = "出现错误,请联系管理员"+ex;
}
sock.Close();
}
//清空消息
private void btnClean_Click(object sender, EventArgs e)
{
tbxMessage.Text = "";
}
接收数据(由于多线程的原因,稍微麻烦一点):
1.打开线程
Thread myThead = null;
2.线程函数,类似于指定中断服务函数
myThead = new Thread(new ThreadStart(BeginListen));
myThead.Start();
3.编写函数(这一部分理解比较麻烦,涉及委托,因为要是用的显示数据的控件是在主线程里面打开的,而控件的缺省值是不可以跨线程使用,所以需要委托。)
a.需要好好理解Invoke函数:他是在拥有该控件的基础窗口句柄的线程上,用指定的参数列表执行指定的委托。通俗一点就是,我委托拥有这个控件的线程来帮我执行这个操作。
b.为什么使用accept函数。为了新的连接创造一个新的socket呢?理解为:sock这个socket已经在监听了,而且队列中允许的传入连接数有多个,所以新创建一个socket,当队列中有忙碌的传入连接时,将地址给新的socket来recieve数据。高效,稳定一些。
private void BeginListen()
{
serverI2.xianP = IPAddress.Parse(tbxIP.Text); //IP
serverFullAddr = new IPEndPoint(serverIP, int.Parse(tbxPort.Text));//设置IP,端口
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//指定本地主机地址和端口号
sock.Bind(serverFullAddr);
lbxMessage.Invoke(new SetTextCallback(SetText), "启动成功 时间:" + DateTime.Now, 1);
byte[] message = new byte[1024];
string mess = "";
while (true)
{
try
{
sock.Listen(5);//backlog 参数指定队列中最多可容纳的等待接受的传入连接数。
Socket newSocket = sock.Accept();//为新建连接创建新的socket。sock这个socket是用来监听的,当他有连接请求的时候,将地址给新的socket来接收,这样不影响他继续监听原本的socket。
int bytes = newSocket.Receive(message);//用刚才chuangjian接收数据
mess = Encoding.Default.GetString(message, 0, bytes);//对接收字节编码(S与C 两端编码格式必须一致不然中文乱码)(当接收的字节大于1024的时候 这应该是循环接收,测试就没有那样写了)
lbxMessage.Invoke(new SetTextCallback(SetText), mess, 1);//子线程操作主线程UI控件
//获取客户端的IP和端口
string ip11 = newSocket.RemoteEndPoint.AddressFamily.ToString();
mess = "已接收数据: "+ mess +" 来自:" +ip11+ " 当前时间为:" + DateTime.Now; //处理数据
newSocket.Send(Encoding.Default.GetBytes(mess));//向客户端发送数据
}
catch (SocketException se)
{
lbxMessage.Invoke(new SetTextCallback(SetText), mess + se, 1);
}
}
}
#region//声名委托
//生命delegate对象,可以理解成函数指针
delegate void SetTextCallback(string text, int num);
//欲传递的方法,她与CompareDelegate具有相同的参数和返回值类型
private void SetText(string text, int num)
{
lbxMessage.Items.Add(text);
}
#endregion
一个参考的完整的Demo:
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);
}
}
}