在公司花了两天的时间来研究Socket编程,感觉也还是学到了一点点皮毛,不过也还是有一点点的小成就。

配合Winform窗体界面,实现了简单的窗体间互相通信的小项目(可以互相发消息,服务器可以给客户端发送文件和震动弹出)。

现在把这两天学习到的知识点记录下来分享。。。

首先要声明:本篇博客并不会详细的讲解Socket底层的实现逻辑,只是讲解他是怎么运用的,是怎么实现窗体间通信的。


Socket的简单原理

Socket的两个概念:端口协议

端口

我们是通过服务端去访问应用程序的,但是在同一个服务端中的应用程序,他们的IP地址都是一样的,所以单单通过IP地址去访问对应的应用程序是不可能的,所以,得再结合端口去实现具体的访问。
每个应用程序就会有一个端口,我们就可以根据IP地址和端口号去实现访问了。(每个应用程序的端口号都是唯一的)

通过端口和IP地址客户端就可以准确无误的去访问服务器里面的应用程序了。

在访问的过程中,又会涉及到协议的问题!

协议

Socket有两个协议:TCPUDP

TCP

其中我们要知道,TCP协议是网络上比较安全稳定的协议,一般不会发送数据丢失。使用TCP协议建立网络连接,需要经过“三次握手”才建立连接。如下图:

只有客户端和服务器完成了这三次握手的过程,服务器才会和客户端进行数据的传输。(只要是少了一次都不会进行互相通信)

TCP建立网络连接的优缺点:

  1. 优点:安全,稳定,防止数据丢失。
  2. 缺点:效率低,经过三次握手的过程,需要耗费一定的时间。

UDP

UDP协议是与TCP协议相反的。

UDP协议不管服务器有没有空,就是一直给服务器发送消息,直到发完为止。他不管服务器有没有接收到,他只是要完成任务就行。

如果服务器很忙的话,并没有时间去处理客户端发过来的消息,那么就会造成数据丢失。

UDP建立网络连接的优缺点:

  1. 优点:快速,效率高。
  2. 缺点:不稳定,容易造成数据丢失。

两个协议各有的优缺点,也不好说哪个比较好。
像UDP协议一般用于视频的传输等。

好了TCP/UDP协议了解到这里就好了!


Socket

需要包含命名空间:
using System.Net;
using System.Net.Sockets;

一个服务器至少有两个Socket用于通信,一个用来监听,一个用来连接客户端进行通信。

我们先看一张图:

这张图描述的就是客户端和服务其进行互相通信的过程。
下面的讲解也是根据这张图来说明。

  1. Socket
    他是一个类,用来定义对象进行通信。
    首先创建一个用于监听的Socket(监听有没有客户端连接服务器)
    如代码:
// 一个负责监听的Socket ************************************************************************// ip地址的类型(ipV4/ipv6)   选择以流的方式     流对应的是Tcp协议
Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

参数一:填写IP地址的类型(ipv4:InterNetwork; ipv6:InterNetworkV6)

参数二:填写TCP/UDP连接对应的方式
补充知识点:TCP是以流的方式(Stream);UDP是以数据报的方式(Dgram)。

参数三:填写TCP/UDP协议
这里就用了TCP的传输方式。

然后创建ip地址和端口号对象:

// 创建ip地址和端口号对象
IPAddress iPAddress = IPAddress.Any; //IPAddress.Parse(this.txtSever.Text);
IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, Convert.ToInt32(this.txtPort.Text));

