TCP/IP协议作为现代网络通讯的基石,内容包罗万象,直接去理解理论是比较困难的;然而通过实践先理解网络通讯的理解,在反过来理解学习TCP/IP协议栈就相对简单很多。C#通过提供的Socket API实现了对TCP/IP协议栈的封装,让实现C/S模型变得更加简单,对于入门TCP/IP协议学习十分有帮助。
Socket通讯实现参考标准的流程如图所示,
·
服务器工作:
1.创建Socket套接字,绑定指定host,并监听。
2.线程堵塞等待用户端请求,当客户端请求到达时建立连接。
3.连接建立完成后,读取请求并处理,然后将处理结果返回给客户端。
4. 服务器等待客户端关闭连接,连接关闭后,一次Socket通讯结束。
客户端工作:
1.创建Socket套接字, 建立连接,发送连接请求数据
2. 请求成功后,等待服务器返回响应
3.服务器回应成功后,处理数据,直到主动关闭连接。
了解了上述Socket通讯的主要过程,在结合C#网络编程的特性,就可以实现如下C/S服务器。客户端的实现比较简单,主要分四个步骤:
1.建立tcp连接(等同于socket 和 connect),处理连接失败状态
2.对于建立成功的连接,发送数据,并等待服务器返回
3.接收数据,输出到编辑框
4.关闭连接
private void button1_Click(object sender, EventArgs e)
{
try
{
string s_ipaddress0 = textBox1.Text;
string s_ipaddress1 = textBox2.Text;
string s_ipaddress2 = textBox3.Text;
string s_ipaddress3 = textBox4.Text;
string s_port = textBox5.Text; //限定port的合法性
bool isPort;
int port;
isPort = Int32.TryParse(s_port, out port);
if (!(isPort && port >= && port <= ))
{
OutToClient("Port is Invalid!");
return;
} //限定ip地址的合法性
string host = s_ipaddress0 + "." + s_ipaddress1 + "." + s_ipaddress2 + "." + s_ipaddress3;
if (!IsIpAdress(host))
{
OutToClient("IpAdress is invaild!");
return;
}
IPAddress ip = IPAddress.Parse(host);
IPEndPoint ipe = new IPEndPoint(ip, port); //Ip端口绑定 如192.168.1.48:80 //发送框不能为空, 不然会导致GetBytes出错
if (textBox6.Text == "")
{
OutToClient("Send Text is empty!");
return;
} OutToClient("Conneting..."); int timeout = ; //tcpClient连接到指定端口,并处理超时
TcpClient NetworkClient = TimeOutSocket.Connect(ipe, timeout);
NetworkStream ntwStream = NetworkClient.GetStream();
if (!ntwStream.CanWrite)
{
OutToClient("Data can not be write!");
return;
} //向绑定地址端口发送数据
OutToClient("Client send:" + textBox6.Text);
string sendStr = textBox6.Text;
byte[] by_send = Encoding.ASCII.GetBytes(sendStr);
ntwStream.Write(by_send, , by_send.Length); //等待服务器端返回数据
string recvStr = "";
byte[] recvBytes = new byte[];
int bytes = ntwStream.Read(recvBytes, , recvBytes.Length); //将接收到数据转发输出到Client编辑框
recvStr += Encoding.ASCII.GetString(recvBytes, , bytes);
OutToClient(recvStr); //关闭连接, 一次发送完成
ntwStream.Close();
NetworkClient.Close();
OutToClient("Conneting Close!\r\n");
}
catch (ArgumentNullException ii)
{
OutToClient("ArgumentNullException: "+ ii);
}
catch (SocketException ii)
{
OutToClient("Socket failed! ");
}
catch (Exception ii)
{
OutToClient("Conneting faild!" + ii);
}
}
服务器内的代码涉及到的知识多一些
1.通过按键1开辟监听线程,用于接收
2.监听线程创建Socket套接字,绑定到指定端口并监听
3.监听线程堵塞在等带连接建立
4.连接建立后监听线程读取客户端数据并回送给客户端处理后数据,然后循环堵塞在连接建立
5.通过按键2删除Socket套接字触发异常,结束线程
按照上述的流程,程序如下:
//新建线程 开始监听
private void button1_Click(object sender, EventArgs e)
{
try
{
isConnectExit = false;
button1.Enabled = false;
button2.Enabled = true; Thread Listen_thread = new Thread(ListenClientConnect);
Listen_thread.IsBackground = true;
Listen_thread.Start();
}
catch (Exception ii)
{
OutToServer("Connet faild!" + ii);
}
}
//监听线程 主要处理数据接收发送
private void ListenClientConnect(object obj)
{
string s_ipaddress0 = textBox1.Text;
string s_ipaddress1 = textBox2.Text;
string s_ipaddress2 = textBox3.Text;
string s_ipaddress3 = textBox4.Text;
string s_port = textBox5.Text; //限定port的合法性
bool isPort;
int port;
isPort = Int32.TryParse(s_port, out port);
if (!(isPort && port >= && port <= ))
{
OutToServer("Port is Invalid!");
return;
} //限定ip地址的合法性
string host = s_ipaddress0 + "." + s_ipaddress1 + "." + s_ipaddress2 + "." + s_ipaddress3;
if (!IsIpAdress(host))
{
OutToServer("IpAdress is invaild!");
return;
} IPAddress ip = IPAddress.Parse(host);
IPEndPoint ipe = new IPEndPoint(ip, port); //新建socket通讯 绑定指定IP+Port 并监听
server_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server_socket.Bind(ipe);
server_socket.Listen(); OutToServer("wait for connect...");
while (isConnectExit == false)
{
Socket CurrentSocket;
try
{
//等待连接 完成三次握手
CurrentSocket = server_socket.Accept();
if(isConnectExit == true)
{
CurrentSocket.Close(); // 关闭Socket
server_socket.Close();
break;
}
OutToServer("Get a Connect!");
ServerProcess(CurrentSocket);
}
catch
{
OutToServer("Connet exti!");
}
}
}
//断开连接
private void button2_Click(object sender, EventArgs e)
{
isConnectExit = true;
button2.Enabled = false;
button1.Enabled = true;
if (server_socket != null)
{
server_socket.Close();
}
}
如此就完成了简单的C/S服务器功能。
实际测试界面如下:
具体源码参考:C/S架构客户端-服务器下载
备注:本程序仅用于演示TCP socket通信流程,服务器端能够监听的IP地址只有本机IP或者127.x.x.1(环回地址), 端口不能为80(和本机http端口冲突)。