网络编程之Socket的TCP协议实现客户端与客户端之间的通信

我认为当你学完某个知识点后,最好是做一个实实在在的小案例。这样才能更好对知识的运用与掌握

如果你看了我前两篇关于socket通信原理的入门文章。我相信对于做出我这个小案列是完全没有问题的!!

既然是小案列。就难免会有很多不足,所以还请大家见谅。先说下用此案例实现的功能

利用Socke的TCP协议实现了

1:端与端之间的通信(客户端和客户端)包括发送文字,小文件,抖动效果

2:同步实现好友下线与上线的效果,即有其他客户端上线会通知其他已经在线的客户端

3:实现自定义通信协议

服务器窗体布局

布局很明了。顶部一个DataGridView显示连接的客户端信息,来一个就新增一个。走一个就干掉一个。

下面就是显示服务器信息

客户端布局:

窗口布局是不是很大众呢。QQ啊。飞秋。类似的布局。

好了。现在启动服务器。连接两个客户端试试

服务器已经获取了客户端信息

客服端用户列表是空的?因为这里我的处理是不能跟自己聊天所以列表是不显示自己的ip的

那么在登陆一个客户端呢?

服务器

此时客户端都已经获取了对方上线的ip

此时就可以通信了。那来试试上面说的功能。发送文字,抖动,和图片

发送文字:

箭头的方向是发送的开始,反过来发送也是可以的

当有人下线,客户端会更新 "用户列表",同时服务器也会端口对下线者的通信

比如:当端口号为:6477下线。即端口连接

刚下线一个客户端。所以现在登陆一个客户端

发送文件: 这里仅仅是小文件。显示发送一个图片

当选择文件文件后。文件路径会显示在框中

从图中可以看出。是一个图片。名字是 2

单击发送。接收的用户会提示是否接受该文件。

当单击是的时候。则弹出保存对话框

从图片中可以看出来,这里获取到了文件名称。那么客户端是怎么获取到的呢?在下面的代码中会一一讲解

现在重新命名 new.jpg保存看看。已经成功了

抖动效果

为了让大家看到抖动效果。这里放一个gif图片。其实跟QQ抖动类似。当然没他那么优美。那么好看。

看看一个通信的流程图

图片中红色的标记框就是

首先:我第一次登陆成功,服务器发送在线好友给我,显示在用户列表中

同时通知其他在线用户。用人(我)上线了。

当然。当有人上线。服务器也会通知我。然后把上线的ip添加到用列表中

当我下线的时候。通知服务器我下线了。服务器则通知其他在线用户。更新列表

发送消息也是先发送到服务器。然后服务器在转发。

好了。具体功能就是这些了。带着这些实现的功能读下面的代码。思绪应该会比较清晰

还记得创建服务器的三个步骤吗?

第一:创建监听的socket

第二:绑定监听用个的ip和端口号

第三:开始监听

因为监听和获取消息都是要循环的。要么用多线程。要么用socket异步。所以这两个方法要独立出来

创建StartServer()方法。用来开启服务器

 1 /// <summary>
 2         /// 打开服务器
 3         /// </summary>
 4         void StartServer()
 5         {
 6             try
 7             {
 8                 string _ip = tbIp.Text;
 9                 int _point = int.Parse(tbPoint.Text);
10
11                 //创建监听客户端请求的socket
12                 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
13                 //监听用的ip和端口
14                 IPAddress address = IPAddress.Parse(_ip);
15                 IPEndPoint point = new IPEndPoint(address, _point);
16
17                 //绑定
18                 socket.Bind(point);
19                 socket.Listen(10);
20
21
22                 //异步 开始监听
23                 socket.BeginAccept(new AsyncCallback(Listen), socket);
24
25
26                 //禁用当前按钮
27                 btnStart.Enabled = false;
28
29                 //启动时间
30                 startTime.Text = DateTime.Now.ToString();
31
32                 //底部提示消息
33                 tssMsg.Text = "服务器已经启动";
34             }
35             catch (Exception ex)
36             {
37                 MessageBox.Show(ex.Message);
38             }
39
40         }

从代码可以看出来。我用到了异步通信 BeginAccept方法 回掉方法是Listen

Listen方法即上面说的要循环监听的代码

Listen方法

 1 /// <summary>
 2         /// 开始监听
 3         /// </summary>
 4         /// <param name="result"></param>
 5         void Listen(IAsyncResult result)
 6         {
 7
 8             try
 9             {
10                 //获取监听的socket
11                 Socket clientSocket = result.AsyncState as Socket;
12                 //与服务器通信的socket
13                 Socket connSocket = clientSocket.EndAccept(result);
14
15                 string ip = connSocket.RemoteEndPoint.ToString();
16                 //连接成功。保存信息
17                 if (!Common.connSocket.ContainsKey(ip))
18                     Common.connSocket.Add(ip, connSocket);
19
20                 //连接成功,更新服务器信息
21                 changeList(connSocket);
22
23
24                 //等待新的客户端连接 ,相当于循环调用
25                 clientSocket.BeginAccept(new AsyncCallback(Listen), clientSocket);
26
27                 byte[] buffer = new byte[1024 * 1024];
28
29
30                 //接收来自客户端信息 ,相当于循环调用
31                 connSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), connSocket);
32
33                 //用户第一次登陆。获取所有在线用户  如果有好友功能。则获取他的好友
34                 SendesClient(connSocket);
35             }
36             catch (Exception ex)
37             {
38
39                 //MessageBox.Show(ex.Message);
40             }
41
42
43         }

如果你在想为什么Listen方法要这样定义:

void Listen(IAsyncResult result)

你可以去看下多线程,异步委托方面的知识

我博客也有这方面的入门:http://www.cnblogs.com/nsky/p/4425286.html

这里把连接的客服端保存在字典中

key:当前的ip value :当前的socke通信

//连接成功。保存信息
if (!Common.connSocket.ContainsKey(ip))
        Common.connSocket.Add(ip, connSocket);

当客户端连接。就获取所有在线的ip

//用户第一次登陆。获取所有在线用户  如果有好友功能。则获取他的好友
  SendesClient(connSocket);

SendesClient方法

 1 /// <summary>
 2         /// 把上线的人发送到客户端
 3         /// </summary>
 4         /// <param name="connSocket">当前连接的客户端</param>
 5         void SendesClient(Socket connSocket)
 6         {
 7             //自定义协议:[命令 2位]
 8             /*
 9              * 第一位:10代表是首次登陆获取所有好友,把自己的ip放最后一位
10              * 好像这里默认已经是最后一位了??
11              */
12             string key = connSocket.RemoteEndPoint.ToString();
13             //把自己的ip删除
14             if (Common.connSocket.ContainsKey(key))
15                 Common.connSocket.Remove(key);
16
17             //把自己的key添加到最后一位
18             if (!Common.connSocket.ContainsKey(key))
19                 Common.connSocket.Add(key, connSocket);
20
21             //发送到客户端
22             byte[] clientByte = Encoding.UTF8.GetBytes(string.Join(",", Common.connSocket.Keys));
23
24
25             //List<byte> bbb = new List<byte>();
26             //bbb.Add(1);
27             //bbb.AddRange(clientByte);
28
29             List<byte> li = clientByte.ToList();
30             li.Insert(0, 10);//第一位插入10 代表是获取好友
31
32             //把当前在线ip发送给自己
33             connSocket.Send(li.ToArray());
34
35             //告诉其他在线的用户。我上线啦,求勾搭
36             //var online = from onn in Common.connSocket
37             //             where !onn.Key.Contains(connSocket.RemoteEndPoint.ToString())  //筛选,不包含自己的ip
38             //             select onn;
39
40             //if (online.Count() <= 0) return; //当前没有上线的
41
42             foreach (KeyValuePair<string, Socket> item in Common.connSocket)
43             {
44                 //不需要给自己发送。因为当自己上线的时候。就已经获取了在线的ip
45                 if (item.Key == connSocket.RemoteEndPoint.ToString()) continue;
46                 //多线程通知在线用户。
47                 Thread thread = new Thread(() =>
48                 {
49                     byte[] buffer = Encoding.UTF8.GetBytes(connSocket.RemoteEndPoint.ToString());
50                     List<byte> list = buffer.ToList();
51                     //有人上线
52                     //[命令(12)| ip(上线的ip)| ...]
53                     list.Insert(0, 12);//说明有人上线
54                     item.Value.Send(list.ToArray());
55                 });
56                 thread.IsBackground = true;
57                 thread.Start();
58             }
59
60             //判断当前用户是否还处于连接状态
61             //if (Common.connSocket.ContainsKey(connSocket.RemoteEndPoint.ToString()))
62             //connSocket.Send(buffer);
63
64             //有人下线。就通知所有在线的人数
65             //如果是qq我想应该是通知我的好友。不是知道是不是
66
67             /*
68              * 如果在线有 A B C
69              * 那么A下线
70              * 是不是要通知B C
71              * 还是在B C 定时访问服务器来获取在线人数呢?
72              * 待解决
73              */
74         }

那么Listen方法是怎么循环监听的呢?其实就是在Listen里面循环调用自己

 //等待新的客户端连接 ,相当于循环调用
  clientSocket.BeginAccept(new AsyncCallback(Listen), clientSocket);

