Socket网络编程(3)--两端通信
上篇博文:http://www.cnblogs.com/wolf-sun/p/3329558.html
介绍了客户端连接服务端,一对一,多对一的情况,下面实现服务器接收消息的功能。LZ这些弄的比较慢,也是边学习,边动手实现的。具体步骤在注释中写的比较清楚,不懂的可以留言,LZ会尽快回复。共同学习,共同进步。
接收消息时机
什么时候接收消息?当服务器开始监听,有客户端连接,并且连接成功,此时负责通信的Socket已经创建,此时就可以接收消息了,可以通过Socket的Receive()方法接收消息。
// 摘要:
// 从绑定的 System.Net.Sockets.Socket 套接字接收数据,将数据存入接收缓冲区。
//
// 参数:
// buffer:
// System.Byte 类型的数组,它是存储接收到的数据的位置。
//
// 返回结果:
// 接收到的字节数。
//
// 异常:
// System.ArgumentNullException:
// buffer 为 null。
//
// System.Net.Sockets.SocketException:
// 试图访问套接字时发生错误。 有关更多信息,请参见备注部分。
//
// System.ObjectDisposedException:
// System.Net.Sockets.Socket 已关闭。
//
// System.Security.SecurityException:
// 调用堆栈中的调用方没有所需的权限。
public int Receive(byte[] buffer);
上面代码介绍了Receive方法接收参数及返回值。
private void ListenConn(object o)
{
//将参数o 转化为监听的socket
Socket socketListener = o as Socket;
//写入循环 每一个连接就创建一个通信用的socket
while (true)
{
//当有客户端连接成功 创建通信用的socket
Socket connSocket = socketListener.Accept();
string ip = connSocket.RemoteEndPoint.ToString();
ShowMsg(ip + " " + DateTime.Now.ToString() + " 连接成功");
//创建一个新线程去接收消息
Thread th = new Thread(ReceiveMsg);
th.Start(connSocket); }
}
接收消息的代码:
//接收客户端的消息
private void ReceiveMsg(object o)
{
Socket connSocket = o as Socket; //通信用的socket连接成功 就可以接收消息了
byte[] buffer = new byte[ * * ];//5M缓存
while (true)
{
//count是当前接收的字节个数
int count = connSocket.Receive(buffer);
string ip = connSocket.RemoteEndPoint.ToString();
//判断接收到的字节个数 是0表示客户端关闭了
if (count > )
{ //将字节转换为字符串
string msg = Encoding.UTF8.GetString(buffer, , count);
ShowMsg(ip + " " + DateTime.Now.ToString() + "\r\n" + msg);
}
else
{
//socket没办法发送空消息 如果收到空消息 客户端关闭
ShowMsg(ip + ":" + "断开连接");
connSocket.Close();
break; } } }
测试:仍然用telnet命令来测试:telnet 127.0.0.1 50000
测试结果:多对一,一对一,发送消息正常,关闭客户端,服务端正常显示哪个客户端断开连接。
服务器端所有代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; namespace Wolfy.ChatServer
{
public partial class Server : Form
{
public Server()
{
InitializeComponent();
//不让其检查跨线程的操作
Control.CheckForIllegalCrossThreadCalls = false;
}
//存放endpoin和通信用的socket
Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
private void btnSend_Click(object sender, EventArgs e)
{
ServerSendMsg(this.txtInputMsg.Text);
}
/// <summary>
/// 服务器给客户端发送消息
/// </summary>
private void ServerSendMsg(string msg)
{
//服务器给客户端发消息
string userkey = comboBoxEndpoint.Text;
if (!string.IsNullOrEmpty(userkey))
{
ShowMsg(msg);
byte[] buffer = Encoding.UTF8.GetBytes(msg);
dic[userkey].Send(buffer);
msg = "";
}
else
{
MessageBox.Show("请选择客户端");
}
} private void btnStartService_Click(object sender, EventArgs e)
{
//服务器ip地址
IPAddress ip = IPAddress.Parse(txtIPAddress.Text);
//ip地址和端口
IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
//创建用于监听的socket
Socket socketListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定ip和端口
socketListener.Bind(endpoint);
//开始监听 限制连接数 最多可以连接10个
socketListener.Listen();
ShowMsg("开始监听......");
//创建线程 去监听连接
Thread th = new Thread(ListenConn);
//将线程变为后台线程
th.IsBackground = true;
th.Start(socketListener);
}
private void ListenConn(object o)
{
//将参数o 转化为监听的socket
Socket socketListener = o as Socket;
//写入循环 每一个连接就创建一个通信用的socket
while (true)
{
//当有客户端连接成功 创建通信用的socket
Socket connSocket = socketListener.Accept();
string ip = connSocket.RemoteEndPoint.ToString();
ShowMsg(ip + " " + DateTime.Now.ToString() + " 连接成功");
//连接成功后加入字典
dic.Add(ip, connSocket);
comboBoxEndpoint.Items.Add(ip);
//创建一个新线程去接收消息
Thread th = new Thread(ReceiveMsg);
th.Start(connSocket);
} }
//接收客户端的消息
private void ReceiveMsg(object o)
{
Socket connSocket = o as Socket; //通信用的socket连接成功 就可以接收消息了
byte[] buffer = new byte[ * * ];//5M缓存
while (true)
{
//count是当前接收的字节个数
int count = connSocket.Receive(buffer);
string ip = connSocket.RemoteEndPoint.ToString();
//判断接收到的字节个数 是0表示客户端关闭了
if (count > )
{
//将字节转换为字符串
string msg = Encoding.UTF8.GetString(buffer, , count);
ShowMsg(ip + " " + DateTime.Now.ToString() + "\r\n" + msg);
}
else
{
//socket没办法发送空消息 如果收到空消息 客户端关闭
ShowMsg(ip + ":" + "断开连接");
connSocket.Close();
break;
} } }
/// <summary>
/// 提示信息辅助方法
/// </summary>
/// <param name="msg"></param>
private void ShowMsg(string msg)
{
this.txtMsgView.AppendText(msg + "\r\n");
} private void txtInputMsg_KeyUp(object sender, KeyEventArgs e)
{
//如果用户按下了Enter键
if (e.KeyCode == Keys.Enter)
{
//则调用 服务器向客户端发送信息的方法
ServerSendMsg(this.txtInputMsg.Text);
}
}
}
}
客户端实现
客户端创建的socket即负责连接又负责通信,所以这里和服务端不同。客户端连接、接收消息和发送消息代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace Wolf.ChatClient
{
public partial class Client : Form
{
public Client()
{
InitializeComponent();
//不让检查跨线程操作
Control.CheckForIllegalCrossThreadCalls = false;
}
Socket socket;
private void btnStartService_Click(object sender, EventArgs e)
{
//连接服务器的ip和端口
IPAddress ip = IPAddress.Parse(txtIPAddress.Text);
IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
//客户端的socket即负责连接又负责通信
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接服务器
socket.Connect(endpoint);
ShowMsg("连接成功......");
//和服务器连接成功后就可以接收服务端的消息了
Thread th = new Thread(ReceiveMsg);
th.IsBackground = true;
th.Start(); }
private void ReceiveMsg()
{
byte[] buffer = new byte[ * * ];
while (true)
{
int count = socket.Receive(buffer);
string msg = Encoding.UTF8.GetString(buffer, , count);
ShowMsg(this.txtIPAddress.Text + ":" + this.txtPort.Text + " " + DateTime.Now.ToString() + "\r\n" + msg);
}
}
private void ShowMsg(string msg)
{
txtMsgView.AppendText(msg + "\r\n");
}
private void btnSend_Click(object sender, EventArgs e)
{
ClientSendMsg(this.txtInputMsg.Text);
} /// <summary>
/// 客户端向服务端发送消息
/// </summary>
/// <param name="msg"></param>
private void ClientSendMsg(string msg)
{
//向服务端发送消息
if (socket != null)
{
ShowMsg(msg);
byte[] buffer = Encoding.UTF8.GetBytes(msg);
socket.Send(buffer);
msg = "";
}
else
{
ShowMsg("<<<<请先连接服务器>>>");
}
} private void txtInputMsg_KeyUp(object sender, KeyEventArgs e)
{
//如果用户按下了Enter键
if (e.KeyCode == Keys.Enter)
{
//则调用 服务器向客户端发送信息的方法
ClientSendMsg(this.txtInputMsg.Text);
}
} private void Client_FormClosing(object sender, FormClosingEventArgs e)
{
//客户端关闭 关闭socket
socket.Shutdown(SocketShutdown.Both);
}
}
}
测试结果:
结语:
边学习,边动手,实现了两端通信,目前支持一对一,多对一通信功能,代码中针对关闭客户端的情况还有bug,有待进一步修改。
Socket网络编程(3)--两端通信的更多相关文章
- socket 网络编程高速入门(一)教你编写基于UDP/TCP的服务(client)通信
由于UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,由于这些函数的结构往往比較复杂,參数大 ...
- Python之路【第七篇】python基础 之socket网络编程
本篇文章大部分借鉴 http://www.cnblogs.com/nulige/p/6235531.html python socket 网络编程 一.服务端和客户端 BS架构 (腾讯通软件:ser ...
- Socket网络编程-基础篇
Socket网络编程 网络通讯三要素: IP地址[主机名] 网络中设备的标识 本地回环地址:127.0.0.1 主机名:localhost 端口号 用于标识进程的逻辑地址 有效端口:0~65535 其 ...
- 循序渐进Socket网络编程(多客户端、信息共享、文件传输)
循序渐进Socket网络编程(多客户端.信息共享.文件传输) 前言:在最近一个即将结束的项目中使用到了Socket编程,用于调用另一系统进行处理并返回数据.故把Socket的基础知识总结梳理一遍. 1 ...
- Py西游攻关之Socket网络编程
新闻 管理 Py西游攻关之Socket网络编程 知识预览 计算机网络 回到顶部 网络通信要素: A:IP地址 (1) 用来标识网络上一台独立的主机 (2) IP地址 = 网络地址 + 主机 ...
- Python面向对象进阶和socket网络编程-day08
写在前面 上课第八天,打卡: 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese: def __i ...
- Socket网络编程TCP、UDP演示样例
Socket网络编程: 1) OSI(了解): 国际标准化组织ISO(International Orgnization for Standardization)指定了网络通信的模型:开放系统互联(O ...
- Socket网络编程-TCP编程
Socket网络编程-TCP编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.socket介绍 1>.TCP/IP协议 2>.跨网络的主机间通讯 在建立通信连接的 ...
- Python面向对象进阶和socket网络编程
写在前面 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese: def __init__(self ...
- Linux Socket 网络编程
Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...
随机推荐
- [CareerCup] 4.8 Contain Tree 包含树
4.8 You have two very large binary trees: Tl, with millions of nodes, and T2, with hundreds of nodes ...
- Jenkins进阶系列之——01使用email-ext替换Jenkins的默认邮件通知
1 简述 众所周知,Jenkins默认提供了一个邮件通知,能在构建失败.构建不稳定等状态后发送邮件.但是它本身有很多局限性,比如它的邮件通知无法提供详细的邮件内容.无法定义发送邮件的格式.无法定义灵活 ...
- WIN7下USB多点触摸,一次发多个数据包的延迟问题,重要!
这个问题很常见, 花了差不多一个星期时间来解决.硬件相关的东西太多坑了,而且这些坑不像代码那样可见. 使用混合模式,每次最多报告2个点.如果是5点则需要上报三次. 问题就来了,atmel的CTP最 ...
- KMP算法的Next数组详解
转载请注明来源,并包含相关链接. 网上有很多讲解KMP算法的博客,我就不浪费时间再写一份了.直接推荐一个当初我入门时看的博客吧:http://www.cnblogs.com/yjiyjige/p/32 ...
- 关于#define预处理指令的一个问题
背景:由于经常需要在远程服务端和测试服务端进行切换,所以将接口的地址定义为了一个预处理变量,例如 //#define APIDOMAIN @"http://10.0.0.2" #d ...
- node 大牛的blog
node一些基本的核心包的使用 http://cnodejs.org/topic/548e53f157fd3ae46b2334fd node的基本的三种框架的比较 http://cnodejs.o ...
- js中的DOM操作(1)
一.操作子节点 childNodes 通过该方式可以获取父节点下的所有子节点,但是由于浏览器的差异,这写节点中可能包含文本节点. <!DOCTYPE html> <html> ...
- Bootstrap3.0学习第七轮(按钮)
详情请查看http://aehyok.com/Blog/Detail/13.html 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok 本文文章链接:ht ...
- 让js的forin循环禁止forin到某个属性的话要怎么做
//知识点1:for In循环是可以枚举到继承的属性的://知识点2:使用defineProperty让属性无法通过forIn枚举到://知识点3:用definedProperty重新定义一个属性药把 ...
- Java基础-jdk动态代理与cglib动态代理区别
JDK动态代理 此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑. 代理模式在实际使用时需要指定具体的目标对象 ...