你也可以写聊天程序 - C# Socket学习1
简述
我们做软件工作的虽然每天都离不开网络,可网络协议细节却不是每个人都会接触和深入了解。我今天就来和大家一起学习下Socket,并写一个简单的聊天程序。
一些基础类
首先我们每天打开浏览器访问网页信息都是使用的HTTP/HTTPS协议,而HTTP是通过的TCP建立的连接。TCP底层又是通过的Socket套接字进行的通信。所以他们之间的抽象关系是:

我们在学习Socket编程的时候可能会需要用到IPEndPoint、Dns、IPAddress等类,再往上TCP相关有TcpListener、TcpClient、NetworkStream,再往上就是大家熟悉的HttpClient等。
IPEndPoint、Dns、IPAddress基础作用如下:
//根据DNS获取域名绑定的IP
foreach (var address in Dns.GetHostEntry("www.baidu.com").AddressList)
{
Console.WriteLine($"百度IP:{address}");
}
//字符串转IP地址
IPAddress ipAddress = IPAddress.Parse("192.168.1.101");
//通过IP和端口构造IPEndPoint对象,用于远程连接
//通过IP可以确定一台电脑,通过端口可以确定电脑上的一个程序
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 80);
利用Socket编写聊天程序
我们首先从Socket开始讲起。
要实现Socket通信,先得有个服务端的监听,再有个客户端的连接,然后客户端和服务端就可以通信了。如下:

