最近在学习网络编程,发现也并没有想象中的那么难。以前总是有意无意听身边朋友在谈论多线程、Socket网络编程等等,由于对网络编程一无所知,所以给我一种“应该很难”的错觉。
一台计算机和另一台计算机是如何通过网络互相通信、传递数据的?
HTTP协议、TCP/IP、UDP到底是什么玩意?
进程和线程有什么区别?
IP地址、端口又是什么?
大名鼎鼎的Socket是用来干嘛的?
这些问题都是以前最困扰我的,这两天终于给弄称敨了。在这里简单巩固下,如果你也想知道这些问题的答案,就去看有关网络编程的书吧,真的很简单的。
1.首先,什么是IP地址和端口??
连入了一个网络(可以是互联网也可以是局域网)上的每一台计算机,都会被自动(或手动)分配一个IP地址,和手机有唯一的手机号码一样,IP地址也是用来标识每一台计算机身份的,一台计算机要想与另一台计算机通信,就必须要知道它的IP地址。
连入了互联网上的计算机,IP地址是随机分配的。
通过IP地址找到计算机就可以通信了吗?当然不能。
我们知道,聊QQ,玩游戏,上网都是安装在计算机上的一个个软件。我们找到了计算机,但是数据到底是从哪一个应用程序(软件)发出来的呢,无法确定——端口,就是用来标识在计算机上运行的一个个应用程序的。端口号可以是0到65535之间的任一数字。一般10000以内的端口号已经被系统中的重要程序给占用了。比如通过浏览器发出的数据的端口号默认是80。
有了IP和端口号,就可以与计算机上的应用程序进行数据传输了。
2.什么是HTTP协议、TCP/IP和UDP?
HTTP协议是超文本传输协议的总称,是在浏览器上访问页面使用最广泛的一种协议,它是无状态的,即它只负责发送请求和响应,执行完了就完了,不记录任何数据和信息。简单说,协议是为了方便在互联网上互相通信、共享资源、传递数据,大家约定好了的,都必须遵守的一种规范和标准。
每一台计算机本身是相互独立的,在计算机或主机上都有一个网卡或路由器,IP地址就是给网卡或路由器分配的,TCP/IP和UDP也是协议,它们是实现计算机与计算机相互通信的协议。
3.进程和线程
计算机上每一个正在运行的应用程序就是一个进程,它是用来方便操作系统管理应用程序的。
线程是操作系统为了管理数据的计算和执行而分配的一段时间片,大约是20毫秒。
每一个进程可以有一个或多个线程,在每一个C#程序中,默认有一个主线程,它会执行main()方法里面的代码,从上而下一行一行执行。
当需要同时执行某段代码,即在执行某段代码的同时又要执行另外一段代码,就要另外开辟一个线程,即用到多线程。
比如聊天系统,一个人可以同时与多人进行聊天,他就需要接收来自多台计算机发送过来的消息,如果不用单线程的话,就只能接收到首先发送过来的那台计算机的数据,而其他计算机发送过来的数据就无法读取。
最后,让我们来认识声名显赫的Socket。
Socket其实也很简单,它就是.NET框架下的一个类,叫做套接字类。上面我们说了那么多,接收数据和发送数据到底是谁来做的——就是我们的Socket,通过它,我们就可以实现计算机与计算机进行数据传输了。
Socket实现数据传输的几个关键方法:Listen()、 Bind()、 Connect()、 Accept()、Send()、Receive()
想做网络编程方面的开发,这些都是必须要知道的最基础知识。
通过这几天的看书,正如吕老师所说,技术真的没有什么了不起的,不要望而生畏,都十分简单,但是什么才是最重要的,我目前不得而知。相信,随着不断的学习和进步,我会知道的。
下面是我用Socket做的一个聊天小程序。大家会发现,也很简单。
参考代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
namespace Socket_Chat
{
public partial class Form1 : Form
{
private byte[] receiveMsgBtyes = new byte[1024];
private static StringBuilder receiveMessage = new StringBuilder();
private IPHostEntry hostEntry;
private int localPort;
//接收消息的套接字
private Socket socketReceiver;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//程序加载即创建Socket(套接字)对象的实例
this.socketReceiver = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//为本地进程随机生成一个端口号
this.localPort = (new Random()).Next(10000,20000);
this.txtLocalPort.Text = localPort.ToString();
//获取本地主机IP对象实例
this.hostEntry = Dns.GetHostByName(Dns.GetHostName());
//IPHostEntry hostEntr = Dns.GetHostEntry(Dns.GetHostName());
//IPHostEntry hostEntr = Dns.GetHostEntry(Dns.GetHostName());
string hostIp = hostEntry.AddressList[0].ToString();
this.txtLocalIp.Text = hostIp;
//IPAddress hostIpAddress = IPAddress.Parse(hostIp);
IPAddress hostIpAddress = hostEntry.AddressList[0];
//IPAddress a = Dns.GetHostAddresses(Dns.GetHostName())[0];
//获取本地主机进程端口实例
IPEndPoint hostPort = new IPEndPoint(hostIpAddress, this.localPort);
//IPEndPoint hostPort = new IPEndPoint(hostEntry.AddressList[0].Address, localPort);
//绑定本地主机进程端口
this.socketReceiver.Bind(hostPort);
//开始监听
this.socketReceiver.Listen(10);
//启动进程接收消息(因为要接收多方发送过来的消息)
Thread threadReceive = new Thread(new ThreadStart(this.ReceiveMessage));
threadReceive.IsBackground = true;
threadReceive.Start();
}
//接收消息
private void ReceiveMessage()
{
//开始循环接受对方发出连接请求,一旦连接成功即返回一个套接字对象,该套接字包含对方IP和端口号
//连接成功前此线程处于阻塞状态
while (true)
{
try
{
Socket clientSocket = this.socketReceiver.Accept();
//接收一串字节数组型的数据存入缓存
int receiveMsgLength = clientSocket.Receive(this.receiveMsgBtyes);
//将字节数组转换成字符串
string strMessage = Encoding.Default.GetString(this.receiveMsgBtyes, 0, receiveMsgLength);
Form1.receiveMessage.AppendLine(string.Format("来自{0}:{1}的消息 {2}:", this.hostEntry.AddressList[0].ToString(),
this.txtFriendPort.Text, DateTime.Now.ToString()));
Form1.receiveMessage.AppendLine("/r/n" + strMessage + "/r/n");
this.txtMessageRecord.Text = Form1.receiveMessage.ToString();
}
catch (Exception e)
{
MessageBox.Show(e.Message,"错误提示");
this.socketReceiver.Shutdown(SocketShutdown.Both);
this.socketReceiver.Close();
break;
}
}
}
private void btnSend_Click(object sender, EventArgs e)
{
if (this.txtFriendIp.Text == "")
{
MessageBox.Show("请输入对方IP!", "提示");
return;
}
if (this.txtFriendPort.Text == "")
{
MessageBox.Show("请输入对方端口号!", "提示");
return;
}
if (this.txtSendMessage.Text == "")
{
MessageBox.Show("消息不能为空!", "提示");
return;
}
//发送消息的套接字
Socket socketSender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取对方主机IP对象实例
IPAddress friendIP = IPAddress.Parse(this.txtFriendIp.Text);
//获取对方主机进程端口实例
IPEndPoint friendPort = new IPEndPoint(friendIP, int.Parse(this.txtFriendPort.Text));
//开启连接
socketSender.Connect(friendPort);
Form1.receiveMessage.AppendLine(string.Format("{0}:{1} {2}:", this.hostEntry.AddressList[0].ToString(),
this.localPort.ToString(), DateTime.Now.ToString()));
Form1.receiveMessage.AppendLine("/r/n" + this.txtSendMessage.Text + "/r/n");
//发送消息
socketSender.Send(Encoding.Default.GetBytes(this.txtSendMessage.Text));
this.txtMessageRecord.Text = Form1.receiveMessage.ToString();
this.txtSendMessage.Text = "";
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
//this.SendMessageByKey += btnSend_Click;
this.btnSend.Click += new System.EventHandler(this.btnSend_Click);
}
}
}
}