邮箱客户端程序的实现

目录

  • 一、邮箱设计
    • 1.MIME协议
    • 2.SMTP过程
      • (1)建立连接
      • (2)发送数据
      • (3)结束会话
  • 二、邮箱流程
  • 三、程序内部
    • 1.登录界面
  • 邮箱内部
    • 1.邮箱主体
  • 接收消息
  • 代码
    • 1.登录窗口
    • 2.主体窗口
    • 3.数据存储

一、邮箱设计

邮箱的设计基于SMTP(Simple Mail Transfer Protocol)协议,即简单邮件传输协议,邮件服务器用SMTP发送、接受邮件,但是邮件客户端只能用SMTP发送邮件,接收邮件一般用IMAP或者POP3。
邮箱客户端程序的实现_第1张图片

邮箱发送客户端程序主要处理对邮件的发送过程,采用的是SMTP协议,其原理是本客户端程序,使用SMTP协议,将邮件发送到SMTP服务器上,此服务器会作为客户端,使用SMTP协议将邮件发送到接收邮件的服务端上,接收方程序即可使用IMAP或POP3协议,对接受到的邮件进行处理,即转为用户可以读懂的邮件内容(也就是发送方想要接收方读的内容)。

其中具体传送邮件的步骤对用户是“透明”的,这也是邮箱发送客户端所要实现的一项功能,对用户隐藏那些不友好的传输手段,为用户提供方便友好的具体接口,提供更好的交互手段。用户看不到SMTP协议的具体内容和如何实现对邮件的转换过程,用户看得到的是自己的邮件能够准确、快捷和完整的发送到收件人手里。

1.MIME协议

在具体实现过程中,由于SMTP在传输报文时,只能传输7位的ASCII格式的报文,不支持不适用7位ASCII格式的语种和语言、视频数据的传输,因此我们需要辅助性协议帮忙传输报文,即MIME协议。
MIME邮件由邮件头和邮件体组成。
其中,邮件头有发件人、收件人、主题、时间、MIME版本、邮件内容的类型等信息。邮件体有段体的类型、段体的传输编码方式、段体的安排方式、段体的ID、段体的位置(路径)等信息。其中multipart类型,是MIME邮件的精髓之处。由于邮件体被分为了很多个段,这些段又各自分为了段头和段体两部分。各个段也需要用multipart类型分隔开。如下图。
邮箱客户端程序的实现_第2张图片

邮件内如果要嵌入各个内容,需要用multipart类型分隔开。
以上为邮件体MIME的实现方式。

2.SMTP过程

接下来是SMTP的工作方式。

(1)建立连接

首先,邮箱客户端程序,会发起建立一个运行到接收端邮箱服务器主机上的SMTP服务器端口号25之间的TCP连接,SMTP服务器向该客户回答应答码220,并且为客户端提供服务器的域名。
客户端当收到应答码后,想服务端发送HELO命令,启动客户端和服务器之间的SMTP的会话。服务端会回应应答码250,通知客户端已经建立服务会话。

(2)发送数据

接着,在客户与服务器之间的连接建立之后,发信的用户就可以与一个或多个收信人交换邮件报文了。
客户用“MAIL FROM”向服务器报告发信人的邮箱与域名,服务器向客户回应应答码“250”,代表请求命令完成;
客户用“RCPT TO”命令向服务器报告收信人的邮箱与域名,服务器向客户回应应答码“250”,代表请求命令完成;
客户用“DTAT”命令对报文的传送进行初始化,服务器回应“354”,表示可以进行邮件输入了;
客户用连续的行向服务器传送报文的内容,每行以两字符的行结束标识(CR与LF,即换行和回车)终止;
当发送结束时,报文以只有一个“.”的行结束,服务器向客户回应应答码“250”,代表请求命令完成。邮件体的内容就发送完成。

(3)结束会话

最后连接终止,客户端发送“QUIT”命令到服务器,服务器收到命令后,回到回应应答码“221”,并结束会话。

