一、TCP客户端与服务器端通信
服务器初始化
(1)调用socket,创建文件描述符
(2)调用bind,将文件描述符与ip/port连接起来。若端口号已被占用,则bind失败
(3)调用listen,声明该文件描述符是服务器的一个文件描述符,为之后的accept做准备
(4)调用accept,并处于阻塞状态,等待客户端连接
建立连接
(1)调用socket,创建文件描述符
(2)调用connect,向服务器发起连接请求。
(3)connect会发送一个请求SYN段并阻塞等待服务器应答(第一次)
(4)服务器收到SYN,会给客户端发送一个确认应答的同时发送一个请求(SYN+ACK),表示同意建立连接(第二次)
(5)客户端收到客户端发的SYN+ACK段,表明客户端连接已建立成功,进入ESTABLISHED状态,从connect()。客户端再向服务器发送一个ACK段,服务器收到后则服务器端连接也建立成功,服务器也进入ESTABLISHED状态。
数据传输
(1)连接建立成功后,在同一连接、同一时刻,通信双方可同时写数据(全双工)
(2)服务器端从accept()返回后调用read()开始读数据,若没有数据则阻塞等待
(3)客户端调用write()向服务器发送数据请求,客户端收到之后调用read()处理请求,此过程服务器调用read()阻塞等待
(4)服务器调用write()将处理好的请求发送给客户端,再次调用read()等待下一个请求
(5)客户端收到后从read()返回,发送下一条请求,如此循环下去
断开连接
(1)没有数据处理了,则客户端调用close()关闭连接,给服务器端发送一个断开连接请求FIN段(第一次)
(2)服务器收到客户端的FIN段,给客户端发送一个确认应答ACK段表明同意断开连接,客户端收到ACK段并调用read()返回0,表明客户端连接已断开(第二次)
(3)read()返回0之后,服务器知道客户端已断开连接,它也调用close()关闭连接,给客户端发送一个断开连接请求FIN段(第三次)
(4)客户端接收到服务器端发送的FIN段,给服务器一个确认应答ACK段,表明同意断开连接。客户端进入TIME_WAIT状态,服务器收到客户端的ACK后则服务器断开连接。
二、编写网游客户端
- 创建项目
- 基本界面设计
- 代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Media;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private NetworkStream stream;
private TcpClient tcpClient;
SoundPlayer player = new SoundPlayer("D:/Fuchikami/nene/Lopu$ - So Cute~.mp3");
Socket socket_send;
void send_stream(string str)
{
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
System.Text.Encoding GBK = System.Text.Encoding.GetEncoding("GBK");
byte[] buffer = GBK.GetBytes(str + "\n");
stream.Write(buffer, 0, buffer.Length);
}
void receive_stream()
{
byte[] receive_data = new byte[1024];
//定义编码格式
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);//为使用GB2312做准备
System.Text.Encoding GBK = System.Text.Encoding.GetEncoding("GBK");
if (stream.CanRead)
{
int len = stream.Read(receive_data, 0, receive_data.Length);
string msg = GBK.GetString(receive_data, 0, receive_data.Length);
string str = "\r\n";
char[] str1 = str.ToCharArray();
string[] messy_code = { "??[2J ", "[5m", "[44m", "[37;0m", "[1;33m", "[1;32m", "[1;31m" };
string[] msg1 = msg.Split(str1);//以换行符为分隔符
for (int j = 0; j < msg1.Length; j++)//逐行显示
{
msg1[j] = msg1[j].Replace(messy_code[0], " ");
msg1[j] = msg1[j].Replace(messy_code[1], " ");
msg1[j] = msg1[j].Replace(messy_code[2], " ");
msg1[j] = msg1[j].Replace(messy_code[3], " ");
msg1[j] = msg1[j].Replace(messy_code[4], " ");
msg1[j] = msg1[j].Replace(messy_code[5], " ");
msg1[j] = msg1[j].Replace(messy_code[6], " ");
listBox1.Items.Add(msg1[j]);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
}
private void pictureBox1_Click(object sender, EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
tcpClient = new TcpClient();
//套接字建立连接
socket_send = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint point = new IPEndPoint(IPAddress.Parse("10.1.230.74"), 3900);
socket_send.Connect(point);
try
{
//向指定的IP地址的服务器发送连接请求
tcpClient.Connect("10.1.230.74", 3900);
listBox1.Items.Add("连接成功");
stream = tcpClient.GetStream();
receive_stream();//接收字节流并显示在屏幕上
}
catch
{
listBox1.Items.Add("服务器未启动");
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (tcpClient.Connected)
{
string action = textBox1.Text.ToString();
listBox1.Items.Add("输入的信息为:" + action);
send_stream(action);
receive_stream();
}
else
{
listBox1.Items.Add("连接已断开");
}
}
private void button3_Click(object sender, EventArgs e)
{
if (stream != null)
{
stream.Close();
tcpClient.Close();
socket_send.Close();
}
listBox1.Items.Add("退出游戏");
}
private void button4_Click(object sender, EventArgs e)
{
player.Load();
player.Play();
}
}
}