监听有客服端连接成功。就开始接受客户端信息

1  //接收来自客户端信息 ,相当于循环调用
2   connSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), connSocket);

Common.ReceiveBuffer是什么??

这是我把公共的数据封装在了一个Common类里面。下面会给出

循环监听有了。那么写一个循环接收消息的也就很简单了。依葫芦画瓢

Receive方法:

 1  /// <summary>
 2         /// 接收来自客户端信息
 3         /// </summary>
 4         /// <param name="result"></param>
 5         void Receive(IAsyncResult result)
 6         {
 7
 8             //与客户端通信的socket
 9             Socket clientSocket = result.AsyncState as Socket;
10
11             try
12             {
13                 //获取实际的长度值
14                 int num = clientSocket.EndReceive(result);
15                 if (num > 0)
16                 {
17                     byte[] data = new byte[num];
18                     //复制实际的长度到data字节数组中
19                     Array.Copy(Common.ReceiveBuffer, 0, data, 0, num);
20
21                     //判断协议位
22                     int command = data[0];
23
24                     //获取内容
25                     string source = Encoding.UTF8.GetString(data, 1, num - 1);
26
27                     //获取接收者的ip
28                     string receiveIp = source.Split(‘,‘)[0];
29
30
31                     if (command == 1) //说明发送的是文字
32                     {
33                         /*协议:
34                          * //[命令(1)|对方的ip和自己的ip 50位)| 内容(文字) | ...]
35                          */
36                         //获取接收者通信连接的socket
37                         if (Common.connSocket.ContainsKey(receiveIp))
38                         {
39                             Common.connSocket[receiveIp].Send(data);
40                         }
41                     }
42                     else if (command == 0) //说明是发送的文件
43                     {
44                         /*协议: 这里50位不知道是否理想。
45                          * [命令(0)| ip(对方的ip和自己的ip 50位)| 内容(文件大小和文件全名 30位)|响应(文件内容) | ...]
46                          */
47
48                         //获取接收者通信连接的socket
49                         if (Common.connSocket.ContainsKey(receiveIp))
50                         {
51                             Common.connSocket[receiveIp].Send(data);
52                         }
53                     }
54                     else if (command == 2)//抖动一下
55                     {
56                         //协议
57                         //震动
58                         //[命令(2)| 对方的ip和自己的ip 50位| ...]
59
60                         //获取接收者通信连接的socket
61                         if (Common.connSocket.ContainsKey(receiveIp))
62                         {
63                             Common.connSocket[receiveIp].Send(data);
64                         }
65                     }
66
67                     //string msg = Encoding.UTF8.GetString(data);
68                     //MessageBox.Show(msg);
69
70
71                     //接收其他信息
72                     clientSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), clientSocket);
73
74                 }
75                 else //客户端断开
76                 {
77                     clientOff(clientSocket);
78                 }
79             }
80             catch (Exception ex)
81             {
82                 clientOff(clientSocket);
83             }
84
85         }

当客户端端口会执行clientOff方法。用来通知其他用户。更新列表

clientOff方法

 1  /// <summary>
 2         /// 客户端关闭
 3         /// </summary>
 4         void clientOff(Socket clientSocket)
 5         {
 6             //从集合删除下线的ip
 7             string outIp = clientSocket.RemoteEndPoint.ToString();
 8             if (Common.connSocket.ContainsKey(outIp))
 9                 Common.connSocket.Remove(outIp);
10
11             //更新服务器在线人数
12             changOnlineCount(false);
13
14             this.Invoke(new Action(() =>
15             {
16                 //更新列表
17                 //删除退出的ip
18                 for (int i = 0; i < dgvList.Rows.Count; i++)
19                 {
20                     if (dgvList.Rows[i].Tag.ToString() == outIp)
21                     {
22                         dgvList.Rows.RemoveAt(i);
23                         break;
24                     }
25                 }
26             }));
27
28             clientSocket.Shutdown(SocketShutdown.Receive);
29             clientSocket.Close();
30
31             //通知所有在线用户。有人下线了。需要更新列表,如果是qq是通知我的好友。不知道是不是这样
32             /*这里有点疑问:
33              * 是客户端定时到服务器获取在线用户?
34              * 还是服务器通知客户端
35
36              */
37
38             //有人下线 协议
39             //[命令(11)| ip(下线的ip)| ...]
40
41             //我这里通知客户端吧
42             foreach (KeyValuePair<string, Socket> item in Common.connSocket)
43             {
44                 Thread thread = new Thread(() =>
45                 {
46                     byte[] buffer = Encoding.UTF8.GetBytes(outIp);
47                     List<byte> list = buffer.ToList();
48                     list.Insert(0, 11);//添加协议位
49                     item.Value.Send(list.ToArray());
50                 });
51                 thread.IsBackground = true;
52                 thread.Start();
53
54
55                 //多线程。通知每个在线用户。更新列表
56                 //ThreadPool.QueueUserWorkItem(new WaitCallback(new Action<object>((o) =>
57                 //{
58                 //    string result = string.Join(",", Common.connSocket.Keys);
59                 //    byte[] buffer = Encoding.UTF8.GetBytes(result);
60                 //    try
61                 //    {
62                 //        //客户端关闭,则发送报异常
63                 //        item.Value.Send(buffer);
64                 //    }
65                 //    catch (Exception ex)
66                 //    {
67
68                 //    }
69                 //})));
70                 //string result = string.Join(",", Common.connSocket.Keys);
71                 //byte[] buffer = Encoding.UTF8.GetBytes(result);
72                 //item.Value.Send(buffer);
73
74             }
75         }

有没有想过。服务器是怎么知道客户端发送的是文字。文件还是抖动呢?

这里就要提到自定义协议了。双方约定好。比如:

客户端发送0 就代表是文字。 发送1 则代表是文件 2 则代表是抖动。

我这里自定义的协议如下。不能说很好。只能说马马虎虎过得去

 1 /*********************协议说明***********************/
 2         //根据协议解码
 3         /*
 4          * 自定义协议规则
 5          *  [ 命令(1) | 内容(30) | ip(22)| 响应(....) | ...]
 6            命令:0-文件 1-文字 2-震动
 7          * 内容:
 8          * 文件(长度,文件名字+后缀名) 响应(文件的字节)
 9          * 文字(内容)
10          * 震动()
11          * ip(自己的ip和对方的ip)
12          *
13          * [ 命令(1) | ip(22) | 内容(30)| 响应(....) | ...]
14          *
15          * 文件:
16          *  [命令(0)| ip(自己的ip和对方的ip)| 内容(文件大小和文件全名)|响应(文件内容) | ...]
17          * 文字:
18          *  [命令(1)|对方的ip和自己的ip 50位)| 内容(文字) | ...]
19          * 震动
20          *  [命令(2)| ip(对方的ip)| ...]
21          * 更新在线人数
22          * [命令(3)| ip(自己的ip和对方的ip)| ...]
23          * 第一次登陆获取在线(好友)人数
24          * [命令(10)| ip(自己的ip和所有的ip)| ...]
25          * 有人下线
26          *  [命令(11)| ip(下线的ip)| ...]
27          * 有人上线
28          *  [命令(12)| ip(上线的ip)| ...]
29          *  0
30          *  1
31          *  2
32          *  3
33          *  4
34          *  5
35          *
36          */

既然协议有了。那么就可以根据协议发送数据包。服务器和其他客户端就可以根据协议解包。

这里来说下发送文件的协议

从上面可以看到我定义的文件协议:

[命令(0)| ip(自己的ip和对方的ip)| 内容(文件大小和文件全名)|响应(文件内容) | ...]

先贴上发送文件的代码(客户端发送)。然后根据我的思路来分析

客户端发送文件代码

 1 /// <summary>
 2         /// 发送文件
 3         /// </summary>
 4         /// <param name="sender"></param>
 5         /// <param name="e"></param>
 6         void btnSendFile_Click(object sender, EventArgs e)
 7         {
 8             //判断是否有选择用户
 9             string sendUser = cbList.Text;
10
11             if (string.IsNullOrEmpty(sendUser))
12             {
13                 //提示
14                 tbHis.AppendText("请选择要发送的用户...\n");
15                 return;
16             }
17             //判断是否选择了文件
18             else if (string.IsNullOrEmpty(tbFile.Text))
19             {
20                 tbHis.AppendText("请选择文件\n");
21                 tbHis.AppendText("\n");
22                 return;
23             }
24             //开始读取文件
25             using (FileStream fs = new FileStream(tbFile.Text, FileMode.Open, FileAccess.Read))
26             {
27                 //大文件会内存溢出
28                 //引发类型为“System.OutOfMemoryException”的异常。
29                 //所以大文件只能 续传。像QQ一样在线接收的方式。
30                 byte[] buffer = new byte[fs.Length];
31                 //获取实际的字节数,如果有需要的话。
32                 int num = fs.Read(buffer, 0, buffer.Length);
33
34                 /*协议: 这里50位不知道是否理想??
35                  * 是不是可以修改为:第一位 协议 第二位标记ip的长度 第三位标记内容的长度??
36                  * [命令(0)| ip(对方的ip和自己的ip 50位)| 内容(文件大小和文件全名 30)|响应(文件内容) | ...]
37                  */
38                 string allIp = string.Format("{0},{1}", cbList.Text, myIp.Text);
39                 byte[] sendIp = Encoding.UTF8.GetBytes(allIp);
40
41                 List<byte> list = sendIp.ToList();
42
43                 //sendIp 不够50位
44                 if (sendIp.Length < 50)
45                 {
46                     for (int i = 0; i < 50 - sendIp.Length; i++)
47                     {
48                         list.Add(0);
49                     }
50                 }
51                 list.Insert(0, 0); //添加协议位
52                 //添加内容
53                 byte[] fileByte = Encoding.UTF8.GetBytes(Common.SafeFileName);
54                 list.AddRange(fileByte);
55                 //内容是否够30
56                 if (fileByte.Length < 30)
57                 {
58                     for (int i = 0; i < 30 - fileByte.Length; i++)
59                     {
60                         list.Add(0);
61                     }
62                 }
63                 //添加响应
64                 list.AddRange(buffer);
65
66                 //开始发送
67                 Common.connSocket.Send(list.ToArray());
68             }
69         }

