C# Socket服務端與客戶端通信(包含大文件的斷點傳輸)

原文鏈接:https://www.cnblogs.com/zzp0320/p/7909828.html

步驟:

一、服務端的建立

1.服務端的項目建立以及頁面佈局

2.各功能按鍵的事件代碼

  1)傳輸類型說明以及全局變量

  2)Socket通信服務端具體步驟:

     (1)建立一個Socket

     (2)接收信息

     (3)發送數據(這裏分發送字符串、文件(包含大文件)、震動)

二、客戶端的建立

1.服務端的項目建立以及頁面佈局

2.各功能按鍵的事件代碼

  1)傳輸類型說明以及全局變量

  2)Socket通信服務端具體步驟:

     (1)建立一個Socket

     (2)接收信息

     (3)發送數據(這裏分發送字符串、文件(包含大文件)、震動)

 

 

注意:此圖是Socket通信的精華,在使用Socket通信時,有什麼迷惑的可以看看此圖,下面我們講解的時候也是參照此圖

 

Socket大家肯定很熟悉,對已內部的通信邏輯,肯定也有一定得了解---

對於Socket研究了兩天寫了一個小程序,通過Socket服務端與客戶端的通信,以及大文件之間斷點的傳輸(這裏只做了服務端給客戶端傳送大文件,如果想把客戶端的大文件傳送給服務端也是一樣的道理,看了文章,大家肯定可以自己實現)······

(自己才疏學淺,如有bug請諒解,但功能還是能實現的)

下面根據步驟進入正題:

一、服務端的建立

1.服務端的項目建立以及頁面佈局

新建解決方案“Socket通信”以及兩個Winform項目(1)SockeClient——客戶端    (2)SocketServer——服務器

給服務端界面佈局——參照上圖(這個大家肯定都是手到擒來就不累贅了······)

2.各功能按鍵的事件代碼

先把整個服務端的代碼貼出來,然後我們在一一講解

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

namespace SocketServer

{

    public partial class Form1 : Form

    {

 

        //說明:在傳遞信息的時候,會在需要傳遞的信息前面加一個字符來標識傳遞的是不同的信息

        // 0:表示傳遞的是字符串信息

        // 1:表示傳遞的是文件信息

        // 2:表示的是震動

 

        /// <summary>

        /// 用來存放連接服務的客戶端的IP地址和端口號,對應的Socket

        /// </summary>

        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

 

        public Form1()

        {

            InitializeComponent();

        }

 

        private void Form1_Load(object sender, EventArgs e)

        {

            //不檢測跨線程之間的空間調用

            Control.CheckForIllegalCrossThreadCalls = false;

        }

 

        /// <summary>

        /// 開啓監聽

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnStart_Click(object sender, EventArgs e)

        {

            try

            {

                //當點擊開始監聽的時候 在服務器端創建一個負責監IP地址跟端口號的Socket

                Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //獲取IP

                IPAddress ip = IPAddress.Any;

                //創建端口號

                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));

                //監聽

                socketWatch.Bind(port);

                ShowMsg("監聽成功");

                socketWatch.Listen(10);

                //新建線程,去接收客戶端發來的信息

                Thread td = new Thread(AcceptMgs);

                td.IsBackground = true;

                td.Start(socketWatch);

            }

            catch

