标签:
socket套接字就是程序间的电话机;
协议:电脑与电脑或者应用程序与应用程序之间默认的语言;
客户端与服务器的某个应用程序连接, 要连接到服务器,首先要知道服务器的IP地址;但是仅仅知道IP地址不行,只知道IP
地址连接到的是服务器;服务器上这么多应用程序,他们的IP地址都是一样的;这个时候如果知道端口号,就可以连接到你想连接
的应用程序;所以说你要连接服务器上的某个应用程序,需要知道IP地址和端口号;
男生去女生宿舍找女生,中间有宿管大妈搁着,女生宿舍相当于服务器,男生相当于客户端,宿管大妈相当于负责监听的socket
女生相当于负责通信的socket;
客户端要连接服务器,首先要知道服务器端的IP地址;服务器上有很多应用程序,客户端是连接到服务器上的某个应用程序;这些
应用程序都在同一台服务器上,他们的IP地址是一样的,但是如何找到我要连接的应用程序呢,这时候就要找到我要连接的应用
程序的端口号;有了IP地址和端口号,这时候我们就能准确的连接到服务器上的应用程序;
Tcp协议安全稳定,一般不会发生数据丢失;TCP传输过程中,会经历三次过程,我们称之为三次握手;TCP协议要求我们必须有服务器;
这个请求是客户端发给服务器的,服务器不能给客户端发请求;因为服务器不知道客户端在哪里;
三次握手就像客户端发送给服务器 你有空吗? 服务器返回我有空, 然后客户端发送我知道你有空了;TCP只有在这三次握手成功后,
才和服务端收发数据;所以TCP好处是安全稳定,但是效率相对低;
UDP协议快速,效率高,但是不稳定,容易发生数据丢失;客户端给服务器发消息了,我不管你服务器有没有空,我就给你发;如果服务器
很忙的话,就没时间处理信息,造成数据丢失;没有好坏,各有各的优点;视频传输的时候用的是UDP;
==========================================================================================================================================//服务器
//模拟的是服务器和客户端之间的通信;下面这段是服务器端;服务器端干的第一件事是创建一个负责监听的Socket;监听的是本机应用程序的端口号;
但是端口号里也包含着本机应用程序的IP地址;监听的目的就是等待客户端的连接,客户端连接过来,我这个负责监听的socket就知道这个信息了,
当知道之后,就创建一个负责通信的Socket;负责通信的Socket是负责监听的Socket调用Accept();为什么要写在一个循环里面,因为我们服务端会有很多
客户端链接过来,应该是每一个客户端都为他创建一个负责通信的Socket;为什么写在一个函数里面让线程去执行呢;因为Accept会阻碍我们主线程的运行;
客户端如果一直不连接过来会卡死,因此我们创建一个新的线程去调用这个方法;函数当中需要用到负责监听的Socket,我们选择以参数的形式传进来,但是
被线程执行的函数如果有参数的话必须是object类型;因此在函数当中进行类型转换as,我们在调用的时候,传的是负责监听的Socket,Listen相当于进入公园
只有10扇门,想进去就要排队;
//当点击开始监听的时候,在服务器端创建一个负责监听IP地址和端口号的Socket
Socket SocketWatch = new Socket(AddressFamily.InterNetWork,SocketType.Stream,ProtocolType.Tcp)
) //0,创建一个负责监听的Socket
IPAddress ip = IPAddress.Any; //IPAddress.Parse(txtServer.Text);
//创建端口号对象;
IPEndPoint point = new IPEndPoint(ip,Convert.ToInt32(TextPort.Text)); //端口号中同样有我们的IP地址;
//监听
SocketWatch.Bind(point); //1.绑定监听端口;
ShowMsg("监听成功");
//服务器在一个点内能接纳的客户端是有上限的,这个上限不是指服务器的容量问题;指的是在一秒内能连接的数量;
SocketWatch.Listen(10); //一个时间点内能接纳的人数为10个人;第十一个人来了排队; //2.设置监听队列;
如果一个服务器最多容纳20000人,怎么增加数量,最好的办法就是加服务器;百度服务器多,全中国都有,连最近的那个服务器;
-----------------------------------------Socket SocketSend = SocketWatch.Accept(); //3.他能接收客户端的连接,并创建负责通信的Socket; //等待客户端连接,如果客户端一直不连的话会死
掉,解决办法创建一个新线程
-----------------------------------------//如果单单这样写的话只能连接一个人;解决2个问题,1假死(创建线程) 2只能连一个客户端(写一个While循环)
-----------------------------------------ShowMsg(SocketSend.RemoteEndPoint.ToString()); //通过负责通信的Socket可以获得远程客户端的IP和端口号,IP在端口号内;192.168.11.87:29990
----------------------------------------- //telnet是本地连接的一个服务器; 控制面板,程序和功能;启用或关闭windows功能;把Telnet服务器和Telnet客户端打上勾,我们通过Telnet去连接服务
器;
Thread th = new Thread(Listen);
th.IsBackground;
th.Start(SocketWatch);
void ShowMsg(string str)
{
txtLog.AppendText(str + "\r\n");(追加的意思,在每行结束的时候换行);
}
Socket SocketSend;
void Listen(Object o) //这个方法最终要被新线程执行,线程所执行的函数,如果有参数,必须是object类型
{
Socket SocketWatch = o as Socket;
try{
while(true)
{
SocketSend = SocketWatch.Accept(); //负责监听的Socket只干了这件事,剩下的与客户端通信都与负责通信的Socket来做;//一旦来一个新的用户,原来的那个负责通信的Socket就没了,导致服务
端只能给最后一个连接的客户端发送信息; 跟哪个客户端通信取决于拿到的是哪个Socket;IP地址不能发消息,得是IP地址对应的Socket才能发;
dicSocket.Add(SocketSend.RemoteEndPoint.ToString(),SocketSend); //拿到远程客户端的IP和其对应的Socket对象;
CboUsers.Items.Add(SocketSend.RemoteEndPoint.ToString());将远程客户端的IP存储到下拉框中;
ShowMsg(SocketSend.RemoteEndPoint.ToString()+ ":连接成功");
//客户端连接过来之后,我们就要接受客户端发送过来的消息;
//由于下面这几句代码只执行一遍,因此无法连续发送字符串,解决办法写一个while循环,但是while循环又会导致程序假死
因此再创建一个线程,去执行这个方法;
------------------------------------------------------------------------------- byte[] buffer = new byte[1024*1024*2];
------------------------------------------------------------------------------- //实际接收到的有效的字节数;
------------------------------------------------------------------------------- int r = SocketSend.Receive(buffer);
------------------------------------------------------------------------------- //这就是客户端发送过来的字符串,我们把它显示在文本框里
------------------------------------------------------------------------------- string str = Encoding.UTF8.GetString(buffer,0,r);
------------------------------------------------------------------------------- ShowMsg(SocketSend.RemoteEndPoint + ":" + str;
//开启一个新线程不停的接收客户端发送过来的消息
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start(SocketSend);
}
catch
{
}
}
}
在FormLoad事件里
Control.CheckForIllegalCrossThreadCalls = false;
//这段就是服务器不停的接收客户端发来的消息;
void Receive(object o)
{
Socket SocketSend = o as Socket;
try
{
while(true)
{
byte[] buffer = new byte[1024*1024*2];
//实际接收到的有效的字节数;
int r = SocketSend.Receive(buffer);
if(r == 0)
{
break; //如果关闭客户端接收到的就是空消息,注意,空消息和空格是不一样的,在线的话发不出空消息
}
//这就是客户端发送过来的字符串,我们把它显示在文本框里
string str = Encoding.UTF8.GetString(buffer,0,r);
ShowMsg(SocketSend.RemoteEndPoint + ":" + str;
}
}
catch
{
//不写的话,就算出现异常也不会显示给用户;给用户的感觉就是什么都没异常;
}
}
凡是操作网络的东西,肯定会引发各种各样的异常;因此要try catch,微软在写源代码的时候用了大量的try catch
//服务器端给客户端发送消息
string str = txtMsg.Text;
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
//为了区分是文本,文件,震动,在字节数组buffer前面加一个数字0,表示文本, 1表示文件,2表示震动
//声明一个长度为buffer.length+1的数组,buffer[0] = 0;然后一个循环赋值后面的;方法1
//方法2用泛型集合
List<byte> list = new List<byte>();
list.Add(0);
list.AddRange(buffer);
//将泛型集合转化为数组
byte[] newbuffer = list.ToArray();
//获得用户在下拉框选中的IP地址;
string ip = CboUsers.SelectItem.ToString();
dicSocket[ip].Send(newbuffer);
//socketSend.Send(buffer);
//将远程连接的客户端的IP地址和对应的Socket联系起来;用键值对存储;
Dictionary<string,Socket> dicSocket = new Dictionary<string,Socket>();
//为了区分服务端发送给客户端的是文本,文件,还是震动;我们就在传送的字节数组的最前面加上一个数字来判断;0表示文本,1表示文件,2表示震动;
//在选择发送文件的按钮里
openfileDialog ofd = new openfileDialog();
ofd.InitialDirectory = @"桌面路径";
ofd.Title = "请选择要发送的文件";
ofd.Filter = "所有文件|*.*";
ofd.ShowDialog();
TxtPath.Text = ofd.FileName;
//当点击发送按钮的时候,把文件发送过去;//发送大文件比这个要麻烦,因为涉及到断点续传的问题;发的时候要把大文件切割成小文件;
//获得要发送文件的路径
string path = txtPath.Text;
using(FileStream fsRead = new FileStream(path,FileMode.open,FileAccess.Read))
{
byte[] buffer = new byte[1024 * 1024 * 5];
int r = fsRead.Read(buffer,0,buffer,length);
List<byte> list = new List<byte>();
list.Add(1);
list.AddRange(buffer);
byte[] newbuffer = list.ToArray();
dicSocket[cboUsers.SelectedItem.Tostring()].Send(newbuffer,0,r+1,SocketFlags.None);
}
当点击震动按钮的时候
byte[] buffer = new byte[1];
buffer[0] = 2;
dicSocket[cboUsers.SelectedItem.Tostring()].Send(buffer);
============================================================================================================================//客户端
Socket SocketSend;
//客户端不需要负责监听的Socket
try
{
SocketSend = new Socket(AddressFamily.InterNetWork,SocketType.Stream,ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse(txtServer.Text);
IPEndPoint point = new IPEndPoint(ip,Convert.ToInt32(txtPort.Text));
//获得要连接的远程服务器应用程序的IP地址和端口号;
SocketSend.Connect(point);
ShowMsg("连接成功");
//开启一个新的线程,不停地接收服务端发来的消息
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start();
}
catch
{
}
void ShowMsg(string str)
{
txtLog.AppendText(str + "\r\n");
}
//先开启服务器监听,在客户端连接;
winform开启第二个项目,右键项目,调试,启动新实例;
//客户端发送信息到服务器
string str = TextMsg.Text.Trim();
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
SocketSend.Send(buffer);
//不停接收客户端接收服务端的消息
void Receive()
{
while(true)
{
try
{
byte[] buffer = new byte[1024*1024*2];
//实际接收到的有效字节数;
int r = socketSend.Receive(buffer);
if(r == 0)
{
break; //因为还有两个buffer[0] ==1 ==2也要用到这个,干脆写到外面了
}
//这里要判断字节数组的第一位
if(buffer[0] == 0) //表示发送的是文字消息
{
//由于包含前面那个0 因此这句话要改为 string s = Encoding.UTF8.GetString(buffer,0,r);
string s = Encoding.UTF8.GetString(buffer,1,r - 1);
ShowMsg(SocketSend.RemoteEndPoint + "" + s);
}
if(buffer[0] == 1) //缺陷是发送的.txt还是.jpg还是其他类型的文件不知道,解决办法是第一位后也创建一个协议,
{
SavaFileDialog sfd = new SavaFileDialog();
sfd.InitialDirectory = @"桌面路径";
sfd.Title = "请选择要保存的文件";
sfd.Filter = "所有文件|*.*";
sfd.ShowDialog(this);
string path = sfd.FileName;
using(FileStream fsWrite = new FileStream(path,FileMode.OpenOrCreate,FileAccess.Write))
{
fsWrite.Write(buffer,1,r-1);
}
MessageBox.Show("保存成功");
}
if(buffer[0] == 2)
{
ZD();
}
}
catch
{
}
}
}
void ZD()
{
for(int i = 0;i<=500;i++)
{
this.Location = new Point(200,200);
this.Location = new Point(280,280);
}
}
在FormLoad事件里
Contral.CheckForIllegalCrossThreadCalls = false;
==================================================================================
客户端 服务端
Socket() Socket()
Bind() 绑定监听窗口
Listen() 设置监听队列
while(true)
{
Connect()---------建立连接-------- Accept() //循环等待客户端连接
}
Send() Receive()
Receive() Send()
Close() 捕捉异常 Close()
标签:
原文地址:http://www.cnblogs.com/hhsfrank/p/4342912.html