zoukankan      html  css  js  c++  java
  • 15-03-16 Socket网络编程

    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 + " ");(追加的意思,在每行结束的时候换行);
    }


    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 + " ");
    }


    //先开启服务器监听,在客户端连接;
    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()

  • 相关阅读:
    javascript转换时间戳
    解决 Out of range value adjusted for column 'ID' at row 1
    解决 ERROR in native method: JDWP No transports initialized, jvmtiError=AGENT_ERROR_TRANSPORT_INIT(197)
    解决 nfs挂载错误wrong fs type, bad option, bad superblock
    修改浏览器Cookie
    elasticsearch备忘
    itextpdf 备忘
    重命名和移动
    win10 教育版本变专业版本
    解决com.intellij.openapi.project.IndexNotReadyException: Please change caller according to com.intellij.
  • 原文地址:https://www.cnblogs.com/hhsfrank/p/4342912.html
Copyright © 2011-2022 走看看