            {

                 

            }          

        }

 

        /// <summary>

        /// 接收客戶端發送的信息

        /// </summary>

        /// <param name="o"></param>

        private void AcceptMgs(object o)

        {

            try

            {

                Socket socketWatc = (Socket)o;

                while (true)

                {

                    ////負責跟客戶端通信的Socket

                    Socket socketSend = socketWatc.Accept();

                    //將遠程連接的客戶端的IP地址和Socket存入集合中

                    dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);

                    //將遠程連接的客戶端的IP地址和端口號存儲下拉框中

                    cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString());

                    ShowMsg(socketSend.RemoteEndPoint.ToString() + ": 連接成功");

                    //新建線程循環接收客戶端發來的信息

                    Thread td = new Thread(Recive);

                    td.IsBackground = true;

                    td.Start(socketSend);

                }

            }

            catch { }

             

        }

 

        /// <summary>

        /// 接收客戶端發來的數據,並顯示出來

        /// </summary>

        private void Recive(object o)

        {

            Socket socketSend = (Socket)o;

            try

            {

                while (true)

                {

                    //客戶端連接成功後,服務器應該接受客戶端發來的消息

                     

                    if (socketSend == null)

                    {

                        MessageBox.Show("請選擇要發送的客戶端");

                        continue;

                    }

                    byte[] buffer = new byte[1024 * 1024 * 2];

                    //實際接受到的有效字節數

                    int r = socketSend.Receive(buffer);

                    //如果客戶端關閉,發送的數據就爲空,然後就跳出循環

                    if (r == 0)

                    {

                        break;

                    }                  

                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息

                    {

                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);

                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);

                    }

                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件

                    {

                        string filePath = "";

                        SaveFileDialog sfd = new SaveFileDialog();

                        sfd.Title = "保存文件";

                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";

                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";

                        //如果沒有選擇保存文件路徑就一直打開保存框

                        while (true)

                        {

                            sfd.ShowDialog(this);

                            filePath = sfd.FileName;

                            if (string.IsNullOrEmpty(filePath))

                            {

                                continue;

                            }

                            else

                            {

                                break;

                            }

                        }

                        //保存接收的文件

                        using (FileStream fsWrite = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))

                        {

                            fsWrite.Write(buffer, 1, r - 1);

                        }

                        ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");

                         

                    }

                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動

                    {

                        ZD();

                    }

                }

            }

            catch{}         

        }

 

 

        /// <summary>

        /// 顯示信息

        /// </summary>

        /// <param name="message"></param>

        private void ShowMsg(string message)

        {

            txtLog.AppendText(message + "\r\n");

        }

 

        /// <summary>

        /// 發送信息

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnSend_Click(object sender, EventArgs e)

        {

             

            //獲得選中客戶端ip對應的通信Socket      

            if (cboUsers.SelectedItem == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];

            if (socketSend == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            string strSend=txtMsg.Text;

            try

            {

                byte[] buffer = Encoding.UTF8.GetBytes(strSend);

                //獲得發送的信息時候,在數組前面加上一個字節 0

                List<byte> list = new List<byte>();

                list.Add(0);

                list.AddRange(buffer);

                //將泛型集合轉換爲數組

                byte[] newBuffer = list.ToArray();

                //將了標識字符的字節數組傳遞給客戶端

                socketSend.Send(newBuffer);

                txtMsg.Text = "";

            }

            catch

            {

            }           

        }

 

        /// <summary>

        /// 選擇文件

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnSelect_Click(object sender, EventArgs e)

        {

            //打開文件

            OpenFileDialog ofd = new OpenFileDialog();

            ofd.Title = "選擇要傳的文件";

            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";

            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";

            ofd.ShowDialog();

            //得到選擇文件的路徑

            txtPath.Text = ofd.FileName;

        }

 

        /// <summary>

        /// 發送文件

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnSendFile_Click(object sender, EventArgs e)

        {

            //判斷是否選擇了要發送的客戶端

            if (cboUsers.SelectedItem == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];

            if (socketSend == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            string filePath = txtPath.Text;

            if (string.IsNullOrEmpty(filePath))

            {

                MessageBox.Show("請選擇文件");

                return;

            }

            Thread td = new Thread(SendBigFile);

            td.IsBackground = true;

            td.Start();

             

        }

 

        /// <summary>

        /// 大文件斷點傳送

        /// </summary>

        private void SendBigFile()

        {

            string filePath = txtPath.Text;

            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];

            try

            {

                //讀取選擇的文件

                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))

                {

                    //1. 第一步:發送一個包,表示文件的長度,讓客戶端知道後續要接收幾個包來重新組織成一個文件

                    long length = fsRead.Length;

                    byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString());

                    //獲得發送的信息時候,在數組前面加上一個字節 1

                    List<byte> list = new List<byte>();

                    list.Add(1);

                    list.AddRange(byteLength);

                    socketSend.Send(list.ToArray()); //

                    //2. 第二步:每次發送一個1MB的包,如果文件較大,則會拆分爲多個包

                    byte[] buffer = new byte[1024 * 1024];

                    long send = 0; //發送的字節數                  

                    while (true)  //大文件斷點多次傳輸

                    {

                        int r = fsRead.Read(buffer, 0, buffer.Length);

                        if (r == 0)

                        {

                            break;

                        }

                        socketSend.Send(buffer, 0, r, SocketFlags.None);

                        send += r;

                        ShowMsg(string.Format("{0}: 已發送:{1}/{2}", socketSend.RemoteEndPoint, send, length));

                    }

                    ShowMsg("發送完成");

                    txtPath.Text = "";

                }

            }

            catch

            {

 

            }

        }

 

        /// <summary>

        /// 震動

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnZD_Click(object sender, EventArgs e)

        {

            //判斷是否選擇了要發送的客戶端

            if (cboUsers.SelectedItem == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];

            if (socketSend == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            try

            {

                // 首字節是2說明是震動

                byte[] buffer = new byte[1];

                buffer[0] = 2;

                socketSend.Send(buffer);

            }

            catch

            {

                 

            }

             

        }

 

        /// <summary>

        /// 震動

        /// </summary>

        private void ZD()

        {

            //獲取當前窗體的座標

            Point point = this.Location;

            //反覆給窗體座標複製一百次,達到震動的效果

            for (int i = 0; i < 100; i++)

            {

                this.Location = new Point(point.X - 5, point.Y - 5);

                this.Location = new Point(point.X + 5, point.Y + 5);

            }

            this.Location = point;

        }

    }

}

  