字节数组必须根据你定义的协议来拼接。这样在服务端才好解包

我这里把ip放到了List<byte>中

List<byte> list = sendIp.ToList();

然后在list前面插入协议位:0 ,因为我定义的协议是0代表发送文件

list.Insert(0, 0); //添加协议位

是否还记得上面提到的。客户端接收文件。打开保存对话框。自动补全了文件名。是怎么做的吗?

因为协议中发文件名发过去了

//添加内容
byte[] fileByte = Encoding.UTF8.GetBytes(Common.SafeFileName);
list.AddRange(fileByte);

还有因为不知道ip的固定长度。所以我这里限制是50位。不够的话就补加空,不知道这样是否划算

//sendIp 不够50位
  if (sendIp.Length < 50)
   {
     for (int i = 0; i < 50 - sendIp.Length; i++)
       {
            list.Add(0);
       }
  }

所以的数据根据协议打包后。就可以发送到服务器。在服务器解包。获取目的者的ip。开始转发

Receive方法中你可以找到解包的代码,部分如下:

 1                     byte[] data = new byte[num];
 2                     //复制实际的长度到data字节数组中
 3                     Array.Copy(Common.ReceiveBuffer, 0, data, 0, num);
 4
 5                     //判断协议位
 6                     int command = data[0];
 7
 8                     //获取内容
 9                     string source = Encoding.UTF8.GetString(data, 1, num - 1);
10
11                     //获取接收者的ip
12                     string receiveIp = source.Split(‘,‘)[0];
13
14
15                     if (command == 1) //说明发送的是文字
16                     {
17                         /*协议:
18                          * //[命令(1)|对方的ip和自己的ip 50位)| 内容(文字) | ...]
19                          */
20                         //获取接收者通信连接的socket
21                         if (Common.connSocket.ContainsKey(receiveIp))
22                         {
23                             Common.connSocket[receiveIp].Send(data);
24                         }
25                     }

获取协议位:

int command = data[0];

注:

你会发现我这里在服务器解包判断协议毫无意义。因为第一位是命令。而后面接着是ip。这些长度都是固定的。

只有取出来ip发送到指定的客户端。让客户端解包判断协议就是了。没错。因为我这里本来加个功能。如果是文件则把文件缓存起来。所以就判断

了协议的命令。你可以根据自己的需求去做。

发送到了客户端,就理所当然的要判断协议位了。如果是抖动就抖一下。是文件。就提示是否接受。等等

看看客户端解包代码:

记住:下面的Receive方法是客户端中的方法。用力接收服务器发来的消息

  1 /// <summary>
  2         /// 接收来自服务器的消息
  3         /// </summary>
  4         /// <param name="result"></param>
  5         void Receive(IAsyncResult result)
  6         {
  7             Socket clientSocket = result.AsyncState as Socket;
  8
  9             //byte[] b = new byte[1024 * 1024];
 10             try
 11             {
 12                 //获取实际的长度
 13                 int num = clientSocket.EndReceive(result);
 14                 if (num > 0)
 15                 {
 16                     //MessageBox.Show(num.ToString());
 17                     byte[] buffer = new byte[num];
 18                     Array.Copy(Common.buffer, 0, buffer, 0, num); //复制数据到data
 19                     //string ip = Encoding.UTF8.GetString(data);
 20
 21
 22                     //以下是客户端=》服务器==》服务器
 23                     /*
 24                      * 当if else 超过3个
 25                      *
 26                      * 建议用switch case语句
 27                      */
 28
 29                     //获取口令
 30                     int command = buffer[0];
 31                     //说明是获取好友
 32                     if (command == 10)
 33                     {
 34                         //协议说明
 35                         //第一次登陆获取在线(好友)人数
 36                         //[命令(10)| ip(自己的ip和所有好友的ip)| ...]
 37
 38
 39                         //其实用户本地ip也可以这样获取
 40                         //string cy = clientSocket.LocalEndPoint;
 41
 42                         string allIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
 43                         string[] temp = allIp.Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
 44
 45
 46                         //跨线程操作UI
 47                         this.Invoke(new Action(() =>
 48                         {
 49                             myIp.Text = temp.Length > 0 ? temp[temp.Length - 1] : "";
 50
 51                             //排除自己的ip
 52                             var other = from i in temp where !i.Contains(myIp.Text) select i;
 53
 54                             cbList.Items.Clear();//清空
 55                             cbList.Items.AddRange(other.ToArray()); //绑定列表
 56
 57                             if (cbList.Items.Count > 0)
 58                                 cbList.SelectedIndex = 0;//默认选中第一个
 59                         }));
 60
 61                     }
 62                     else if (command == 11) //说明是有人下线
 63                     {
 64                         //协议说明
 65                         // 有人下线
 66                         //[命令(11)| ip(下线的ip)| ...]
 67
 68                         //获取下线的ip
 69                         string outIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
 70
 71                         this.Invoke(new Action(() =>
 72                         {
 73                             //删除下线的ip
 74                             cbList.Items.Remove(outIp);
 75                             if (cbList.Items.Count > 0)
 76                                 cbList.SelectedIndex = 0;//默认选中第一个
 77                         }));
 78                     }
 79                     else if (command == 12) //有人上线了
 80                     {
 81                         //协议说明
 82                         // 有人上线
 83                         //[命令(12)| ip(上线的ip)| ...]
 84
 85                         //获取上线的ip
 86                         string onlineIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
 87                         //添加上线的ip
 88
 89                         this.Invoke(new Action(() =>
 90                         {
 91                             //添加上线的ip
 92                             cbList.Items.Add(onlineIp);
 93                             if (cbList.Items.Count > 0)
 94                                 cbList.SelectedIndex = 0;//默认选中第一个
 95                         }));
 96                     }
 97
 98                     //以下是客户端=》服务器==》客户端
 99
100                     else if (command == 1)
101                     {
102                         //协议:
103                         //[命令(1)|对方的ip和自己的ip 50位)| 内容(文字) | ...]
104
105                         //获取ip段
106                         string[] sourceIp = Encoding.UTF8.GetString(buffer, 1, 50).Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
107
108                         //发消息来的ip
109                         string fromIp = sourceIp[1];
110
111
112
113                         //获取内容
114                         string content = Encoding.UTF8.GetString(buffer, 50 + 1, num - 50 - 1);
115
116                         this.Invoke(new Action(() =>
117                         {
118                             //列表框中选择当前的ip
119                             cbList.Text = fromIp.ToString();
120
121                             //显示内容
122                             tbHis.AppendText(string.Format("时间:{0}\n", DateTime.Now.ToString()));
123                             //tbHis.AppendText(string.Format("提示{0}对我说:", fromIp)); //我操。这样怎么就不行
124                             tbHis.AppendText(fromIp + "\n");
125                             tbHis.AppendText("对你说:\n");
126                             tbHis.AppendText(content + "\n");
127                             tbHis.AppendText("\n");
128                         }));
129                     }
130                     else if (command == 0) //发送的文件
131                     {
132                         /*协议: 这里50位不知道是否理想。
133                          * [命令(0)| ip(对方的ip和自己的ip 50位)| 内容(文件大小和文件全名 30位)|响应(文件内容) | ...]
134                          */
135
136                         //这里有冗余代码
137
138                         //获取ip段
139                         string[] sourceIp = Encoding.UTF8.GetString(buffer, 1, 50).Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
140
141                         //发消息来的ip
142                         string fromIp = sourceIp[1];
143
144                         this.Invoke(new Action(() =>
145                         {
146                             //列表框中选择当前的ip
147                             cbList.Text = fromIp.ToString();
148                         }));
149
150
151                         //获取内容
152                         string content = Encoding.UTF8.GetString(buffer, 50 + 1, 30);
153
154                         //获取响应
155                         //string pass = Encoding.UTF8.GetString(buffer, 50 + 30 + 1, num - 50 - 30 - 1);
156
157                         //显示
158                         //tbHis.AppendText(string.Format("{0}给你发了一个文件:{1}\n", fromIp, content));
159                         tbHis.AppendText(fromIp);
160                         tbHis.AppendText("给你发了一个文件");
161                         tbHis.AppendText(content + "\n");
162                         tbHis.AppendText("\n");
163
164
165                         //提示用户是否接收文件
166                         if (MessageBox.Show("是否接受文件\n" + content, "接收文件", MessageBoxButtons.YesNo) == DialogResult.Yes)
167                         {
168                             //开始保存
169                             SaveFileDialog sfd = new SaveFileDialog();
170                             sfd.FileName = content;
171
172                             //获取文件类型
173                             string ex = content.Split(‘.‘)[1];
174
175                             //保存文件类型
176                             sfd.Filter = "|*." + ex;
177                             if (sfd.ShowDialog(this) == DialogResult.OK)
178                             {
179                                 using (FileStream fs = new FileStream(sfd.FileName, FileMode.Create))
180                                 {
181                                     fs.Write(buffer, 50 + 30 + 1, num - 50 - 30 - 1);
182                                 }
183                             }
184
185                         }
186
187                     }
188                     else if (command == 2) //发送抖动
189                     {
190                         //如果窗口在任务栏。则显示
191                         this.Show();
192                         this.WindowState = FormWindowState.Normal;
193                         this.Activate();
194
195                         int n = -1;
196
197                         for (int i = 0; i < 10; i++)
198                         {
199                             n = -n;
200                             this.Location = new Point(this.Location.X + 10 * n, this.Location.Y + 10 * n);
201                             this.TopMost = true;//在所有窗口的顶部
202                             System.Threading.Thread.Sleep(50);
203                         }
204
205                         //抖动完成。结束顶层显示
206                         this.TopMost = false;
207                     }
208
209                     //连接成功,再一次获取服务器发来的消息
210                     clientSocket.BeginReceive(Common.buffer, 0, Common.buffer.Length, 0, new AsyncCallback(Receive), clientSocket);
211
212                 }
213             }
214             catch (Exception ex) //服务器端口
215             {
216                 MessageBox.Show(ex.Message);
217                 clientSocket.Shutdown(SocketShutdown.Receive);
218                 clientSocket.Close();
219             }
220         }