Any是自动获取连接进行的客户端的IP地址。
端口号是获取我们textBox控件里面的。

  1. Bind
    绑定监听端口

    // 让负责监听的Socket绑定IP地址和端口号
    socketWatch.Bind(iPEndPoint);
    

    到了这里,就完成了监听的工作。

  2. Listen
    设置监听队列
    在同一个时间段允许最大的连接个数

    // 设置监听队列(比如在同一个时间段允许最大的连接个数)
    socketWatch.Listen(10);
    

    比如在一秒内最多允许10个客户端连接服务器,从第11个客户端往后都得排队等待连接。

  3. Accept
    该函数用于等待客户端与服务器进行连接,并返回一个新的Socket,用于与客户端进行通信。

     // 负责监听的Socket来接受客户端的连接(创建跟客户端通信的Socket)Socket  socket = socketWatch.Accept();   // Accept() : 等待客户端连接
    

    利用死循环可以一直与多个客户端进行连接
    为了不卡死主线程,可以使用多线程对其进行操作!

     // 该线程的作用是:服务器不停的监听,等待客户端连接,并且创建与之通信用的Socket
    Thread th = new Thread(Listen);
    th.IsBackground = true;
    th.Start(socketWatch);
    
    private void Listen(Object o) {Socket socketWatch = o as Socket;// 等待客户端连接,并且创建与之通信用的Socketwhile (true) {// 负责监听的Socket来接受客户端的连接(创建跟客户端通信的Socket)Socket socket= socketWatch.Accept();   // Accept() : 等待客户端连接                }}
    
  4. Receive
    获取客户端发过来的消息(单位:byte)

    // 客户端连接成功后,服务器接收客户端发来的消息
    byte[] buffer = new byte[1024 * 1024 * 2];
    // b:实际接收到的有效字节数
    int b = socket.Receive(buffer); // 调用Receive函数返回客户端发来的消息(单位:字节)
    

    可以根据b来判断客户端发过来的消息的字节个数。

  5. Send
    服务器给客户端发送消息

    仅支持字节发送数组发送

    byte[] newByte = Encoding.UTF8.GetBytes("我是服务器");
    socket.Send(newByte);
    

好了,服务器的流程基本上就这样了。

客户端与服务端雷同,就不演示了。




练手小项目

可能讲的不是很好,但是没关系,我把我自己写好的代码上传到此给需要的朋友玩一下。(可以实现两个窗口间互相通信)

界面如下:

服务器:

客户端:

代码如下:
服务器

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
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();}private void Form1_Load(object sender, EventArgs e) {Control.CheckForIllegalCrossThreadCalls = false;}private void btnStart_Click(object sender, EventArgs e) {try {// 一个负责监听的Socket ************************************************************************// ip地址的类型(ipV4/ipv6)   选择以流的方式     流对应的是Tcp协议Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);// 创建ip地址和端口号对象     IPAddress iPAddress = IPAddress.Any; //IPAddress.Parse(this.txtSever.Text);IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, Convert.ToInt32(this.txtPort.Text));// 让负责监听的Socket绑定IP地址和端口号socketWatch.Bind(iPEndPoint);ShowMsg("监听成功!");// 设置监听队列(比如在同一个时间段允许最大的连接个数)socketWatch.Listen(10);// 该线程的作用是:服务器不停的监听,等待客户端连接,并且创建与之通信用的SocketThread th = new Thread(Listen);th.IsBackground = true;th.Start(socketWatch);}catch (Exception) {  }}// 存储连接服务器的客户端的IP地址/端口号和负责通信的socketDictionary<string, Socket> dictionary = new Dictionary<string, Socket>();Socket socket;  // 负责通信private void Listen(Object o) {Socket socketWatch = o as Socket;// 等待客户端连接,并且创建与之通信用的Socketwhile (true) {try {// 负责监听的Socket来接受客户端的连接(创建跟客户端通信的Socket)socket = socketWatch.Accept();   // Accept() : 等待客户端连接// 将连接成功后的客户端IP地址和端口号显示出来ShowMsg(socket.RemoteEndPoint.ToString() + "连接成功\r\n");// 将远程连接的客户端的IP地址和socket存入集合中dictionary.Add(socket.RemoteEndPoint.ToString(), socket);// 将远程连接的客户端的IP地址和端口号存储到下拉框中this.cobUsers.Items.Add(socket.RemoteEndPoint.ToString());// 该线程的作用是:使服务器不停的接收客户端发过来的消息Thread th = new Thread(Recive);th.IsBackground = true;th.Start(socket);}catch (Exception) {  }}}// 服务器不停的接收客户端发过来的消息private void Recive(Object o) {Socket socket = o as Socket;while (true) {try {// 客户端连接成功后,服务器接收客户端发来的消息byte[] buffer = new byte[1024 * 1024 * 2];// b:实际接收到的有效字节数    int b = socket.Receive(buffer); // 调用Receive函数返回客户端发来的消息(单位:字节)// 用户点击了叉×if (b == 0) {break;}// 将其转为人能看得懂的字符串类型string str = Encoding.UTF8.GetString(buffer, 0, b);ShowMsg(socket.RemoteEndPoint.ToString() + "(客户端):" + str);  // 显示出来}catch (Exception) { }}}private void ShowMsg(string str) {this.txtLog.AppendText(str + "\r\n");}private void btnSend_Click(object sender, EventArgs e) {try {// 获取文本的值并转换为byte类型数组byte[] by = Encoding.UTF8.GetBytes(this.txtMsg.Text.Trim());this.txtMsg.Text = "";// 定义一个泛型集合,将文本表示符0 和 byte数组存储入集合中List<byte> list = new List<byte>();list.Add(0);list.AddRange(by);// 将泛型集合转换为数组byte[] newByte = list.ToArray();// 服务器给客户端发送消息//socket.Send(by);string ip = this.cobUsers.SelectedItem.ToString();  // 获得下拉框中选中项的IP地址和端口号dictionary[ip].Send(newByte);    // 将其传入Dictionary中获得跟客户端通信的socket}catch (Exception) {  }}// 选择需要发送的文件private void btnSelect_Click(object sender, EventArgs e) {try {OpenFileDialog ofd = new OpenFileDialog();// 设置初始路径ofd.InitialDirectory = @"C:\Users\yangg\Desktop";ofd.Title = "请选择你需要发送的文件";// 设置文件筛选ofd.Filter = "所有文件|*.*";if (ofd.ShowDialog() == DialogResult.OK) {this.txtPath.Text = ofd.FileName;}}catch (Exception) {  }}private void btnSentFile_Click(object sender, EventArgs e) {try {// 获取文件路径string path = this.txtPath.Text;// 使用文件流以读的方式打开文件using (FileStream fileRead = new FileStream(path, FileMode.Open, FileAccess.Read)) {byte[] buffer = new byte[1024 * 1024 * 10];// 将文件中的数据读取进字节数组中,返回读取到的个数int r = fileRead.Read(buffer, 0, buffer.Length);List<byte> list = new List<byte>();list.Add(1);list.AddRange(buffer);byte[] newBuffer = list.ToArray();// 发送dictionary[this.cobUsers.SelectedItem.ToString()].Send(newBuffer, 0, r + 1, SocketFlags.None);}}catch (Exception) {  }}private void btnZD_Click(object sender, EventArgs e) {byte[] buffer = new byte[1];buffer[0] = 2;dictionary[this.cobUsers.SelectedItem.ToString()].Send(buffer);}}
}

客户端

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
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 _Client {public partial class Form1 : Form {public Form1() {InitializeComponent();}Socket socket;private void btnStart_Click(object sender, EventArgs e) {try {// 创建一个发送消息的Socketsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);// 建立IP地址和端口号的对象IPAddress iPAddress = IPAddress.Parse(this.txtSever.Text);IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, Convert.ToInt32(this.txtPort.Text));// 客户端服务器建立连接socket.Connect(iPEndPoint);Shows("连接成功!");// 建立线程,无限的接收服务器发过来的消息Thread th = new Thread(Recive);th.IsBackground = true;th.Start();}catch (Exception) {  }}private void Shows(string str) {this.txtLog.AppendText(str + "\r\n");}// 客户端给服务器发送消息private void benSend_Click(object sender, EventArgs e) {try {// 获取待发送的文本,并转换为byte字节数组byte[] by = Encoding.UTF8.GetBytes(this.txtMsg.Text.Trim());this.txtMsg.Text = "";// 客户端给服务器发送消息socket.Send(by);}catch (Exception) {  }}// 不停的接收服务器发过来的消息private void Recive() {while (true) {try {byte[] by = new byte[1024 * 1024 * 10];// 获取服务器发过来的消息,返回接收到的个数int r = socket.Receive(by);if (r == 0) {break;}// 表示发送文字消息if (by[0] == 0) {string str = Encoding.UTF8.GetString(by, 1, r - 1);Shows(socket.RemoteEndPoint.ToString() + "(服务器):" + str);} else if (by[0] == 1) {    // 表示发送文件SaveFileDialog sfd = new SaveFileDialog();sfd.InitialDirectory = @"C:\Users\yangg\Desktop";sfd.Title = "请选择报错位置";sfd.Filter = "所有文件|*.*";if (sfd.ShowDialog(this) == DialogResult.OK) {string path = sfd.FileName;using (FileStream fileWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write)) {fileWrite.Write(by, 1, r - 1);                               }MessageBox.Show("保存成功");}} else if (by[0] == 2) {    // 表示发送震动ZD();}}catch (Exception) {  }}}// 震动private void ZD() {int x = this.Location.X;int y = this.Location.Y;for (int i = 0; i < 300; i++) {this.Location = new Point(x - 50, y);this.Location = new Point(x, y + 50);this.Location = new Point(x + 50, y);this.Location = new Point(x, y - 50);}this.Location = new Point(x, y);}private void Form1_Load(object sender, EventArgs e) {Control.CheckForIllegalCrossThreadCalls = false;}}
}

CSDN:
https://download.csdn.net/download/cpp_learner/12761444

百度连接:
链接:https://pan.baidu.com/s/16TE3Q51NhxMXNv2sp9gfcQ
提取码:fc4n


总结:
也就基本上是这样了,其他的也就不会了,希望对大家有帮助吧!

C# Socket网络编程(附赠练手小项目)相关推荐

  1. c++小项目_编程初学者的练手小项目(Pythonamp;C/C++)

    老是有一些刚接触编程的小伙伴问,怎么样才能快速提高编程能力?"一切不写代码的学编程,都是瞎搞!",想要提高编程能力,当然是写代码.写代码.写代码.在不断做项目的过程中,加强基础语法 ...

  2. Golang练手小项目系列

    Golang练手小项目系列 本系列整理了10个工作量和难度适中的Golang小项目,适合已经掌握Go语法的工程师进一步熟练语法和常用库的用法. golang练手小项目系列(1)-位向量 golang练 ...

  3. 爬虫练手小项目:豆瓣高分图书TOP100

    爬虫练手小项目:豆瓣高分图书TOP100 import requests import re from requests.exceptions import RequestException impo ...

  4. go语言面向对象 练手小项目

    学习一门语言,最重要的就是动手,一个项目的代码写三遍,看起来笨的方法,实际上也是最快的方法. go语言的第一个练手小项目: 家庭收支记录软件的编写: 1.项目需求说明 模拟现实基于文本界面的<家 ...

  5. ssm练手小项目_20 个 JavaScript+Html+CSS 练手的小项目

    前言: 最近在 GitHub 上发现了一个 vanillawebprojects[1] 开源仓库,里面收集了 20 个 JavaScript+Html+CSS的练手项目,没有使用任何框架,可以让你从基 ...

  6. 台式小风扇(HTML+CSS+JS练手小项目)

    台式小风扇(HTML+CSS+JS练手小项目) 功能介绍 外观展示 HTML代码 CSS代码 JS代码 总结 功能介绍 前段时间看到这样的风扇特效,感觉还挺好玩,就自己也写一个练练手. 风扇有四个档位 ...

  7. 数据结构练手小项目(AVL树、哈希表、循环链表、MySQL数据库)

    文章目录 前言 正文(无删减) 我的想法(删减修改版) 数据导入与数据存储 功能实现 数据结构 用户结构 SIM卡结构 AVL树数据结构 哈希表结构 数据表 用户表 SIM卡表 时间安排 前言 本月主 ...

  8. html+css+js之20个练手小项目(一)

    html+css+js之20个练手小项目(一)--Hangman 前言 一.HTML 二.CSS 三.JS 前言 前端新手练习,记录不迷失. 主要练习html和CSS布局以及JS. 来源github, ...

  9. 练手小项目,爬取3DM图片

    博客原文:https://weweweha.com 1. 概述 ​ 爬取3DM指定网页的游戏壁纸,并且通过多线程来加速爬取图片的速度. 2.使用库 ​ request库用来1解析指定网页,re库用来搜 ...

最新文章

  1. SSL/TLSV1.0 programe in linux
  2. pandorabox php7,【恐惧交响4之潘多拉】ProjectSAM Symphobia 4 Pandora v1.0.7
  3. 2019 好笔友-见字如面
  4. 【学习笔记】硬件设备选型
  5. [Python人工智能] 二十四.易学智能GPU搭建Keras环境实现LSTM恶意URL请求分类
  6. Java并发包常见异常_Java并发包1--线程的状态及常用方法
  7. LINUX添加一块网卡地址配置及问题
  8. ACM/CF赛制getstart模板
  9. 获取计算机显示屏高度,CSS 使用calc()获取当前可视屏幕高度
  10. STM32WB55开发板(一)单板设计-硬件介绍
  11. 躲避方块的球——C语言游戏
  12. cJSON基本使用方法
  13. 开播虚拟人物特效插件 yy歪歪直播使用主播专用
  14. github免用户名密码管理代码
  15. 使用Dash快速构建你的数据可视化前端
  16. 【收藏】如何优雅的在 Python设置报警通知(邮件、短信、电脑外放声音)
  17. Spring 工作原理
  18. 微信小程序实现大转盘抽奖----踩坑之路
  19. 格局大小决定你的人生高度---the height of your life depends on the size of your life pattern.
  20. 我的微信小程序入门学习-地图定位

热门文章

  1. 电脑锁定之后重新打开屏幕,双屏幕只能显示一个(娱乐向记录,技术向勿入)
  2. c++20中的分支预测
  3. RTSP在线视频流资源地址
  4. 07_非线性优化(下)
  5. walden词频统计0
  6. 破解局域网限速 突破路由器限速 突破IP限速 突破内网IP限速 解决方案 v 2010.7.3
  7. 轨迹分析—Matlab计算均方位移
  8. libvirt-qemu-虚拟机内存分配和内存热插拔
  9. 华为带火落后的增程式技术,吓得传统汽车企业瑟瑟发抖
  10. android保存连接地址到桌面快捷方式,Android创建桌面快捷方式两种方法