1)傳輸類型說明以及全局變量

這些說明以及全局變量,說的也比較清楚,也不累贅了。

2)Socket通信服務端具體步驟:

(這些步驟都是根據第一個圖來的)

 (1)建立一個Socket

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

/// <summary>

        /// 開啓監聽

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnStart_Click(object sender, EventArgs e)

        {

            try

            {

                //當點擊開始監聽的時候 在服務器端創建一個負責監IP地址跟端口號的Socket

                Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //獲取IP

                IPAddress ip = IPAddress.Any;

                //創建端口號

                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));

                //監聽

                socketWatch.Bind(port);

                ShowMsg("監聽成功");

                socketWatch.Listen(10);

                //新建線程,去接收客戶端發來的信息

                Thread td = new Thread(AcceptMgs);

                td.IsBackground = true;

                td.Start(socketWatch);

            }

            catch

            {

                 

            }          

        }

  

在開啓監聽按鈕裏,我們建立了Socket,以及監聽的最大客戶端數 socketWatch.Listen(10)

由於服務端會不停的去監視接收客戶端發來的信息,如果把這個工作放到主線程裏,程序會出現假死的現象,所以這裏給他放到一個新的線程裏。

(2)接收信息

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

/// <summary>

        /// 接收客戶端發送的信息

        /// </summary>

        /// <param name="o"></param>

        private void AcceptMgs(object o)

        {

            try

            {

                Socket socketWatc = (Socket)o;

                while (true)

                {

                    ////負責跟客戶端通信的Socket

                    Socket socketSend = socketWatc.Accept();

                    //將遠程連接的客戶端的IP地址和Socket存入集合中

                    dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);

                    //將遠程連接的客戶端的IP地址和端口號存儲下拉框中

                    cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString());

                    ShowMsg(socketSend.RemoteEndPoint.ToString() + ": 連接成功");

                    //新建線程循環接收客戶端發來的信息

                    Thread td = new Thread(Recive);

                    td.IsBackground = true;

                    td.Start(socketSend);

                }

            }

            catch { }

             

        }

  

接收信息是會根據接收到字節數字的第一個字節來判斷接收到的是什麼

這個在方法Recive裏進行判斷

1

/// <summary>

複製代碼

namespace SocketClient
{
    public partial class Form1 : Form
    {

        //說明:在傳遞信息的時候,會在需要傳遞的信息前面加一個字符來標識傳遞的是不同的信息
        // 0:表示傳遞的是字符串信息
        // 1:表示傳遞的是文件信息
        // 2:表示的是震動

        /// <summary>
        /// 用來存放連接服務的IP地址和端口號,對應的Socket (這個爲了以後的擴展用,現在暫時沒用)
        /// </summary>
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