都是if else if判断的。着实看着会晕乎乎的。因为是小案列。就没去过多的封装。大家可以根据自己的喜好去封装

上面大部分都是讲的服务端口的代码。相比客户端就是一样的。只是少了一个监听的代码。直接连接服务器。接收和发送消息就ok了

这里就不分析了。稍后直接贴上参考代码

当然socket通信协议方面的知识不单单就我这点皮毛。大家自己可以深入研究。

比如:很多网站右下角都会有这样一个窗口,大家都见过吧。

我想这也是相同的原理。当打开浏览器。ajax异步去连接服务器,

获取消息发送到客户端。你说呢?

因为想做微信打飞机的游戏。其实网上也有很多源码,可我认为别人的终究是别人的。只有自己做过那才叫曾经拥有。

可单机版的太没有挑战性,那就实现一个多人在线一起玩的。就涉及到了服务器

所以想用C#写一个服务器,unity3d做客户端,与之通信。所以才有了这篇博文的诞生。

最后把全部源码给大家参考下

服务端源码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 using System.Net;
 10 using System.Net.Sockets;
 11 using System.Threading;
 12
 13 namespace TopServer
 14 {
 15     public partial class mainServer : Form
 16     {
 17
 18         /*********************协议说明***********************/
 19         //根据协议解码
 20         /*
 21          * 自定义协议规则
 22          *  [ 命令(1) | 内容(30) | ip(22)| 响应(....) | ...]
 23            命令:0-文件 1-文字 2-震动
 24          * 内容:
 25          * 文件(长度,文件名字+后缀名) 响应(文件的字节)
 26          * 文字(内容)
 27          * 震动()
 28          * ip(自己的ip和对方的ip)
 29          *
 30          * [ 命令(1) | ip(22) | 内容(30)| 响应(....) | ...]
 31          *
 32          * 文件:
 33          *  [命令(0)| ip(自己的ip和对方的ip)| 内容(文件大小和文件全名)|响应(文件内容) | ...]
 34          * 文字:
 35          *  [命令(1)|对方的ip和自己的ip 50位)| 内容(文字) | ...]
 36          * 震动
 37          *  [命令(2)| ip(对方的ip)| ...]
 38          * 更新在线人数
 39          * [命令(3)| ip(自己的ip和对方的ip)| ...]
 40          * 第一次登陆获取在线(好友)人数
 41          * [命令(10)| ip(自己的ip和所有的ip)| ...]
 42          * 有人下线
 43          *  [命令(11)| ip(下线的ip)| ...]
 44          * 有人上线
 45          *  [命令(12)| ip(上线的ip)| ...]
 46          *  0
 47          *  1
 48          *  2
 49          *  3
 50          *  4
 51          *  5
 52          *
 53          */
 54
 55         public mainServer()
 56         {
 57             InitializeComponent();
 58             Init();
 59         }
 60
 61         /// <summary>
 62         /// 初始化datagridview属性
 63         /// </summary>
 64         public void Init()
 65         {
 66
 67             #region datagridview一些属性设置
 68             dgvList.AllowUserToAddRows = false;
 69             dgvList.AllowUserToDeleteRows = false;
 70             dgvList.AllowUserToResizeColumns = false;
 71             dgvList.AllowUserToResizeRows = false;
 72             dgvList.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
 73             dgvList.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 74             dgvList.MultiSelect = false;
 75             dgvList.ReadOnly = true;
 76             dgvList.RowHeadersVisible = false;
 77             dgvList.BackgroundColor = Color.White;
 78             dgvList.ScrollBars = ScrollBars.Vertical;
 79             dgvList.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
 80             #endregion
 81
 82             //窗体加载事件
 83             //this.Load += new EventHandler(mainServer_Load);
 84
 85             //启动服务器按钮
 86             this.btnStart.Click += new EventHandler(btnStart_Click);
 87
 88             //关闭服务器按钮
 89             this.btnStop.Click += new EventHandler(btnStop_Click);
 90         }
 91
 92         /// <summary>
 93         /// 关闭服务器
 94         /// </summary>
 95         /// <param name="sender"></param>
 96         /// <param name="e"></param>
 97         void btnStop_Click(object sender, EventArgs e)
 98         {
 99             if (Common.ListenSocket != null)
100             {
101                 Common.ListenSocket.Close();
102                 Common.ListenSocket = null;
103                 btnStart.Enabled = true;
104                 //底部提示消息
105                 tssMsg.Text = "服务器已经关闭";
106             }
107         }
108
109         /// <summary>
110         /// 启动服务器
111         /// </summary>
112         /// <param name="sender"></param>
113         /// <param name="e"></param>
114         void btnStart_Click(object sender, EventArgs e)
115         {
116             StartServer();
117         }
118
119         /// <summary>
120         /// 窗体加载事件
121         /// </summary>
122         /// <param name="sender"></param>
123         /// <param name="e"></param>
124         void mainServer_Load(object sender, EventArgs e)
125         {
126             //启动时间
127             startTime.Text = DateTime.Now.ToString();
128
129             //启动服务器
130             StartServer();
131         }
132
133         /// <summary>
134         /// 打开服务器
135         /// </summary>
136         void StartServer()
137         {
138             try
139             {
140                 string _ip = tbIp.Text;
141                 int _point = int.Parse(tbPoint.Text);
142
143                 //创建监听客户端请求的socket
144                 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
145                 //监听用的ip和端口
146                 IPAddress address = IPAddress.Parse(_ip);
147                 IPEndPoint point = new IPEndPoint(address, _point);
148
149                 //绑定
150                 socket.Bind(point);
151                 socket.Listen(10);
152
153
154                 //异步 开始监听
155                 socket.BeginAccept(new AsyncCallback(Listen), socket);
156
157
158                 //禁用当前按钮
159                 btnStart.Enabled = false;
160
161                 //启动时间
162                 startTime.Text = DateTime.Now.ToString();
163
164                 //底部提示消息
165                 tssMsg.Text = "服务器已经启动";
166             }
167             catch (Exception ex)
168             {
169                 MessageBox.Show(ex.Message);
170             }
171
172         }
173
174         /// <summary>
175         /// 开始监听
176         /// </summary>
177         /// <param name="result"></param>
178         void Listen(IAsyncResult result)
179         {
180
181             try
182             {
183                 //获取监听的socket
184                 Socket clientSocket = result.AsyncState as Socket;
185                 //与服务器通信的socket
186                 Socket connSocket = clientSocket.EndAccept(result);
187
188                 string ip = connSocket.RemoteEndPoint.ToString();
189                 //连接成功。保存信息
190                 if (!Common.connSocket.ContainsKey(ip))
191                     Common.connSocket.Add(ip, connSocket);
192
193                 //连接成功,更新服务器信息
194                 changeList(connSocket);
195
196
197                 //等待新的客户端连接 ,相当于循环调用
198                 clientSocket.BeginAccept(new AsyncCallback(Listen), clientSocket);
199
200                 byte[] buffer = new byte[1024 * 1024];
201
202
203                 //接收来自客户端信息 ,相当于循环调用
204                 connSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), connSocket);
205
206                 //用户第一次登陆。获取所有在线用户  如果有好友功能。则获取他的好友
207                 SendesClient(connSocket);
208             }
209             catch (Exception ex)
210             {
211
212                 //MessageBox.Show(ex.Message);
213             }
214
215
216         }
217         /// <summary>
218         /// 接收来自客户端信息
219         /// </summary>
220         /// <param name="result"></param>
221         void Receive(IAsyncResult result)
222         {
223
224             //与客户端通信的socket
225             Socket clientSocket = result.AsyncState as Socket;
226
227             try
228             {
229                 //获取实际的长度值
230                 int num = clientSocket.EndReceive(result);
231                 if (num > 0)
232                 {
233                     byte[] data = new byte[num];
234                     //复制实际的长度到data字节数组中
235                     Array.Copy(Common.ReceiveBuffer, 0, data, 0, num);
236
237                     //判断协议位
238                     int command = data[0];
239
240                     //获取内容
241                     string source = Encoding.UTF8.GetString(data, 1, num - 1);
242
243                     //获取接收者的ip
244                     string receiveIp = source.Split(‘,‘)[0];
245
246
247                     if (command == 1) //说明发送的是文字
248                     {
249                         /*协议:
250                          * //[命令(1)|对方的ip和自己的ip 50位)| 内容(文字) | ...]
251                          */
252                         //获取接收者通信连接的socket
253                         if (Common.connSocket.ContainsKey(receiveIp))
254                         {
255                             Common.connSocket[receiveIp].Send(data);
256                         }
257                     }
258                     else if (command == 0) //说明是发送的文件
259                     {
260                         /*协议: 这里50位不知道是否理想。
261                          * [命令(0)| ip(对方的ip和自己的ip 50位)| 内容(文件大小和文件全名 30位)|响应(文件内容) | ...]
262                          */
263
264                         //获取接收者通信连接的socket
265                         if (Common.connSocket.ContainsKey(receiveIp))
266                         {
267                             Common.connSocket[receiveIp].Send(data);
268                         }
269                     }
270                     else if (command == 2)//抖动一下
271                     {
272                         //协议
273                         //震动
274                         //[命令(2)| 对方的ip和自己的ip 50位| ...]
275
276                         //获取接收者通信连接的socket
277                         if (Common.connSocket.ContainsKey(receiveIp))
278                         {
279                             Common.connSocket[receiveIp].Send(data);
280                         }
281                     }
282
283                     //string msg = Encoding.UTF8.GetString(data);
284                     //MessageBox.Show(msg);
285
286
287                     //接收其他信息
288                     clientSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), clientSocket);
289
290                 }
291                 else //客户端断开
292                 {
293                     clientOff(clientSocket);
294                 }
295             }
296             catch (Exception ex)
297             {
298                 clientOff(clientSocket);
299             }
300
301         }
302
303         /// <summary>
304         /// 客户端关闭
305         /// </summary>
306         void clientOff(Socket clientSocket)
307         {
308             //从集合删除下线的ip
309             string outIp = clientSocket.RemoteEndPoint.ToString();
310             if (Common.connSocket.ContainsKey(outIp))
311                 Common.connSocket.Remove(outIp);
312
313             //更新服务器在线人数
314             changOnlineCount(false);
315
316             this.Invoke(new Action(() =>
317             {
318                 //更新列表
319                 //删除退出的ip
320                 for (int i = 0; i < dgvList.Rows.Count; i++)
321                 {
322                     if (dgvList.Rows[i].Tag.ToString() == outIp)
323                     {
324                         dgvList.Rows.RemoveAt(i);
325                         break;
326                     }
327                 }
328             }));
329
330             clientSocket.Shutdown(SocketShutdown.Receive);
331             clientSocket.Close();
332
333             //通知所有在线用户。有人下线了。需要更新列表,如果是qq是通知我的好友。不知道是不是这样
334             /*这里有点疑问:
335              * 是客户端定时到服务器获取在线用户?
336              * 还是服务器通知客户端
337
338              */
339
340             //有人下线 协议
341             //[命令(11)| ip(下线的ip)| ...]
342
343             //我这里通知客户端吧
344             foreach (KeyValuePair<string, Socket> item in Common.connSocket)
345             {
346                 Thread thread = new Thread(() =>
347                 {
348                     byte[] buffer = Encoding.UTF8.GetBytes(outIp);
349                     List<byte> list = buffer.ToList();
350                     list.Insert(0, 11);//添加协议位
351                     item.Value.Send(list.ToArray());
352                 });
353                 thread.IsBackground = true;
354                 thread.Start();
355
356
357                 //多线程。通知每个在线用户。更新列表
358                 //ThreadPool.QueueUserWorkItem(new WaitCallback(new Action<object>((o) =>
359                 //{
360                 //    string result = string.Join(",", Common.connSocket.Keys);
361                 //    byte[] buffer = Encoding.UTF8.GetBytes(result);
362                 //    try
363                 //    {
364                 //        //客户端关闭,则发送报异常
365                 //        item.Value.Send(buffer);
366                 //    }
367                 //    catch (Exception ex)
368                 //    {
369
370                 //    }
371                 //})));
372                 //string result = string.Join(",", Common.connSocket.Keys);
373                 //byte[] buffer = Encoding.UTF8.GetBytes(result);
374                 //item.Value.Send(buffer);
375
376             }
377         }
378         /// <summary>
379         /// 把上线的人发送到客户端
380         /// </summary>
381         /// <param name="connSocket">当前连接的客户端</param>
382         void SendesClient(Socket connSocket)
383         {
384             //自定义协议:[命令 2位]
385             /*
386              * 第一位:10代表是首次登陆获取所有好友,把自己的ip放最后一位
387              * 好像这里默认已经是最后一位了??
388              */
389             string key = connSocket.RemoteEndPoint.ToString();
390             //把自己的ip删除
391             if (Common.connSocket.ContainsKey(key))
392                 Common.connSocket.Remove(key);
393
394             //把自己的key添加到最后一位
395             if (!Common.connSocket.ContainsKey(key))
396                 Common.connSocket.Add(key, connSocket);
397
398             //发送到客户端
399             byte[] clientByte = Encoding.UTF8.GetBytes(string.Join(",", Common.connSocket.Keys));
400
401
402             //List<byte> bbb = new List<byte>();
403             //bbb.Add(1);
404             //bbb.AddRange(clientByte);
405
406             List<byte> li = clientByte.ToList();
407             li.Insert(0, 10);//第一位插入10 代表是获取好友
408
409             //把当前在线ip发送给自己
410             connSocket.Send(li.ToArray());
411
412             //告诉其他在线的用户。我上线啦,求勾搭
413             //var online = from onn in Common.connSocket
414             //             where !onn.Key.Contains(connSocket.RemoteEndPoint.ToString())  //筛选,不包含自己的ip
415             //             select onn;
416
417             //if (online.Count() <= 0) return; //当前没有上线的
418
419             foreach (KeyValuePair<string, Socket> item in Common.connSocket)
420             {
421                 //不需要给自己发送。因为当自己上线的时候。就已经获取了在线的ip
422                 if (item.Key == connSocket.RemoteEndPoint.ToString()) continue;
423                 //多线程通知在线用户。
424                 Thread thread = new Thread(() =>
425                 {
426                     byte[] buffer = Encoding.UTF8.GetBytes(connSocket.RemoteEndPoint.ToString());
427                     List<byte> list = buffer.ToList();
428                     //有人上线
429                     //[命令(12)| ip(上线的ip)| ...]
430                     list.Insert(0, 12);//说明有人上线
431                     item.Value.Send(list.ToArray());
432                 });
433                 thread.IsBackground = true;
434                 thread.Start();
435             }
436
437             //判断当前用户是否还处于连接状态
438             //if (Common.connSocket.ContainsKey(connSocket.RemoteEndPoint.ToString()))
439             //connSocket.Send(buffer);
440
441             //有人下线。就通知所有在线的人数
442             //如果是qq我想应该是通知我的好友。不是知道是不是
443
444             /*
445              * 如果在线有 A B C
446              * 那么A下线
447              * 是不是要通知B C
448              * 还是在B C 定时访问服务器来获取在线人数呢?
449              * 待解决
450              */
451         }
452
453         /// <summary>
454         /// 更新列表
455         /// </summary>
456         /// <param name="socket"></param>
457         void changeList(Socket socket)
458         {
459             //获取客户端信息 ip和端口号
460             string ip = socket.RemoteEndPoint.ToString();
461             //客户端登陆时间
462             string time = DateTime.Now.ToString();
463
464             //跨线程操作ui
465             this.Invoke(new Action(() =>
466             {
467                 //新增一行
468                 dgvList.Rows.Add();
469
470                 //获取当前dgvList的行
471                 int rows = dgvList.Rows.Count;
472
473                 //赋值
474                 dgvList.Rows[rows - 1].Cells[0].Value = ip;
475                 dgvList.Rows[rows - 1].Cells[1].Value = time;
476
477                 //把ip当作当前行的tag标记一下,为了删除行的时候可以找到该行
478                 dgvList.Rows[rows - 1].Tag = ip;
479
480                 //更新在线人数
481                 //lbCount.Text = int.Parse(lbCount.Text) + 1 + "";//后面加空字符串。转为字符串
482                 //或者
483                 //lbCount.Text = (int.Parse(lbCount.Text) + 1).ToString();
484
485                 //foreach (DataGridViewRow item in dgvList.Rows)
486                 //{
487                 //   if(item.Tag==ip)item.
488
489                 //}
490
491
492                 //dgvList.DataSource = Common.connSocket;
493
494                 //更新在线人数
495                 changOnlineCount(true);
496             }));
497
498         }
499
500         /// <summary>
501         /// 更新在线人数
502         /// </summary>
503         /// <param name="tag">true=>+ false=>-</param>
504         void changOnlineCount(bool tag)
505         {
506             int num = 0;
507             if (tag) num = int.Parse(lbCount.Text) + 1;
508             else num = int.Parse(lbCount.Text) - 1;
509
510             this.Invoke(new Action(() =>
511             {
512                 //更新在线人数
513                 lbCount.Text = num.ToString();
514                 if (num == 0) Common.connSocket.Clear();
515
516             }));
517         }
518     }
519
520     /// <summary>
521     /// 公共类
522     /// </summary>
523     public class Common
524     {
525         /// <summary>
526         /// 保存服务器来的消息
527         /// </summary>
528         public static byte[] ReceiveBuffer = new byte[1024 * 1024];
529
530         /// <summary>
531         /// 监听用的socket
532         /// </summary>
533         public static Socket ListenSocket;
534
535         /// <summary>
536         /// 保存所有负责通信用是socket
537         /// </summary>
538         public static Dictionary<string, Socket> connSocket = new Dictionary<string, Socket>();
539     }
540 }