以上,就是SMTP客户端发送的全过程,也是基于这种方式,实现的邮箱客户端。

二、邮箱流程

邮箱客户端程序的实现_第3张图片

1.用户在登录界面输入用户名(邮箱号)和密码(授权码),点击登录按钮,跳转到邮箱主体部分。
2. 在主体部分,由用户输入发件人、收件人、主题,选择附件并输入正文内容,点击发送邮箱按钮。
3. 从输入框内读取信息,首先与服务其建立连接,想服务器打招呼并说明请求登录,发送邮箱用户名和密码,发送发件人和收件人,发送数据(包括邮件日期、发件人、收件人、主题、使用邮箱、正文和附件内容),结束发送(发送‘.’)。
4. 若发送成功,则弹出消息框发送成功告知用户;否则弹出发送失败。

三、程序内部

1.登录界面

邮箱客户端程序的实现_第4张图片邮箱登录,在此处读取用户的邮箱账号和密码,保存在数据类中,以便在主界面进行使用该数据。
邮箱客户端程序的实现_第5张图片
在此界面内,为了使界面看起来更美观,将Label标签背景隐藏成透明,并将窗口固定。
邮箱客户端程序的实现_第6张图片
在用户点击登录按钮后,会获取密码,并跳转到主界面,之后才能隐藏该窗口。但是需要在主窗口关闭时,才能关闭登录窗口,隐藏需要在创建主窗口时,传入该窗口的“this”对象。
邮箱客户端程序的实现_第7张图片

邮箱内部

1.邮箱主体

邮箱客户端程序的实现_第8张图片
在此窗口内,用户可以输入发件人、收件人(默认可以直接修改全部)、主题、附件和正文。
在用户输入全部信息后,点击发送按钮,由此开始程序内部的发送步骤。
邮箱客户端程序的实现_第9张图片
程序首先声明套接字,会根据用户登录时使用的邮箱,检测是否为qq邮箱或者网易邮箱,由此选择不同的SMTP服务器,使用端口号25,进行连接。
邮箱客户端程序的实现_第10张图片
建立连接后,会先从服务器接收回应,然后打招呼并请求登录,成功后,会随机发送用户名(邮箱)和密码(授权码)。
邮箱客户端程序的实现_第11张图片
说明发送人和收件人,随后说明发送数据。
在这里插入图片描述
此为邮箱头,包括日期、发件人、收件人、主题和使用邮箱。
在这里插入图片描述
发送正文内容,其中要用外部界限和内部界限。
邮箱客户端程序的实现_第12张图片
随后是发送附件内容,如果文件路径不为空的话,会读取附件路径,得到目标文件名,对其编码然后发送段头,随后,打开文件内部数据,同样需要对其转换成编码,然后发送附件内容。
邮箱客户端程序的实现_第13张图片
当选择附件内容后会找到其路径,然后写入文本框附件路径。
邮箱客户端程序的实现_第14张图片
随后结束,发送连接结束,根据是否发送成功与否,会弹出消息框提示。
即为发送全部过程。
邮箱客户端程序的实现_第15张图片
此为接受服务端返回消息,并打印出的接收函数。

接收消息

邮箱客户端程序的实现_第16张图片
可以在控制台上接收查看服务端发来的消息。

代码

1.登录窗口

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace tcp_ip
{
    public partial class Firstform : Form
    {
        public Firstform()
        {
            InitializeComponent();
            //   this.FormBorderStyle = FormBorderStyle.None;
            this.ShowInTaskbar = false;

            //标签透明
            label1.BackColor = Color.Transparent;
            label2.BackColor = Color.Transparent;
            label3.BackColor = Color.Transparent;
            label4.BackColor = Color.Transparent;
            //固定窗口
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.StartPosition = FormStartPosition.CenterScreen;
          //  label1.Parent = Firstform;
        }

        private void pictureBox1_Click(object sender, EventArgs e)//鲨鱼图案
        {
            pictureBox1.Image = Image.FromFile("E:\\Tcpip\\big_work\\content\\first.png");
        }


        private void button1_Click(object sender, EventArgs e)//登录按钮
        {
            Data.clientname = textBox2.Text;
            Data.password = textBox1.Text;//获取密码
            Mainform mainform=new Mainform(this);
            mainform.Show();
            this.Hide();//主窗口内关闭
        }

    }
}