        /// <summary>
        /// 存儲保存文件的路徑
        /// </summary>
        string  filePath = "";
        /// <summary>
        /// 負責通信的Socket
        /// </summary>
        Socket socketSend;       

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //不檢測跨線程之間的空間調用
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 建立連接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //創建負責通信的Socket
                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //獲取服務端的IP
                IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
                //獲取服務端的端口號
                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
                //獲得要連接的遠程服務器應用程序的IP地址和端口號
                socketSend.Connect(port);
                ShowMsg("連接成功");
                //新建線程,去接收客戶端發來的信息
                Thread td = new Thread(AcceptMgs);
                td.IsBackground = true;
                td.Start();
            }
            catch { }
        }

        /// <summary>
        /// 接收數據
        /// </summary>
        private void AcceptMgs()
        {           
            try
            {
                /// <summary>
                /// 存儲大文件的大小
                /// </summary>
                long length = 0;
                long recive = 0; //接收的大文件總的字節數
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    if (length > 0)  //判斷大文件是否已經保存完
                    {
                        //保存接收的文件
                        using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                        {
                            fsWrite.Write(buffer, 0, r);
                            length -= r; //減去每次保存的字節數
                            ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));
                            if (length <= 0)
                            {
                                ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
                            }
                            continue;
                        }                        
                    }
                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息
                    {
                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
                    }
                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件
                    {
                        length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1));
                        recive = length;
                        filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
                        //如果沒有選擇保存文件路徑就一直打開保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }                                        
                    }
                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動
                    {
                        ZD();
                    }
                }
            }
            catch { }
            
           
        }


        /// <summary>
        /// 顯示信息
        /// </summary>
        /// <param name="message"></param>
        private void ShowMsg(string message)
        {
            txtLog.AppendText(message + "\r\n");
        }

        /// <summary>
        /// 發送數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
                //獲得發送的信息時候,在數組前面加上一個字節 0
                List<byte> list = new List<byte>();
                list.Add(0);
                list.AddRange(buffer);
                //將泛型集合轉換爲數組
                byte[] newBuffer = list.ToArray();
                //將了標識字符的字節數組傳遞給客戶端
                socketSend.Send(newBuffer);
                txtMsg.Text = "";
            }
            catch{}          
        }  

        /// <summary>
        /// 選擇文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //打開文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "選擇要傳的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到選擇文件的路徑
            txtPath.Text = ofd.FileName;
        }

        /// <summary>
        /// 發送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {       
            try
            {
                string filePath = txtPath.Text;
                if (string.IsNullOrEmpty(filePath))
                {
                    MessageBox.Show("請選擇文件");
                    return;
                }
                //讀取選擇的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    //獲得發送的信息時候,在數組前面加上一個字節 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    byte[] newBuffer = list.ToArray();
                    //將了標識字符的字節數組傳遞給客戶端
                    socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);
                    txtPath.Text = "";
                }
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {        
            try
            {
                // 首字節是2說明是震動
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                socketSend.Send(buffer);
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        private void ZD()
        {
            //獲取當前窗體的座標
            Point point = this.Location;
            //反覆給窗體座標複製一百次,達到震動的效果
            for (int i = 0; i < 100; i++)
            {
                this.Location = new Point(point.X - 5, point.Y - 5);
                this.Location = new Point(point.X + 5, point.Y + 5);
            }
            this.Location = point;
        }
    }
}

複製代碼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

/// 接收客戶端發來的數據,並顯示出來

/// </summary>

private void Recive(object o)

{

    Socket socketSend = (Socket)o;

    try

    {

        while (true)

        {

            //客戶端連接成功後,服務器應該接受客戶端發來的消息

             

            if (socketSend == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                continue;

            }

            byte[] buffer = new byte[1024 * 1024 * 2];

            //實際接受到的有效字節數

            int r = socketSend.Receive(buffer);

            //如果客戶端關閉,發送的數據就爲空,然後就跳出循環

            if (r == 0)

            {

                break;

            }                  

            if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息

            {

                string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);

                ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);

            }

            else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件

            {

                string filePath = "";

                SaveFileDialog sfd = new SaveFileDialog();

                sfd.Title = "保存文件";

                sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";

                sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";

                //如果沒有選擇保存文件路徑就一直打開保存框

                while (true)

                {

                    sfd.ShowDialog(this);

                    filePath = sfd.FileName;

                    if (string.IsNullOrEmpty(filePath))

                    {

                        continue;

                    }

                    else

                    {

                        break;

                    }

                }

                //保存接收的文件

                using (FileStream fsWrite = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))

                {

                    fsWrite.Write(buffer, 1, r - 1);

                }

                ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");

                 

            }

            else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動

            {

                ZD();

            }

        }

    }

    catch{}         

}

  