客户端源码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 using System.Net.Sockets;
 10 using System.Net;
 11 using System.Threading;
 12 using System.IO;
 13
 14 namespace TopClient
 15 {
 16     public partial class UserList : Form
 17     {
 18         public UserList()
 19         {
 20             InitializeComponent();
 21             Init();
 22         }
 23         public void Init()
 24         {
 25             //窗体加载
 26             this.Load += new EventHandler(UserList_Load);
 27
 28             //发送文字
 29             this.btnSender.Click += new EventHandler(btnSender_Click);
 30
 31             //选择文件
 32             this.btnChangeFile.Click += new EventHandler(btnChangeFile_Click);
 33
 34             //发送文件
 35             this.btnSendFile.Click += new EventHandler(btnSendFile_Click);
 36
 37             //抖动对方
 38             this.btnDd.Click += new EventHandler(btnDd_Click);
 39
 40             //创建time
 41             /*
 42              * 用力监听服务器是否开启
 43              */
 44             //Common.time = new System.Windows.Forms.Timer();
 45             //Common.time.Interval = 1000; //如果服务器未开启。则一秒访问一次
 46             //Common.time.Tick += new EventHandler(time_Tick);
 47         }
 48         /// <summary>
 49         /// 发送抖动
 50         /// </summary>
 51         /// <param name="sender"></param>
 52         /// <param name="e"></param>
 53         void btnDd_Click(object sender, EventArgs e)
 54         {
 55             //判断是否有选择用户
 56             string sendUser = cbList.Text;
 57
 58             if (string.IsNullOrEmpty(sendUser))
 59             {
 60                 //提示
 61                 tbHis.AppendText("请选择要抖动的用户...\n");
 62                 return;
 63             }
 64             //协议
 65             //震动
 66             //[命令(2)| 对方的ip和自己的ip 50位| ...]
 67             string allIp = string.Format("{0},{1}", cbList.Text, myIp.Text);
 68
 69             byte[] sendIp = Encoding.UTF8.GetBytes(allIp);
 70
 71             List<byte> list = sendIp.ToList();
 72             list.Insert(0, 2);//添加协议位
 73
 74             //sendIp 不够50位
 75             if (sendIp.Length < 50)
 76             {
 77                 for (int i = 0; i < 50 - sendIp.Length; i++)
 78                 {
 79                     list.Add(0);
 80                 }
 81             }
 82             //开始发送
 83             Common.connSocket.Send(list.ToArray());
 84         }
 85
 86         /// <summary>
 87         /// 发送文件
 88         /// </summary>
 89         /// <param name="sender"></param>
 90         /// <param name="e"></param>
 91         void btnSendFile_Click(object sender, EventArgs e)
 92         {
 93             //判断是否有选择用户
 94             string sendUser = cbList.Text;
 95
 96             if (string.IsNullOrEmpty(sendUser))
 97             {
 98                 //提示
 99                 tbHis.AppendText("请选择要发送的用户...\n");
100                 return;
101             }
102             //判断是否选择了文件
103             else if (string.IsNullOrEmpty(tbFile.Text))
104             {
105                 tbHis.AppendText("请选择文件\n");
106                 tbHis.AppendText("\n");
107                 return;
108             }
109             //开始读取文件
110             using (FileStream fs = new FileStream(tbFile.Text, FileMode.Open, FileAccess.Read))
111             {
112                 //大文件会内存溢出
113                 //引发类型为“System.OutOfMemoryException”的异常。
114                 //所以大文件只能 续传。像QQ一样在线接收的方式。
115                 byte[] buffer = new byte[fs.Length];
116                 //获取实际的字节数,如果有需要的话。
117                 int num = fs.Read(buffer, 0, buffer.Length);
118
119                 /*协议: 这里50位不知道是否理想??
120                  * 是不是可以修改为:第一位 协议 第二位标记ip的长度 第三位标记内容的长度??
121                  * [命令(0)| ip(对方的ip和自己的ip 50位)| 内容(文件大小和文件全名 30)|响应(文件内容) | ...]
122                  */
123                 string allIp = string.Format("{0},{1}", cbList.Text, myIp.Text);
124                 byte[] sendIp = Encoding.UTF8.GetBytes(allIp);
125
126                 List<byte> list = sendIp.ToList();
127
128                 //sendIp 不够50位
129                 if (sendIp.Length < 50)
130                 {
131                     for (int i = 0; i < 50 - sendIp.Length; i++)
132                     {
133                         list.Add(0);
134                     }
135                 }
136                 list.Insert(0, 0); //添加协议位
137                 //添加内容
138                 byte[] fileByte = Encoding.UTF8.GetBytes(Common.SafeFileName);
139                 list.AddRange(fileByte);
140                 //内容是否够30
141                 if (fileByte.Length < 30)
142                 {
143                     for (int i = 0; i < 30 - fileByte.Length; i++)
144                     {
145                         list.Add(0);
146                     }
147                 }
148                 //添加响应
149                 list.AddRange(buffer);
150
151                 //开始发送
152                 Common.connSocket.Send(list.ToArray());
153             }
154         }
155
156         /// <summary>
157         /// 选择文件
158         /// </summary>
159         /// <param name="sender"></param>
160         /// <param name="e"></param>
161         void btnChangeFile_Click(object sender, EventArgs e)
162         {
163             OpenFileDialog ofd = new OpenFileDialog();
164             if (ofd.ShowDialog() == DialogResult.OK)
165             {
166                 tbFile.Text = ofd.FileName;
167                 //保存文件名和扩展名
168                 Common.SafeFileName = ofd.SafeFileName;
169             }
170         }
171         /// <summary>
172         ///
173         /// </summary>
174         /// <param name="sender"></param>
175         /// <param name="e"></param>
176         void time_Tick(object sender, EventArgs e)
177         {
178             connectServer();
179         }
180
181         /// <summary>
182         /// 发送
183         /// </summary>
184         /// <param name="sender"></param>
185         /// <param name="e"></param>
186         void btnSender_Click(object sender, EventArgs e)
187         {
188             //判断是否有选择用户
189             string sendUser = cbList.Text;
190             //获取聊天的内容
191             string content = tbContent.Text;
192
193             if (string.IsNullOrEmpty(sendUser))
194             {
195                 //提示
196                 tbHis.AppendText("请选择要发送的用户...\n");
197                 return;
198             }
199             else if (string.IsNullOrEmpty(content))
200             {
201                 //提示
202                 tbHis.AppendText("你不打算输入点什么咯...\n");
203                 return;
204             }
205
206             try
207             {
208                 //文字: ip设置最大值为 50 位
209                 //[命令(1)|对方的ip和自己的ip 50位)| 内容(文字) | ...]
210                 //这里把对方的ip放前面是为了在服务端好获取
211                 //把自己的ip和对方的ip转为byte
212
213                 //如果是独立电脑应该可以这样获取
214                 //Common.connSocket.LocalEndPoint
215                 //如果此处不发送自己的ip。那么也可以在服务端获取
216
217                 string allIp = string.Format("{0},{1}", cbList.Text, myIp.Text);
218
219
220                 byte[] sendIp = Encoding.UTF8.GetBytes(allIp);
221                 byte[] buffer = Encoding.UTF8.GetBytes(content);
222
223                 List<byte> list = sendIp.ToList();
224                 list.Insert(0, 1);//添加协议位
225
226                 //sendIp 不够50位
227                 if (sendIp.Length < 50)
228                 {
229                     for (int i = 0; i < 50 - sendIp.Length; i++)
230                     {
231                         list.Add(0);
232                     }
233                 }
234
235                 //把内容添加到末尾
236                 list.AddRange(buffer);
237
238                 //开始发送
239                 Common.connSocket.Send(list.ToArray());
240                 tbContent.Clear();
241
242                 //把发送的内容显示在上面
243                 tbHis.AppendText(string.Format("时间:{0}\n", DateTime.Now.ToString()));
244                 tbHis.AppendText(string.Format("我对{0}说:\n", cbList.Text));
245                 tbHis.AppendText(content + "\n");
246                 tbHis.AppendText("\n");
247                 //清空输入框
248                 tbContent.Clear();
249             }
250             catch (Exception ex)
251             {
252                 MessageBox.Show(ex.Message);
253             }
254
255         }
256         //保存服务器来的byte
257         byte[] buffer = new byte[1024 * 1024];
258         //保存ip
259         public static string ip;
260         void UserList_Load(object sender, EventArgs e)
261         {
262             connectServer();
263         }
264
265         void connectServer()
266         {
267             string ip = "192.168.1.2";
268             int _point = 8000;
269
270             try
271             {
272                 Common.connSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
273                 IPAddress address = IPAddress.Parse(ip);
274                 IPEndPoint point = new IPEndPoint(address, _point);
275                 Common.connSocket.Connect(point);
276
277                 //连接成功,获取服务器发来的消息
278                 Common.connSocket.BeginReceive(Common.buffer, 0, Common.buffer.Length, 0, new AsyncCallback(Receive), Common.connSocket);
279
280                 //Thread t = new Thread(get);
281                 //t.IsBackground = true;
282                 //t.Start(Common.connSocket);
283
284             }
285             catch (Exception ex)
286             {
287                 MessageBox.Show(ex.Message);
288             }
289         }
290
291         void get(object o)
292         {
293             while (true)
294             {
295                 Socket clientSocket = o as Socket;
296
297                 byte[] buffer = new byte[1024 * 1024];
298                 try
299                 {
300                     //获取实际的长度
301                     int num = clientSocket.Receive(buffer); //服务器关闭会报错
302                     if (num > 0)
303                     {
304                         //获取口令
305                         int command = buffer[0];
306                         //说明是获取好友
307                         if (command == 10)
308                         {
309                             //协议说明
310                             //第一次登陆获取在线(好友)人数
311                             //[命令(10)| ip(自己的ip和所有好友的ip)| ...]
312
313
314                             //其实用户本地ip也可以这样获取
315                             //string cy = clientSocket.LocalEndPoint;
316
317                             string allIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
318                             string[] temp = allIp.Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
319
320
321                             //跨线程操作UI
322                             this.Invoke(new Action(() =>
323                             {
324                                 myIp.Text = temp.Length > 0 ? temp[temp.Length - 1] : "";
325
326                                 //排除自己的ip
327                                 var other = from i in temp where !i.Contains(myIp.Text) select i;
328
329                                 cbList.Items.Clear();//清空
330                                 cbList.Items.AddRange(other.ToArray()); //绑定列表
331                             }));
332
333                         }
334                         else if (command == 11) //说明是有人下线
335                         {
336                             //协议说明
337                             // 有人下线
338                             //[命令(11)| ip(下线的ip)| ...]
339
340                             //获取下线的ip
341                             string outIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
342
343                             this.Invoke(new Action(() =>
344                             {
345                                 //删除下线的ip
346                                 cbList.Items.Remove(outIp);
347                             }));
348                         }
349                         else if (command == 12) //有人上线了
350                         {
351                             //协议说明
352                             // 有人上线
353                             //[命令(12)| ip(上线的ip)| ...]
354
355                             //获取上线的ip
356                             string onlineIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
357                             //添加上线的ip
358
359                             this.Invoke(new Action(() =>
360                             {
361                                 //添加上线的ip
362                                 cbList.Items.Add(onlineIp);
363                             }));
364
365                         }
366
367                         this.Invoke(new Action(() =>
368                         {
369                             if (cbList.Items.Count > 0)
370                                 cbList.SelectedIndex = 0;//默认选中第一个
371
372                         }));
373                     }
374                 }
375                 catch (Exception ex)
376                 {
377                     MessageBox.Show(ex.Message);
378                     clientSocket.Shutdown(SocketShutdown.Receive);
379                     clientSocket.Close();
380                     break;
381                 }
382
383
384                 //string ip = Encoding.UTF8.GetString(buffer, 0, num);
385                 ////MessageBox.Show(ip);
386
387                 ////第一个是自己的ip
388                 //string[] userIp = ip.Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
389
390                 //try
391                 //{
392
393                 //    //cbList.Items.Clear();
394                 //    //MessageBox.Show(cbList.Items.Count.ToString());
395
396                 //    //var m0 = from m in userIp
397                 //    //         where !m.Contains(userIp[0].ToString())
398                 //    //         select m;
399                 //    //cbList.Items.AddRange(userIp.ToArray());
400
401                 //    //this.Invoke(new Action(() =>
402                 //    //{
403                 //    //    cbList.Items.Clear();
404                 //    //    //MessageBox.Show(cbList.Items.Count.ToString());
405
406                 //    //    cbList.Items.AddRange(userIp.ToArray());
407
408
409                 //    //}));
410                 //}
411                 //catch (Exception ex)
412                 //{
413                 //    MessageBox.Show(ex.Message);
414                 //}
415
416
417                 //MessageBox.Show(string.Join(",",userIp));
418
419                 //cbList.DataSource = userIp.ToList();
420
421
422
423             }
424         }
425
426         /// <summary>
427         /// 接收来自服务器的消息
428         /// </summary>
429         /// <param name="result"></param>
430         void Receive(IAsyncResult result)
431         {
432             Socket clientSocket = result.AsyncState as Socket;
433
434             //byte[] b = new byte[1024 * 1024];
435             try
436             {
437                 //获取实际的长度
438                 int num = clientSocket.EndReceive(result);
439                 if (num > 0)
440                 {
441                     //MessageBox.Show(num.ToString());
442                     byte[] buffer = new byte[num];
443                     Array.Copy(Common.buffer, 0, buffer, 0, num); //复制数据到data
444                     //string ip = Encoding.UTF8.GetString(data);
445
446
447                     //以下是客户端=》服务器==》服务器
448                     /*
449                      * 当if else 超过3个
450                      *
451                      * 建议用switch case语句
452                      */
453
454                     //获取口令
455                     int command = buffer[0];
456                     //说明是获取好友
457                     if (command == 10)
458                     {
459                         //协议说明
460                         //第一次登陆获取在线(好友)人数
461                         //[命令(10)| ip(自己的ip和所有好友的ip)| ...]
462
463
464                         //其实用户本地ip也可以这样获取
465                         //string cy = clientSocket.LocalEndPoint;
466
467                         string allIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
468                         string[] temp = allIp.Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
469
470
471                         //跨线程操作UI
472                         this.Invoke(new Action(() =>
473                         {
474                             myIp.Text = temp.Length > 0 ? temp[temp.Length - 1] : "";
475
476                             //排除自己的ip
477                             var other = from i in temp where !i.Contains(myIp.Text) select i;
478
479                             cbList.Items.Clear();//清空
480                             cbList.Items.AddRange(other.ToArray()); //绑定列表
481
482                             if (cbList.Items.Count > 0)
483                                 cbList.SelectedIndex = 0;//默认选中第一个
484                         }));
485
486                     }
487                     else if (command == 11) //说明是有人下线
488                     {
489                         //协议说明
490                         // 有人下线
491                         //[命令(11)| ip(下线的ip)| ...]
492
493                         //获取下线的ip
494                         string outIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
495
496                         this.Invoke(new Action(() =>
497                         {
498                             //删除下线的ip
499                             cbList.Items.Remove(outIp);
500                             if (cbList.Items.Count > 0)
501                                 cbList.SelectedIndex = 0;//默认选中第一个
502                         }));
503                     }
504                     else if (command == 12) //有人上线了
505                     {
506                         //协议说明
507                         // 有人上线
508                         //[命令(12)| ip(上线的ip)| ...]
509
510                         //获取上线的ip
511                         string onlineIp = Encoding.UTF8.GetString(buffer, 1, num - 1);
512                         //添加上线的ip
513
514                         this.Invoke(new Action(() =>
515                         {
516                             //添加上线的ip
517                             cbList.Items.Add(onlineIp);
518                             if (cbList.Items.Count > 0)
519                                 cbList.SelectedIndex = 0;//默认选中第一个
520                         }));
521                     }
522
523                     //以下是客户端=》服务器==》客户端
524
525                     else if (command == 1)
526                     {
527                         //协议:
528                         //[命令(1)|对方的ip和自己的ip 50位)| 内容(文字) | ...]
529
530                         //获取ip段
531                         string[] sourceIp = Encoding.UTF8.GetString(buffer, 1, 50).Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
532
533                         //发消息来的ip
534                         string fromIp = sourceIp[1];
535
536
537
538                         //获取内容
539                         string content = Encoding.UTF8.GetString(buffer, 50 + 1, num - 50 - 1);
540
541                         this.Invoke(new Action(() =>
542                         {
543                             //列表框中选择当前的ip
544                             cbList.Text = fromIp.ToString();
545
546                             //显示内容
547                             tbHis.AppendText(string.Format("时间:{0}\n", DateTime.Now.ToString()));
548                             //tbHis.AppendText(string.Format("提示{0}对我说:", fromIp)); //我操。这样怎么就不行
549                             tbHis.AppendText(fromIp + "\n");
550                             tbHis.AppendText("对你说:\n");
551                             tbHis.AppendText(content + "\n");
552                             tbHis.AppendText("\n");
553                         }));
554                     }
555                     else if (command == 0) //发送的文件
556                     {
557                         /*协议: 这里50位不知道是否理想。
558                          * [命令(0)| ip(对方的ip和自己的ip 50位)| 内容(文件大小和文件全名 30位)|响应(文件内容) | ...]
559                          */
560
561                         //这里有冗余代码
562
563                         //获取ip段
564                         string[] sourceIp = Encoding.UTF8.GetString(buffer, 1, 50).Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
565
566                         //发消息来的ip
567                         string fromIp = sourceIp[1];
568
569                         this.Invoke(new Action(() =>
570                         {
571                             //列表框中选择当前的ip
572                             cbList.Text = fromIp.ToString();
573                         }));
574
575
576                         //获取内容
577                         string content = Encoding.UTF8.GetString(buffer, 50 + 1, 30);
578
579                         //获取响应
580                         //string pass = Encoding.UTF8.GetString(buffer, 50 + 30 + 1, num - 50 - 30 - 1);
581
582                         //显示
583                         //tbHis.AppendText(string.Format("{0}给你发了一个文件:{1}\n", fromIp, content));
584                         tbHis.AppendText(fromIp);
585                         tbHis.AppendText("给你发了一个文件");
586                         tbHis.AppendText(content + "\n");
587                         tbHis.AppendText("\n");
588
589
590                         //提示用户是否接收文件
591                         if (MessageBox.Show("是否接受文件\n" + content, "接收文件", MessageBoxButtons.YesNo) == DialogResult.Yes)
592                         {
593                             //开始保存
594                             SaveFileDialog sfd = new SaveFileDialog();
595                             sfd.FileName = content;
596
597                             //获取文件类型
598                             string ex = content.Split(‘.‘)[1];
599
600                             //保存文件类型
601                             sfd.Filter = "|*." + ex;
602                             if (sfd.ShowDialog(this) == DialogResult.OK)
603                             {
604                                 using (FileStream fs = new FileStream(sfd.FileName, FileMode.Create))
605                                 {
606                                     fs.Write(buffer, 50 + 30 + 1, num - 50 - 30 - 1);
607                                 }
608                             }
609
610                         }
611
612                     }
613                     else if (command == 2) //发送抖动
614                     {
615                         //如果窗口在任务栏。则显示
616                         this.Show();
617                         this.WindowState = FormWindowState.Normal;
618                         this.Activate();
619
620                         int n = -1;
621
622                         for (int i = 0; i < 10; i++)
623                         {
624                             n = -n;
625                             this.Location = new Point(this.Location.X + 10 * n, this.Location.Y + 10 * n);
626                             this.TopMost = true;//在所有窗口的顶部
627                             System.Threading.Thread.Sleep(50);
628                         }
629
630                         //抖动完成。结束顶层显示
631                         this.TopMost = false;
632                     }
633
634                     //连接成功,再一次获取服务器发来的消息
635                     clientSocket.BeginReceive(Common.buffer, 0, Common.buffer.Length, 0, new AsyncCallback(Receive), clientSocket);
636
637                 }
638             }
639             catch (Exception ex) //服务器端口
640             {
641                 MessageBox.Show(ex.Message);
642                 clientSocket.Shutdown(SocketShutdown.Receive);
643                 clientSocket.Close();
644             }
645         }
646     }
647 }