2.主体窗口

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

namespace tcp_ip
{
    
    public partial class Mainform : Form
    {
        public static Socket socket;
        public static string textname;//附件名字
        Thread thread;
        public static int flag=0;
        Form enter_form=null;//登录窗口
        public Mainform(Form firstf)
        {
            enter_form = firstf;
            InitializeComponent();
            this.ShowInTaskbar = false;

            panel1.BackColor = Color.Transparent;
            label5.BackColor = Color.Transparent;
            label2.BackColor = Color.Transparent;
            label3.BackColor = Color.Transparent;
            label4.BackColor = Color.Transparent;
            label6.BackColor = Color.Transparent;
            label1.BackColor = Color.Transparent;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.StartPosition = FormStartPosition.CenterScreen;
        }


        public static string rece()
        {
                int len ;//receive长度
                byte[] buffer = new byte[1024];//接收传来的信息
                len= socket.Receive(buffer);//接收信息
                byte[] buffer2 = new byte[len];
                Array.Copy(buffer, buffer2, len);
                string s= Encoding.UTF8.GetString(buffer2);
                Console.WriteLine(s);//打印出
            return s;
        }

        private void myclose(object sender, FormClosedEventArgs e)//关闭主窗口
        {
            enter_form.Close();//关闭登陆窗口
        }