(3)發送數據(這裏分發送字符串、文件(包含大文件)、震動)

發送字符串信息

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

/// <summary>

        /// 發送信息

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnSend_Click(object sender, EventArgs e)

        {

             

            //獲得選中客戶端ip對應的通信Socket      

            if (cboUsers.SelectedItem == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];

            if (socketSend == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            string strSend=txtMsg.Text;

            try

            {

                byte[] buffer = Encoding.UTF8.GetBytes(strSend);

                //獲得發送的信息時候,在數組前面加上一個字節 0

                List<byte> list = new List<byte>();

                list.Add(0);

                list.AddRange(buffer);

                //將泛型集合轉換爲數組

                byte[] newBuffer = list.ToArray();

                //將了標識字符的字節數組傳遞給客戶端

                socketSend.Send(newBuffer);

                txtMsg.Text = "";

            }

            catch

            {

            }           

        }

  發送震動

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

/// <summary>

        /// 震動

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnZD_Click(object sender, EventArgs e)

        {

            //判斷是否選擇了要發送的客戶端

            if (cboUsers.SelectedItem == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];

            if (socketSend == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            try

            {

                // 首字節是2說明是震動

                byte[] buffer = new byte[1];

                buffer[0] = 2;

                socketSend.Send(buffer);

            }

            catch

            {

                 

            }

             

        }

 

        /// <summary>

        /// 震動

        /// </summary>

        private void ZD()

        {

            //獲取當前窗體的座標

            Point point = this.Location;

            //反覆給窗體座標複製一百次,達到震動的效果

            for (int i = 0; i < 100; i++)

            {

                this.Location = new Point(point.X - 5, point.Y - 5);

                this.Location = new Point(point.X + 5, point.Y + 5);

            }

            this.Location = point;

        }

  

發送文件(包含大文件)

首先要選擇文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

/// <summary>

        /// 選擇文件

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnSelect_Click(object sender, EventArgs e)

        {

            //打開文件

            OpenFileDialog ofd = new OpenFileDialog();

            ofd.Title = "選擇要傳的文件";

            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";

            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";

            ofd.ShowDialog();

            //得到選擇文件的路徑

            txtPath.Text = ofd.FileName;

        }

  然後在發送文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

/// <summary>

        /// 發送文件

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void btnSendFile_Click(object sender, EventArgs e)

        {

            //判斷是否選擇了要發送的客戶端

            if (cboUsers.SelectedItem == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];

            if (socketSend == null)

            {

                MessageBox.Show("請選擇要發送的客戶端");

                return;

            }

            string filePath = txtPath.Text;

            if (string.IsNullOrEmpty(filePath))

            {

                MessageBox.Show("請選擇文件");

                return;

            }

            Thread td = new Thread(SendBigFile);

            td.IsBackground = true;

            td.Start();

             

        }

 

        /// <summary>

        /// 大文件斷點傳送

        /// </summary>

        private void SendBigFile()

        {

            string filePath = txtPath.Text;

            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];

            try

            {

                //讀取選擇的文件

                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))

                {

                    //1. 第一步:發送一個包,表示文件的長度,讓客戶端知道後續要接收幾個包來重新組織成一個文件

                    long length = fsRead.Length;

                    byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString());

                    //獲得發送的信息時候,在數組前面加上一個字節 1

                    List<byte> list = new List<byte>();

                    list.Add(1);

                    list.AddRange(byteLength);

                    socketSend.Send(list.ToArray()); //

                    //2. 第二步:每次發送一個4KB的包,如果文件較大,則會拆分爲多個包

                    byte[] buffer = new byte[1024 * 1024];

                    long send = 0; //發送的字節數                  

                    while (true)  //大文件斷點多次傳輸

                    {

                        int r = fsRead.Read(buffer, 0, buffer.Length);

                        if (r == 0)

                        {

                            break;

                        }

                        socketSend.Send(buffer, 0, r, SocketFlags.None);

                        send += r;

                        ShowMsg(string.Format("{0}: 已發送:{1}/{2}", socketSend.RemoteEndPoint, send, length));

                    }

                    ShowMsg("發送完成");

                    txtPath.Text = "";

                }

            }

            catch

            {

 

            }

        }

  