服务端代码如下:
//1 创建Socket对象
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2 绑定ip和端口
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEndPoint = new IPEndPoint(ip, 50001);
socketServer.Bind(ipEndPoint);
//3、开启侦听(等待客户机发出的连接),并设置最大客户端连接数为10
socketServer.Listen(10);
//4、【阻塞】,等待客户端连接
Socket newSocket = socketServer.Accept();
//5、【阻塞】,等待读取客户端发送过来的数据
byte[] data = new byte[1024 * 1024];
int readLeng = newSocket.Receive(data, 0, data.Length, SocketFlags.None);
//6、读取数据
var msg = Encoding.UTF8.GetString(data, 0, readLeng);
客户端代码如下:
//1 创建Socket对象
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2 连接到服务端
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEndPoint = new IPEndPoint(ip, 50001);
socketClient.Connect(ipEndPoint);
//3 发送消息到服务端
socketClient.Send(Encoding.UTF8.GetBytes("你好,农码一生"));
到此,我们就可以开启服务端的服务,并接受客户端的发来的消息了。
不过,这里有个很大的问题,服务端只能建立一个客户端连接和接受一次客户端发来的消息。如果想要连接更多的客户端和接受无数次的消息,服务端代码两处阻塞的地方需要另外开一个线程然后包到循环里面去。
修改后的服务端代码如下:
void .... ()
{
//1 创建Socket对象
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2 绑定ip和端口
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEndPoint = new IPEndPoint(ip, 50001);
socketServer.Bind(ipEndPoint);
//3、开启侦听(等待客户机发出的连接),并设置最大客户端连接数为10
socketServer.Listen(10);
//开启新的线程,循环等待新的客户端连接
Task.Run(() => { Accept(socketServer); });
}
void Accept(Socket socket)
{
while (true)
{
//4、【阻塞】,等待客户端连接
Socket newSocket = socket.Accept();
//开启新的线程,循环等待接收新的数据
Task.Run(() => { Receive(newSocket); });
}
}
void Receive(Socket newSocket)
{
while (true)
{
//5、【阻塞】,等待读取客户端发送过来的数据
byte[] data = new byte[1024 * 1024];
int readLeng = newSocket.Receive(data, 0, data.Length, SocketFlags.None);
//6、读取数据
var msg = Encoding.UTF8.GetString(data, 0, readLeng);
}
}
到此,服务端就可以接受多个客户端的连接和接收多次客户端发来的消息了。不过我们可能还需客服端能接收服务端发来的消息,这个你可以自己尝试下。文末会提供完整的代码参考。
注意:
用Socket来编写聊天软件是长连接,有状态的。不确定服务端什么时候会发送消息过来,我们也可以连续发送消息而不响应。所以,对于消息的接收就需要开一个新的线程循环接收。
而对于HTTP来说,虽然它是也是通过TCP建立的通信,但在数据请求完毕后会马上关闭连接,这个过程很短。每次访问都会建立一个新的连接,是无状态的。
对于浏览器来说是一问一答的形式,先发送请求(Send),然后接收响应(Receive)所以就可以做到不开启新的线程,直接有序的同步的完成。这个在下一篇《模拟浏览器的请求和服务端的响应》会具体分析。
利用TCP编写聊天程序
虽然上面我们利用Socket类实现了一个简单的聊天程序,但是微软觉得Socket太复杂。为了让你们早点干完活,早点下班,于是又在Socket的基础上有封装了两个相关的类TcpListener、TcpClient。
利用TcpListener、TcpClient来实现同上面相同的功能。
服务端代码:
void ...()
{
//1 开启监听
TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 9999);
tcpListener.Start(10); //最多同时接收10个用户连接
//开启一个线程,循环等待客户端的连接
Task.Run(() => { Accept(); });
}
//等待客户端的连接
void Accept()
{
while (true)
{
//2 【阻塞】等待客户端的连接
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
//开启一个新的线程 等待新的消息
Task.Run(() => { Read(networkStream, tcpClient); });
}
}
//接收消息
void Read(NetworkStream networkStream)
{
while (true)
{
byte[] buffer = new byte[1024 * 1024];
//3 【阻塞】等待接收新的消息
var readLen = networkStream.Read(buffer, 0, buffer.Length);
var msg = Encoding.UTF8.GetString(buffer, 0, readLen);
}
}
客户端代码:
//1 连接服务端
TcpClient tcpClient = new TcpClient();
tcpClient.Connect(IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text));
//2 发送消息到服务端
byte[] buffer = Encoding.UTF8.GetBytes("你好,农码一生");
networkStream.Write(buffer, 0, buffer.Length);
用TcpListener、TcpClient的实现也算ok了,TcpListener代码写的服务端和Socket通信也是完成没问题的,因为他们最后都是Socket。
对此你有觉得比Socket简单和容易理解?其实我更习惯Socket。
注意:
// 1、断开连接使用
socketClient?.Shutdown(SocketShutdown.Both);
socketClient?.Close();
// 2、服务端需要判断
int readLeng = newSocket.Receive(data, 0, data.Length, SocketFlags.None);
if (readLeng == 0)//客户端断开连接
{
//停止会话(禁用Socket上的发送和接收,该方法允许Socket对象一直等待,直到将内部缓冲区的数据发送完为止)
newSocket.Shutdown(SocketShutdown.Both);
//关闭连接
newSocket.Close();
//跳出循环
return;
}
3、具体请参考文末提供的完整demo
效果图