        private void button_send_Click(object sender, EventArgs e)
        {
            socket=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//声明通信套接字
            IPAddress iRAddress=null;
            if (Data.clientname.Substring(Data.clientname.Length - 6) == "qq.com")//检测是否为QQ邮箱
                iRAddress = Dns.GetHostAddresses("smtp.qq.com")[0];
            else if (Data.clientname.Substring(Data.clientname.Length - 7) .Equals("163.com"))
                iRAddress = Dns.GetHostAddresses("SMTP.163.com")[0];
            int port = int.Parse("25");
            IPEndPoint ep = new IPEndPoint(iRAddress, port);
            try
            {
                socket.Connect(ep);
                flag = 1;
 
            }
            catch
            {

            }
            if(flag == 1)
            {
               
                rece();
                //打招呼           
                socket.Send(Encoding.ASCII.GetBytes("ehlo hlp\r\n"));//向服务器打招呼
                rece();

                //登录
                socket.Send(Encoding.ASCII.GetBytes("auth login\r\n"));//请求登录
                rece();

                //发送用户名
                byte[] da1=Encoding.ASCII.GetBytes(Data.clientname);//转码
                socket.Send(Encoding.ASCII.GetBytes(Convert.ToBase64String(da1)+"\r\n"));//发送用户名
                rece();

                //发送密码
                da1 = Encoding.ASCII.GetBytes(Data.password);//转码
                socket.Send(Encoding.ASCII.GetBytes(Convert.ToBase64String(da1)+"\r\n"));//发送密码
                rece();

                //说明发件人
                socket.Send(Encoding.ASCII.GetBytes("mail from:<"+Data.clientname+">\r\n"));//说明发件人
                rece();

                //说明收件人
                socket.Send(Encoding.ASCII.GetBytes("RCPT TO:<" + accept_textBox.Text + ">\r\n"));//发送收件人
                rece();

                //说明发送数据
                socket.Send(Encoding.ASCII.GetBytes("DATA\r\n"));//说明发送数据
                rece();

                //发送数据
                socket.Send(Encoding.ASCII.GetBytes("Date:" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")+"\r\n"));//日期
                socket.Send(Encoding.UTF8.GetBytes("From:" + "\""+textBox1.Text +"\""+ "<" + Data.clientname + ">\r\n"));//发件人
                socket.Send(Encoding.ASCII.GetBytes("To:<" + accept_textBox.Text + ">\r\n"));//收件人
                socket.Send(Encoding.UTF8.GetBytes("Subject:"+subject_textBox.Text +"\r\n"));//主题
                socket.Send(Encoding.ASCII.GetBytes("X-mailer:shark 邮箱\r\n"));//使用邮箱
                socket.Send(Encoding.ASCII.GetBytes("Content-Type:multipart/mixed;boundary=\"*shark_out*\"\r\n"));//外部界限
                socket.Send(Encoding.ASCII.GetBytes("--*shark_out*\r\n"));
                socket.Send(Encoding.ASCII.GetBytes("Content-Type:multipart/alternative;boundary=\"*shark_inside*\"\r\n"));//内部界限
                socket.Send(Encoding.ASCII.GetBytes("--*shark_inside*\r\n"));//开始正文
                socket.Send(Encoding.ASCII.GetBytes("Content-Type:text/plain;charset=utf-8\r\n"));
                socket.Send(Encoding.ASCII.GetBytes("Content-Transfer-Encoding:7bit\r\n"));
                //正文内容
                socket.Send(Encoding.UTF8.GetBytes(main_textBox.Text+"\r\n"));
                //附件
                socket.Send(Encoding.UTF8.GetBytes("\r\n"));
                if(richTextBox1.Text!="")
                {    
                    socket.Send(Encoding.ASCII.GetBytes("--*shark_inside*\r\n"));
                    string alli = richTextBox1.Text;//读取附件路径
                    string[] name = alli.Split('\\');//分割'/'
                    string namelast = name[name.Length - 1];
                    string base64alli = "";
                    base64alli = Convert.ToBase64String(Encoding.UTF8.GetBytes(namelast));//先编码
                    socket.Send(Encoding.UTF8.GetBytes("Content-Type:application/octet-stream;name=\"" + namelast + "\"\r\n"));
                    socket.Send(Encoding.UTF8.GetBytes("Content-Transfer-Encoding:base64\r\n"));
                    socket.Send(Encoding.UTF8.GetBytes("Content-Disposition:attachment; filename=\"=?utf-8?B?" + base64alli + "?=\"\r\n"));
                    FileStream fileStream = new FileStream(alli, FileMode.Open);//打开文件内部数据
                    string base64Str = "";
                    fileStream.Seek(0, SeekOrigin.Begin);
                    byte[] bufferxx = new byte[fileStream.Length];
                    fileStream.Read(bufferxx, 0, Convert.ToInt32(fileStream.Length));
                    base64Str = Convert.ToBase64String(bufferxx);//转换数据编码
                    socket.Send(Encoding.UTF8.GetBytes(base64Str + "\r\n"));//发送数据内容
                    fileStream.Close();
                }
                socket.Send(Encoding.ASCII.GetBytes(".\r\n"));//结束发送
                string lp = rece();        //读取回送信息
            //    socket.Send(Encoding.UTF8.GetBytes("quit\r\n"));//连接结束
                if(!lp.Equals("") && lp[0]=='2')//查看是否发送成功
                {
                    MessageBox.Show("发送成功");
                }
                else
                {
                    MessageBox.Show("发送失败");
                }  
            }
        }


        private void button1_Click(object sender, EventArgs e)//选择附件文件
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.InitialDirectory = "c:\\";//打开C
            openFileDialog.Filter = "文本文件|*.*|C#文件|*.cs|所有文件|*.*";
            openFileDialog.ReadOnlyChecked = true;
            openFileDialog.FilterIndex = 1;
            if(openFileDialog.ShowDialog() == DialogResult.OK)
            {
                richTextBox1.Text= openFileDialog.FileName;//写入附件路径
            }
        }


        private void textBox1_MouseDown(object sender, MouseEventArgs e)
        {
            textBox1.SelectAll();
        }

    }
}

3.数据存储

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace tcp_ip
{
    public class Data
    {
        public static string clientname;//用户名
        public static string password;//密码
        
    }
}

你可能感兴趣的:(socket编程,服务器,网络,c#)