注意:(1)發送文件的時候會分兩步發送 :第一步:發送一個包,表示文件的長度,讓客戶端知道後續要接收幾個包來重新組織成一個文件

                                                 第二步:每次發送一個1MB的包,如果文件較大,則會拆分爲多個包

     (2)每個客戶端連接服務端的啥時候,都會把客戶端的ip以及端口號,放到下拉框裏,想給那個客戶端發信息,就選擇對應的客戶端

二、客戶端的建立

1.客戶端的項目建立以及頁面佈局

客戶端的界面佈局與服務端很像,就是把對應的開始監聽換成連接,當然代碼也會有所改變,後面會講到·····

2.各功能按鍵的事件代碼

先把整個服客戶端的代碼貼出來,然後我們在一一講解

複製代碼

namespace SocketClient
{
    public partial class Form1 : Form
    {

        //說明:在傳遞信息的時候,會在需要傳遞的信息前面加一個字符來標識傳遞的是不同的信息
        // 0:表示傳遞的是字符串信息
        // 1:表示傳遞的是文件信息
        // 2:表示的是震動

        /// <summary>
        /// 用來存放連接服務的IP地址和端口號,對應的Socket (這個爲了以後的擴展用,現在暫時沒用)
        /// </summary>
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

        /// <summary>
        /// 存儲保存文件的路徑
        /// </summary>
        string  filePath = "";
        /// <summary>
        /// 負責通信的Socket
        /// </summary>
        Socket socketSend;       

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //不檢測跨線程之間的空間調用
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 建立連接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //創建負責通信的Socket
                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //獲取服務端的IP
                IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
                //獲取服務端的端口號
                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
                //獲得要連接的遠程服務器應用程序的IP地址和端口號
                socketSend.Connect(port);
                ShowMsg("連接成功");
                //新建線程,去接收客戶端發來的信息
                Thread td = new Thread(AcceptMgs);
                td.IsBackground = true;
                td.Start();
            }
            catch { }
        }

        /// <summary>
        /// 接收數據
        /// </summary>
        private void AcceptMgs()
        {           
            try
            {
                /// <summary>
                /// 存儲大文件的大小
                /// </summary>
                long length = 0;
                long recive = 0; //接收的大文件總的字節數
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    if (length > 0)  //判斷大文件是否已經保存完
                    {
                        //保存接收的文件
                        using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                        {
                            fsWrite.Write(buffer, 0, r);
                            length -= r; //減去每次保存的字節數
                            ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));
                            if (length <= 0)
                            {
                                ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
                            }
                            continue;
                        }                        
                    }
                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息
                    {
                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
                    }
                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件
                    {
                        length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1));
                        recive = length;
                        filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
                        //如果沒有選擇保存文件路徑就一直打開保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }                                        
                    }
                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動
                    {
                        ZD();
                    }
                }
            }
            catch { }
            
           
        }


        /// <summary>
        /// 顯示信息
        /// </summary>
        /// <param name="message"></param>
        private void ShowMsg(string message)
        {
            txtLog.AppendText(message + "\r\n");
        }

        /// <summary>
        /// 發送數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
                //獲得發送的信息時候,在數組前面加上一個字節 0
                List<byte> list = new List<byte>();
                list.Add(0);
                list.AddRange(buffer);
                //將泛型集合轉換爲數組
                byte[] newBuffer = list.ToArray();
                //將了標識字符的字節數組傳遞給客戶端
                socketSend.Send(newBuffer);
                txtMsg.Text = "";
            }
            catch{}          
        }  

        /// <summary>
        /// 選擇文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //打開文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "選擇要傳的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到選擇文件的路徑
            txtPath.Text = ofd.FileName;
        }

        /// <summary>
        /// 發送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {       
            try
            {
                string filePath = txtPath.Text;
                if (string.IsNullOrEmpty(filePath))
                {
                    MessageBox.Show("請選擇文件");
                    return;
                }
                //讀取選擇的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    //獲得發送的信息時候,在數組前面加上一個字節 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    byte[] newBuffer = list.ToArray();
                    //將了標識字符的字節數組傳遞給客戶端
                    socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);
                    txtPath.Text = "";
                }
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {        
            try
            {
                // 首字節是2說明是震動
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                socketSend.Send(buffer);
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        private void ZD()
        {
            //獲取當前窗體的座標
            Point point = this.Location;
            //反覆給窗體座標複製一百次,達到震動的效果
            for (int i = 0; i < 100; i++)
            {
                this.Location = new Point(point.X - 5, point.Y - 5);
                this.Location = new Point(point.X + 5, point.Y + 5);
            }
            this.Location = point;
        }
    }
}

