控制台客户端
代码
命名空间:
using System;
using System.Net;//
using System.Net.Sockets;//包括
using System.Text;//
using System.Threading;//线程调用
字段:
private static int bufferSize = 1024;
//要连接的IP
private static string host = "127.0.0.1";
//这是要连接的端口号
private static int port = 8080;
private static IPAddress iPAddress = IPAddress.Parse(host);
//保存端口号和类
private static IPEndPoint ToipEnd = new IPEndPoint(iPAddress, port);
//保存本机的端口号和类
//这里是和本机交互,所以IP相同,端口号我直接给绑了45199
private static IPEndPoint localipEnd = new IPEndPoint(iPAddress, 45199);
//俩天序号
private static int Count = 0;
//本地的Socket
static Socket socketOfClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
注:上面代码里我直接给客户端绑定了端口号,实际上不用绑定,连接到服务器会自动分配端口号。
main函数如下:
static void Main(string[] args)
{
//绑定45199,不用绑定
//socketOfClient.Bind(localipEnd);
try
{
//连接8080
socketOfClient.Connect(ToipEnd);
Console.WriteLine("连成功" + socketOfClient.RemoteEndPoint);
Console.WriteLine("开始聊天");
}
catch (Exception e)
{
throw new Exception(e.ToString());
}
Thread threadOfSend = new Thread(SendMessage);
//记得在Start方法里面传送对象,这些对象都会被装箱。
threadOfSend.Start(socketOfClient);
Thread thread1OfRecive = new Thread(ReciveMsg);
thread1OfRecive.Start(socketOfClient);
}
发送方法:
//发送信息方法
private static void SendMessage(object o)
{
Socket socketOfPeer = o as Socket;
while (true)
{
try
{
byte[] buffer = new byte[bufferSize];
string msg = Console.ReadLine();
Console.WriteLine($"[{socketOfClient.LocalEndPoint}] 我:\n{msg}");
if (msg != string.Empty)
{
buffer = Encoding.UTF8.GetBytes(msg);
//这里客户端socket使用Send向Remote发送信息
socketOfPeer.Send(buffer);
}
}
catch (Exception e)
{
throw e;
}
}
}
接受方法:
private static void ReciveMsg(object o)
{
Socket socketOfPeer = o as Socket;
while (true)
{
Console.WriteLine("接受信息。。");
byte[] buffer = new byte[bufferSize];
try
{
int length = socketOfPeer.Receive(buffer);
byte[] realBuffer = new byte[length];
Array.Copy(buffer, 0, realBuffer, 0, length);
string msg = Encoding.UTF8.GetString(realBuffer);
Console.WriteLine($"[{Count++}] [{socketOfPeer.RemoteEndPoint}]说:");
Console.WriteLine($"{msg}");
}
catch (Exception e)
{
throw e;
}
}
}
流程
1.主线程,main函数中,直接connect进行连接(服务器必须提前启动)。连接诶成功后,将socket交给2个线程,循环接受和发送信息即可。
服务器客户端
代码
字段:
private static string serverIP = "127.0.0.1";
private static int host = 8080;
private static int endHost = 45199;
private static IPAddress iPAddress = IPAddress.Parse(serverIP);
private static IPEndPoint toIpEnd = new IPEndPoint(iPAddress, endHost);
private static IPEndPoint localIpEnd = new IPEndPoint(iPAddress, host);
private static Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static int count = 0;
private static int bufferMax = 1024;
main函数:
static void Main()
{
//服务器一定要绑定端口号和ip
server.Bind(localIpEnd);
try
{
Console.WriteLine($"本机:{server.LocalEndPoint}");
Console.WriteLine($"正在连接");
//Connect是直接连接指定的ip和端口,这里要使用Listen
//server.Connect(toIpEnd);
//listen函数进行监听,最大连接数是10个
server.Listen(10);
}
catch (Exception e)
{
Console.WriteLine($"{e}");
}
Thread threadSend = new Thread(Run);
threadSend.Start(server);
}
Run方法:
private static void Run(object o)
{
Socket socket = o as Socket;
try
{
//这是保存的新的Socket
Socket connSocket = socket.Accept();
//此处使用一下语句会报错,因为旧的socket没有保存连接的信息
//Console.WriteLine($"{socket.RemoteEndPoint}");
//以下是对的
//Console.WriteLine($"{connSocket .RemoteEndPoint}");
//客户和服务器连接成功。
Console.WriteLine("{0}成功连接到本机。", connSocket.RemoteEndPoint);
//接下来的事情交给会话线程
Thread recTh = new Thread(ReciveMsg);
recTh.Start(connSocket);
Thread sendTh = new Thread(SendMessage);
sendTh.Start(connSocket);
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
**注:
以上方法中,第一个socket 是本服务端的socket对象。
第二个conSocket是在socket对象接受到连接时new的新对象,这个新对象才包含连接对象的信息,而socket是没有的。
可以打印socket的LocalEndPoint和RemoteEndPoint,查看各个socket的信息。
服务器中会话线程的函数是和客户端一样的。
**
流程
1.mian函数中,主线程绑定服务器IP(bind),开启侦听(listen),开启一个新线程,将socket对象交给新线程,运行run函数 处理连接信息。
2.在Run方法中,服务器socket进行监听(listen函数),使用Accept接收连接。如果没有连接,Accept会阻塞(因为只有接收到连接,后面的代码才能进行与之通信),直到Accept接收到连接并保存。这时服务器和客户端的socket已经建立起连接了。这时开始2个新线程,循环处理发送和接受信息。
socket运行流程图示
参考文章
http://www.west999.com/cms/wiki/code/2018-07-20/36646.html