socket 客户端和服务端通信
客户端要连接服务器:首先要知道服务器的IP地址。而服务器里有很多的应用程序,每一个应用程序对应一个端口号 所以客户端想要与服务器中的某个应用程序进行通信就必须要知道那个应用程序的所在服务器的IP地址,及应用程序所对应的端口号
TCP协议:安全稳定,一般不会发生数据丢失,但是效率低。利用TCP发生数据一般经过3次握手(所有效率低,自己百度三次握手) UDP协议:快速,效率高,但是不稳定,容易发生数据丢失(没有经过三次握手,不管服务器有空没空,信息全往服务器发,所有效率搞,但服务器忙的时候就没办法处理你的数据,容易造成数据丢失,不稳定)
首先创建一个解决方案,在解决方案下创建一个“Socket通信”windows窗体应用程序的的项目,用作服务端,然后再在解决方案下创建一个“Socket客户端”windows窗体应用程序的项目 用作客户端
Socket通信 (服务器端)
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- namespace Socket通信
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- this.txtPort.Text = "50000";
- this.txtIp.Text = "192.168.253.2";
- }
- private void btnStart_Click(object sender, EventArgs e)
- {
- try
- {
- //当点击开始监听的时候,在服务器端创建一个负责监听IP地址跟端口号的Socket
- Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- //Any:提供一个 IP 地址,指示服务器应侦听所有网络接口上的客户端活动。此字段为只读。
- IPAddress ip = IPAddress.Any;
- //IPAddress ip = IPAddress.Parse(this.txtIp.Text);
- //创建端口号对象;将txtPort.Text控件的值设为服务端的端口号
- IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
- //监听
- socketWatch.Bind(point);
- ShowMsg("监听成功"); //注意:这个ShowMeg方法是自己定义的。看下面的代码可以找到这个方法
- 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()方法将它的参数添加进来
- }
- catch
- { }
- }
- //将远程连接的客户端的IP地址和Socket存入集合中
- Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
- /// <summary>
- /// 等待客户端连接,如果监控到有客户端连接进来就创建一个与之通信的Socket
- /// </summary>
- /// <param name="o"></param>
- Socket socketSend; // 定义一个负责通信的Socket
- void Listen(object o) //这里为什么不直接传递Socket类型的参数呢? 原因是:被线程执行的方法如果有参数的话,参数必须是object类型
- {
- Socket socketWatch = o as Socket;
- while (true) //为什么这里要有个while循环?因为当一个人连接进来的时候创建了与之通信的Socket后就程序就会往下执行了,就不会再回来为第二个人的连接创建负责通信的Socket了。(应该是每个人(每个客户端)创建一个与之通信的Socket)所以要写在循环里。
- {
- try
- {
- //等待客户端连接;Accept()这个方法能接收客户端的连接,并为新连接创建一个负责通信的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);
- }
- catch
- { }
- }
- }
- /// <summary>
- /// 服务端不停的接收客户端发送过来的消息
- /// </summary>
- /// <param name="o"></param>
- void GetData(object o)
- {
- Socket socketSend = o as Socket;
- while (true)
- {
- try
- {
- //将客户端发过来的数据先放到一个字节数组里面去
- 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);
- }
- catch
- {
- }
- }
- }
- 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(object sender, EventArgs e)
- {
- if (comboBox1.SelectedItem == null) //如果comboBox控件没有选中值。就提示用户选择客户端
- {
- MessageBox.Show("请选择客户端");
- return;
- }
- //服务器要想给客户端发消息,就需要先拿到负责通信的那个Socket
- string str = txtMes.Text.Trim();
- byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
- string getIp = comboBox1.SelectedItem as string; //comboBox存储的是客户端的(ip+端口号)
- socketSend = dic[getIp] as Socket; //根据这个(ip及端口号)去dic键值对中找对应 赋值与客户端通信的Socket【每个客户端都有一个负责与之通信的Socket】
- socketSend.Send(buffer);
- }
- }
- }
Socket客户端 (客户端)
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- namespace Socket客户端
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- this.txtIp.Text = "192.168.253.2"; //因为我们要在客户端连接服务端,所以这里是服务端的IP
- this.txtPort.Text = "50000"; //因为我们要在客户端连接服务端,所以这里服务端的端口号
- }
- Socket socketSend;
- private void btnStart_Click(object sender, EventArgs e)
- {
- try
- {
- //创建负责通信的Socket
- socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- IPAddress ip = IPAddress.Parse(this.txtIp.Text);
- //创建一个IPEndPoint类的实例,用远程服务器的IP地址和端口号来初始化它
- IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.txtPort.Text));
- //获得要连接的远程服务器应用程序的IP地址和端口号,并建立与远程服务器的连接
- socketSend.Connect(point);
- ShowMsg("连接成功");
- //开启一个新的线程,不停的接收服务端发送过来的消息
- Thread th = new Thread(GetData);
- th.IsBackground = true; //IsBackground获取或设置一个值,该值指示某个线程是否为后台线程。
- th.Start();
- }
- catch
- {
- }
- }
- void ShowMsg(string str)
- {
- this.txtLog.AppendText("\r\n"+str + "\r\n");
- }
- /// <summary>
- /// 客户端不停的接收服务端发送过来的消息
- /// </summary>
- /// <param name="o"></param>
- void GetData(object o)
- {
- while (true)
- {
- try
- {
- //将服务端发过来的数据先放到一个字节数组里面去
- 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);
- //RemoteEndPoint方法是获取服务器的IP地址和端口号
- ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str);
- }
- catch
- {
- }
- }
- }
- /// <summary>
- /// 客户端给服务器发送消息
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnSend_Click(object sender, EventArgs e)
- {
- //获取输入框输入的数据
- string str = txtMes.Text.Trim();
- //将输入框的数据转换成二进制数据
- byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
- //Send方法是将数据发送到连接的Socket
- socketSend.Send(buffer);
- this.txtLog.AppendText("我自己:" + this.txtMes.Text);
- this.txtMes.Text = "";
- }
- private void Form1_Load(object sender, EventArgs e)
- {
- //在程序加载的时候取消跨线程的检查
- Control.CheckForIllegalCrossThreadCalls = false;
- }
- }
- }
当这两个项目(服务端,和客户端)都写好后,怎么测试呢?
首先我们将服务端设置启动项,然后启动调试,
然后我们在将鼠标移动到“Socket客户端” (客户端)这个项目下,鼠标右键项目名称“Socket客户端”--》调试--》启动实例 就可以了。
开打开始命令 cmd telnet 10.18.16.46 5000 即telnet 服务器ip地址 绑定的端口号
如果用命令,需要在 控制面板--》程序和功能--》打开或关闭windows功能 将Telnet 服务器,和Telnet客户端打上钩
---------------------------------------------------------------------------------------------------------------
注释一下:
//创建一个用来监听的Socket对象(参数1:表示采用IPv4,参数2:表示使用数据流来传输数据,而不是数据包 参数3:表示采用Tcp协议)
Socket skConn =newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
//创建IP和监听端口(参数1:IP地址 参数2:端口号是9999)
IPEndPoint endPoint=newIPEndPoint(IPAddress.Parse("192.168.253.3"),9999);
socket 客户端和服务端通信的更多相关文章
- Python进阶----SOCKET套接字基础, 客户端与服务端通信, 执行远端命令.
Python进阶----SOCKET套接字基础, 客户端与服务端通信, 执行远端命令. 一丶socket套接字 什么是socket套接字: 专业理解: socket是应用层与TCP/IP ...
- Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...
- 基于开源SuperSocket实现客户端和服务端通信项目实战
一.课程介绍 本期带给大家分享的是基于SuperSocket的项目实战,阿笨在实际工作中遇到的真实业务场景,请跟随阿笨的视角去如何实现打通B/S与C/S网络通讯,如果您对本期的<基于开源Supe ...
- Netty入门之客户端与服务端通信(二)
Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...
- netty-3.客户端与服务端通信
(原) 第三篇,客户端与服务端通信 以下例子逻辑: 如果客户端连上服务端,服务端控制台就显示,XXX个客户端地址连接上线. 第一个客户端连接成功后,客户端控制台不显示信息,再有其它客户端再连接上线,则 ...
- Python socket编程客户端与服务端通信
[本文出自天外归云的博客园] 目标:实现客户端与服务端的socket通信,消息传输. 客户端 客户端代码: from socket import socket,AF_INET,SOCK_STREAM ...
- 二、网络编程-socket之TCP协议开发客户端和服务端通信
知识点:之前讲的udp协议传输数据是不安全的,不可靠不稳定的,tcp协议传输数据安全可靠,因为它们的通讯机制是不一样的.udp是用户数据报传输,也就是直接丢一个数据包给另外一个程序,就好比寄信给别人, ...
- Netty入门——客户端与服务端通信
Netty简介Netty是一个基于JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性.换句话说,Netty是一个NIO框架,使用它可以简单快速 ...
- 实验09——java基于TCP实现客户端与服务端通信
TCP通信 需要先创建连接 - 并且在创建连接的过程中 需要经过三次握手 底层通过 流 发送数据 数据没有大小限制 可靠的传输机制 - 丢包重发 包的顺序的 ...
随机推荐
- Android学习笔记 - 开始
因为项目需求,要在Android上开发一个证件识别软件,项目时间 9/10- 9/30 工作内容: (1)修改证件识别库 (2)移植证件识别库至Android (3)开发一个Android应用程序 学 ...
- 转载 Adobe DreamweaverCS6安装及破解
一:安装 百度链接:链接:http://pan.baidu.com/s/1dF8hTex 密码:zrew (重点) 1) Adobe DreamweaverCS6中文版下载 2)Adobe Dre ...
- LNMP(linux+nginx+mysql+php)服务器环境配置【转载】
本文转载自 园友David_Tang的博客,如有侵权请联系本人及时删除,原文地址: http://www.cnblogs.com/mchina/archive/2012/05/17/2507102.h ...
- 线性可分SVM完全推导过程
- JavaScript学习笔记——3.对象
JavaScript 对象 - 创建对象 1- var obj = new Object(); 2- var obj = {}; *例子:var person = {Name:"Hack&q ...
- 【算法】关于图论中的最小生成树(Minimum Spanning Tree)详解
本节纲要 什么是图(network) 什么是最小生成树 (minimum spanning tree) 最小生成树的算法 什么是图(network)? 这里的图当然不是我们日常说的图片或者地图.通常情 ...
- EOS 智能合约 plublic key 转换
在做一个EOS 的action接口时,定义如下: void setbplist(const account_name bp_name, const uint64_t bp_time, const ...
- 2.2、Softmax Regression算法实践
Softmax Regression算法实践 有了上篇博客的理论知识,我们可以利用实现好的函数,来构建Softmax Regression分类器,在训练分类器的过程中,我们使用多分类数据作为训练数据: ...
- mysql5.6 的st_distance 实现按照距离远近排序。
当前所处在的位置(113.858202 , 22.583819 ),需要查询我附近1000米内的小区,并安装由近到远的顺序排列 SELECT s.id,s.name,s.lng,s.lat, rou ...
- MapReduce编写的正确姿势
先看一下目录结构 这里是job接口,负责参数的传递和定时的调用 下面的图是MR 程序相关的目录图片,其中MR的入口程序负责读取数据,并指定对应的Map.Reduce程序. 程序的流程 首先简单的说一下 ...