複製代碼

1)傳輸類型說明以及全局變量

 

這些說明以及全局變量,說的也比較清楚,也不累贅了。

2)Socket通信服務端具體步驟:

(這些步驟都是根據第一個圖來的)

 (1)建立一個通信的Socket

複製代碼

/// <summary>
        /// 建立連接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //創建負責通信的Socket
                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //獲取服務端的IP
                IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
                //獲取服務端的端口號
                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
                //獲得要連接的遠程服務器應用程序的IP地址和端口號
                socketSend.Connect(port);
                ShowMsg("連接成功");
                //新建線程,去接收客戶端發來的信息
                Thread td = new Thread(AcceptMgs);
                td.IsBackground = true;
                td.Start();
            }
            catch { }
        }

複製代碼

在連接按鈕裏,我們建立了Socket

由於客戶端會不停的去監視接收服務端發來的信息,如果把這個工作放到主線程裏,程序會出現假死的現象,所以這裏給他放到一個新的線程裏。

(2)接收信息

複製代碼

/// <summary>
        /// 接收數據
        /// </summary>
        private void AcceptMgs()
        {           
            try
            {
                /// <summary>
                /// 存儲大文件的大小
                /// </summary>
                long length = 0;
                long recive = 0; //接收的大文件總的字節數
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    if (length > 0)  //判斷大文件是否已經保存完
                    {
                        //保存接收的文件
                        using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                        {
                            fsWrite.Write(buffer, 0, r);
                            length -= r; //減去每次保存的字節數
                            ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));
                            if (length <= 0)
                            {
                                ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
                            }
                            continue;
                        }                        
                    }
                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息
                    {
                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
                    }
                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件
                    {
                        length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1));
                        recive = length;
                        filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
                        //如果沒有選擇保存文件路徑就一直打開保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }                                        
                    }
                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動
                    {
                        ZD();
                    }
                }
            }
            catch { }
            
           
        }

複製代碼

接收信息是會根據接收到字節數字的第一個字節來判斷接收到的是什麼,如果接收的是個大文件,首先會接收大文件的大小,然後根據大小接收相同大小的字節數組追加保存到一個文件裏去。

(3)發送數據(這裏分發送字符串、文件(包含大文件)、震動)

發送字符串信息

複製代碼

/// <summary>
        /// 發送數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
                //獲得發送的信息時候,在數組前面加上一個字節 0
                List<byte> list = new List<byte>();
                list.Add(0);
                list.AddRange(buffer);
                //將泛型集合轉換爲數組
                byte[] newBuffer = list.ToArray();
                //將了標識字符的字節數組傳遞給客戶端
                socketSend.Send(newBuffer);
                txtMsg.Text = "";
            }
            catch{}          
        }

複製代碼

發送震動

複製代碼

/// <summary>
        /// 震動
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {        
            try
            {
                // 首字節是2說明是震動
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                socketSend.Send(buffer);
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        private void ZD()
        {
            //獲取當前窗體的座標
            Point point = this.Location;
            //反覆給窗體座標複製一百次,達到震動的效果
            for (int i = 0; i < 100; i++)
            {
                this.Location = new Point(point.X - 5, point.Y - 5);
                this.Location = new Point(point.X + 5, point.Y + 5);
            }
            this.Location = point;
        }

複製代碼

發送文件(不包含大文件)

首先要選擇文件

複製代碼

/// <summary>
        /// 選擇文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //打開文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "選擇要傳的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到選擇文件的路徑
            txtPath.Text = ofd.FileName;
        }

複製代碼

然後在發送文件

複製代碼

/// <summary>
        /// 發送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {       
            try
            {
                string filePath = txtPath.Text;
                if (string.IsNullOrEmpty(filePath))
                {
                    MessageBox.Show("請選擇文件");
                    return;
                }
                //讀取選擇的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    //獲得發送的信息時候,在數組前面加上一個字節 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    byte[] newBuffer = list.ToArray();
                    //將了標識字符的字節數組傳遞給客戶端
                    socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);
                    txtPath.Text = "";
                }
            }
            catch{ }
        }

複製代碼

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章