客户端的一个公共类 Common.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Net;
 6 using System.Net.Sockets;
 7
 8 namespace TopClient
 9 {
10     /****************************************
11      * 与服务器通信的类
12     ****************************************/
13     public class Common
14     {
15         /// <summary>
16         /// 与服务器通信的socket
17         /// </summary>
18         public static Socket connSocket;
19
20         /// <summary>
21         /// 保存服务器来的byte
22         /// </summary>
23         public static byte[] buffer = new byte[1024 * 1024];
24
25         /// <summary>
26         /// 保存当登陆成功后。从服务器获取的所有用ip
27         /// </summary>
28         public static string ip;
29
30         /// <summary>
31         /// time计时器
32         /// </summary>
33         public static System.Windows.Forms.Timer time;
34
35         /// <summary>
36         /// 当前是否连接到服务器
37         /// </summary>
38         public static bool isConnect = false;
39
40         /// <summary>
41         /// 保存文件的文件名和扩展名 xxx.png
42         /// </summary>
43         public static string SafeFileName;
44     }
45 }

时间: 2024-08-16 10:38:16

网络编程之Socket的TCP协议实现客户端与客户端之间的通信的相关文章

【转】JAVA网络编程之Socket用法

JAVA网络编程之Socket用法 分类: JAVA2012-08-24 15:56 710人阅读 评论(0) 收藏 举报 在客户/服务器通信模式中,客户端需要主动建立与服务器连接的Socket,服务器端收到客户端的连接请求,也会创建与客户端连接的Socket.Socket可以看做是通信连接两端的收发器,客户端和服务店都通过Socket来收发数据. 1.构造Socket public Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字 public Socket(Str

网络编程之socket

网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例化需要3个参数: 1.地址簇:socket.AF_INET 2. 流:socket.SOCK_STREAM 3.使用的协议: 默认为0 服务器套接字:以下简称socket_server 客户端套接字:以下简称socket_client 地址:address=('127.0.0.1',8000) so

网络编程之Socket &amp; ServerSocket

网络编程之Socket & ServerSocket Socket:网络套接字,网络插座,建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口:socket用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信. 1.客户端Socket类 此类实现客户端套接字 构造方法 构造方法 作用 Socket(String host, i

Java网络编程之Socket通信(二)

之前在前面已经介绍了Socket通信的一些基本原理,以及如何让客户端与服务器端建立通信,和实现通信的一些基本步骤(包括首先使得服务器端与客户端建立连接,建立连接之后,服务器端开始侦听客户端的请求,侦听到客户端的请求之后,通过输入输出流处理相关信息实现通信,最后通信完毕结束通信等一系列流程). 但是之前只是单个客户端与服务器进行通信,而我们实际应用中单个客户端的情况几乎不存在,都是多个客户端同时与服务器进行交互(这里同时交互就会出现并发性的问题,对于并发性的问题暂时还不是很懂,只知道有这个概念),

Python网络编程之socket

socket是网络连接端点.例如当你的Web浏览器请求ansheng.me的网站时,你的Web浏览器创建一个socket并命令它去连接ansheng.me的Web服务器主机,Web服务器也对过来的请求在一个socket上进行监听.两端使用各自的socket来发送和接收信息. 在使用的时候,每个socket都被绑定到一个特定的IP地址和端口.IP地址是一个由4个数组成的序列,这4个数均是范围0~255中的值:端口数值的取值范围是0~65535.端口数小于1024的都是为众所周知的网络服务所保留的:

Linux网络编程之socket相关结构体

Linux中的网络编程是通过 Socket (套接字)实现. Socket有三种类型: 流式套接字(SOCK_STREAM) 流式套接字可以提供可靠的.面向连接的通讯流,它使用TCP协议.TCP保证了数据传输的正确性和顺序性. 数据报套接字(SOCK_DGRAM) 数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错,它使用数据报协议UDP. 原始套接字(SOCK_RAM) 原始套接字允许使用IP协议,主要用于新的网络协议的测试等. Socket

python网络编程之socket编程

一 客户端/服务器架构 即C/S架构,包括 1.硬件C/S架构(打印机) 2.软件C/S架构(web服务) 美好的愿望: 最常用的软件服务器是 Web 服务器.一台机器里放一些网页或 Web 应用程序,然后启动 服务.这样的服务器的任务就是接受客户的请求,把网页发给客户(如用户计算机上的浏览器),然 后等待下一个客户请求.这些服务启动后的目标就是"永远运行下去".虽然它们不可能实现这样的 目标,但只要没有关机或硬件出错等外力干扰,它们就能运行非常长的一段时间. 生活中的C/S架构: 商

网络编程之socket(转)

“一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价 值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好 友所在的QQ进程通信?这些都得靠socket?那什么是socket?socket的类型有哪些?还有socket的基本函数,这些都是本文想介绍的. 本文的主要内容如下: 1.网络中进程之间如何通信? 2

4.2.2 网络编程之Socket

1基于TCP协议的Socket 服务器端首先声明一个ServerSocket对象并且指定端口号,然后调用Serversocket的accept()方法接收客户端的数据.Accept()方法在没有数据进行接收的处于堵塞状态.,一旦接收到数据,通过inputstream读取接收的数据. 客户端创建一个Socket对象,指定服务器端的ip地址和端口号(newSocket("172.168.10.108",8080);),通过inputstream读取数据,获取服务器发出的数据(socket.