原文:你也可以写个聊天程序 - 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

效果图

结束

你也可以写个聊天程序 - C# Socket学习1的更多相关文章

  1. 你也可以写聊天程序 - C# Socket学习1

    简述 我们做软件工作的虽然每天都离不开网络,可网络协议细节却不是每个人都会接触和深入了解.我今天就来和大家一起学习下Socket,并写一个简单的聊天程序. 一些基础类 首先我们每天打开浏览器访问网页信 ...

  2. 聊天程序——基于Socket、Thread (二)

    聊天程序简述 1.目的:主要是为了阐述Socket,以及应用多线程,本文侧重Socket相关网路编程的阐述.如果您对多线程不了解,大家可以看下我的上一篇博文浅解多线程 . 2.功能:此聊天程序功能实现 ...

  3. 你也可以写个服务器 - C# Socket学习2

    前言 这里说的服务器是Web服务器,是类似IIS.Tomcat之类的,用来响应浏览器请求的服务. Socket模拟浏览器的Url Get请求 首先浏览器的请求是HTTP协议.我们上一篇说过,HTTP是 ...

  4. Node.js + Web Socket 打造即时聊天程序嗨聊

    前端一直是一块充满惊喜的土地,不仅是那些富有创造性的页面,还有那些惊赞的效果及不断推出的新技术.像node.js这样的后端开拓者直接将前端人员的能力扩大到了后端.瞬间就有了一统天下的感觉,来往穿梭于前 ...

  5. java Socket多线程聊天程序

    参考JAVA 通过 Socket 实现 TCP 编程 参考java Socket多线程聊天程序(适合初学者) 以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包 ...

  6. Socket聊天程序——Common

    写在前面: 上一篇记录了Socket聊天程序的客户端设计,为了记录的完整性,这里还是将Socket聊天的最后一个模块--Common模块记录一下.Common的设计如下: 功能说明: Common模块 ...

  7. Socket聊天程序——客户端

    写在前面: 上周末抽点时间把自己写的一个简单Socket聊天程序的初始设计和服务端细化设计记录了一下,周二终于等来毕业前考的软考证书,然后接下来就是在加班的日子度过了,今天正好周五,打算把客户端的详细 ...

  8. Socket聊天程序——服务端

    写在前面: 昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出Socket聊天程序的服务端大体设计图,如下图: ...

  9. Socket聊天程序——初始设计

    写在前面: 可能是临近期末了,各种课程设计接踵而来,最近在csdn上看到2个一样问答(问题A,问题B),那就是编写一个基于socket的聊天程序,正好最近刚用socket做了一些事,出于兴趣,自己抽了 ...

随机推荐

  1. yum本地安装rrdtool报错,提示版本冲突的解决办法

    [root@mysql-mon40 mm]# yum localinstall rrdtool-1.4.7-1.el6.rfx.x86_64.rpm -y 错误: Multilib version p ...

  2. Android Studio 之 数据存活【2】,返回桌面切换回来,内容还保存着

    使用ViewMode SavedState来使得ViewModel在系统杀死后台进程的时候得以存活. 本集使用到的库,需要在gradle中添加: implementation 'androidx.li ...

  3. [技术博客]React Native——HTML页面代码高亮&数学公式解析

    问题起源 原有博文显示时代码无法高亮,白底黑字的视觉效果不好. 原有博文中无法解析数学公式,导致页面会直接显示数学公式源码. 为了解决这两个问题,尝试了一些方法,最终利用开源类库实现了页面美化. (失 ...

  4. mvn常见参数命令讲解

    关于-N -N,--non-recursive Do not recurse into sub-projects 意思是,不递归到子项目(子模块). 举例: 一个父项目下Father面有3个子项目A. ...

  5. systemctl enable rc-local.service error

    /******************************************************************************* * systemctl enable ...

  6. web项目脱敏白名单管理

    写在前面 下午没事, 看一下同事写的脱敏白名单管理功能. 所谓的脱敏就是将页面中查询出的信息列表当中的身份证号及手机号等关键信息部分用*号代替.该功能主要思路是新增一个页面用于配置哪些页面以及页面中哪 ...

  7. centos7安装yum

    由于不小心把自带的yum给卸载了,卸载命令:rpm -qa yum: 在浏览器打开链接:http://mirrors.163.com/centos/6/os/x86_64/Packages/下载这四个 ...

  8. Kibana启动后外网访问不了

    问题 Kibana启动后,使用外网访问 http://ip地址:5601 访问不了日志中最后显示 "statusCode":302 ,在控制台 curl http://localh ...

  9. android双进程守护,让程序崩溃后一定可以重启

    由于我们做的是机器人上的软件,而机器人是24小时不间断服务的,这就要求我们的软件不能退出到系统桌面.当然最好是能够做到程序能够不卡顿,不崩溃,自己不退出.由于我们引用了很多第三方的开发包,也不能保证他 ...

  10. vue---引入外部样式

    在用 vue-cli 进行项目开发的时候,经常会遇到要引入外部的样式,那该怎么做的呢? 示例:使用 import 引入 scss import variables from '@/assets/sty ...