结束
- 本文已同步至索引目录:《物联网基础组件IoTClient开发系列》
- 完整Demo:https://github.com/zhaopeiym/BlogDemoCode/tree/master/Socket编程
- 关于demo本来打算用.net core3.0来写的,可是winform的设计器不争气。所以demo还是用的.net fx,只有TcpListener是用的.net core控制台写的。
- 为什么要写这个,一是学习下底层的协议通信,二是为了实现一套物联网设备通讯协议做准备。类似dathlin这位大佬的Hsl。
- 推荐阅读
你也可以写聊天程序 - C# Socket学习1的更多相关文章
- 你也可以写个聊天程序 - C# Socket学习1
原文:你也可以写个聊天程序 - C# Socket学习1 简述 我们做软件工作的虽然每天都离不开网络,可网络协议细节却不是每个人都会接触和深入了解.我今天就来和大家一起学习下Socket,并写一个简单 ...
- 你也可以写个服务器 - C# Socket学习2
前言 这里说的服务器是Web服务器,是类似IIS.Tomcat之类的,用来响应浏览器请求的服务. Socket模拟浏览器的Url Get请求 首先浏览器的请求是HTTP协议.我们上一篇说过,HTTP是 ...
- 聊天程序——基于Socket、Thread (二)
聊天程序简述 1.目的:主要是为了阐述Socket,以及应用多线程,本文侧重Socket相关网路编程的阐述.如果您对多线程不了解,大家可以看下我的上一篇博文浅解多线程 . 2.功能:此聊天程序功能实现 ...
- java Socket多线程聊天程序
参考JAVA 通过 Socket 实现 TCP 编程 参考java Socket多线程聊天程序(适合初学者) 以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包 ...
- Socket网络编程--聊天程序(9)
这一节应该是聊天程序的最后一节了,现在回顾我们的聊天程序,看起来还有很多功能没有实现,但是不管怎么说,都还是不错的.这一节我们将讲多服务器问题(高大上的说法就是负载问题了.)至于聊天程序的文件发送(也 ...
- Socket聊天程序——Common
写在前面: 上一篇记录了Socket聊天程序的客户端设计,为了记录的完整性,这里还是将Socket聊天的最后一个模块--Common模块记录一下.Common的设计如下: 功能说明: Common模块 ...
- Socket聊天程序——客户端
写在前面: 上周末抽点时间把自己写的一个简单Socket聊天程序的初始设计和服务端细化设计记录了一下,周二终于等来毕业前考的软考证书,然后接下来就是在加班的日子度过了,今天正好周五,打算把客户端的详细 ...
- Socket聊天程序——服务端
写在前面: 昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出Socket聊天程序的服务端大体设计图,如下图: ...
- Socket聊天程序——初始设计
写在前面: 可能是临近期末了,各种课程设计接踵而来,最近在csdn上看到2个一样问答(问题A,问题B),那就是编写一个基于socket的聊天程序,正好最近刚用socket做了一些事,出于兴趣,自己抽了 ...
随机推荐
- codeforces 735C. Tennis Championship(贪心)
题目链接 http://codeforces.com/contest/735/problem/C 题意:给你一个数n表示有几个人比赛问最多能赢几局,要求两个比赛的人得分不能相差超过1即得分为2的只能和 ...
- HDU dp递推 母牛的故事 *
母牛的故事 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submi ...
- .NET Core CSharp 中级篇2-8 特性标签
.NET Core CSharp 中级篇2-8 本节内容为特性标签 简介 标签Attribute是一个非常重要的技术,你可以使用Attribute技术优化精简你的代码.特性标签可以运用在程序集,模块, ...
- 使用Elastic APM监控你的.NET Core应用
作者:Jax 前言 在应用实际的运维过程中,我们需要更多的日志和监控来让我们对自己的应用程序的运行状况有一个全方位的了解.然而对于大部分开发者而言,平时大家所关注的更多的是如何更优雅的实现业务,或者是 ...
- 2019icpc南昌网络赛_I_Yukino With Subinterval
题意 给定一个序列,两种操作,单点修改,询问区间\([l,r]\)值域在\([x,y]\)范围内的连续段个数. 分析 原数组为\(a\),构造一个新的数组\(b\),\(b[i]=(a[i]==a[i ...
- Altera Quartus II 15.0安装
写在前面的话 开始学习之前,我们首先应该选择并安装好自己的开发工具,那么我们用什么软件来编译代码呢?梦翼师兄推荐给大家的是Altera 目前最新的Quartus II 15.0 版本,当然啦,这 ...
- JSP学习笔记(5)——Servlet、监听器、过滤器、MVC模式介绍
MVC模式 在讲解Servlet前,先介绍一下MVC模式. M:model 模型,相当于数据层,用于存放数据,如一个Java中的一个bean类 V:view 视图,相当于页面层,用于显示数据,如一个网 ...
- 体验一下:AndroidX
背景 今天在更新 ButterKnife 到最新版本的时候出现了一个问题,我引入的是 10.1.0 最新版本: implementation 'com.jakewharton:butterknife: ...
- NOIP2008复赛 提高组 第一题
描述 笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼.但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大! 这种方法的具体描述如下:假设maxn是单词中出现次数最多的 ...
- 关于设置tomcat端口为80的事
今天有人要求tomcat服务器的访问地址不能带端口访问, 也就是说只能用80端口访问网站. 那么问题来了, Ubuntu系统禁止root用户以外的用户访问1024以下的商品, 因此tomcat 默认为 ...