用代码编一个自动用java编写聊天程序的程序

分享一个C#编写简单的聊天程序(详细介绍)-C#教程_软件编程-脚本宝典
页面导航: >
> 分享一个C#编写简单的聊天程序(详细介绍)
分享一个C#编写简单的聊天程序(详细介绍)
这是一篇基于Socket进行网络编程的入门文章,我对于网络编程的学习并不够深入,这篇文章是对于自己知识的一个巩固,同时希望能为初学的朋友提供一点参考。文章大体分为四个部分
这是一篇基于Socket进行网络编程的入门文章,我对于网络编程的学习并不够深入,这篇文章是对于自己知识的一个巩固,同时希望能为初学的朋友提供一点参考。文章大体分为四个部分:程序的分析与设计、C#网络编程基础(篇外篇)、聊天程序的实现模式、程序实现。
程序的分析与设计
1.明确程序功能
如果大家现在已经参加了工作,你的经理或者老板告诉你,“小王,我需要你开发一个聊天程序”。那么接下来该怎么做呢?你是不是在脑子里有个雏形,然后就直接打开VS2005开始设计窗体,编写代码了呢?在开始之前,我们首先需要进行软件的分析与设计。就拿本例来说,如果只有这么一句话“一个聊天程序”,恐怕现在大家对这个“聊天程序”的概念就很模糊,它可以是像QQ那样的非常复杂的一个程序,也可以是很简单的聊天程序;它可能只有在对方在线的时候才可以进行聊天,也可能进行留言;它可能每次将消息只能发往一个人,也可能允许发往多个人。它还可能有一些高级功能,比如向对方传送文件等。所以我们首先需要进行分析,而不是一上手就开始做,而分析的第一步,就是搞清楚程序的功能是什么,它能够做些什么。在这一步,我们的任务是了解程序需要做什么,而不是如何去做。
了解程序需要做什么,我们可以从两方面入手,接下来我们分别讨论。
1.1请求客户提供更详细信息
我们可以做的第一件事就是请求客户提供更加详细的信息。尽管你的经理或老板是你的上司,但在这个例子中,他就是你的客户(当然通常情况下,客户是公司外部委托公司开发软件的人或单位)。当遇到上面这种情况,我们只有少得可怜的一条信息“一个聊天程序”,首先可以做的,就是请求客户提供更加确切的信息。比如,你问经理“对这个程序的功能能不能提供一些更具体的信息?”。他可能会像这样回答:“哦,很简单,可以登录聊天程序,登录的时候能够通知其他在线用户,然后与在线的用户进行对话,如果不想对话了,就注销或者直接关闭,就这些吧。”
有了上面这段话,我们就又可以得出下面几个需求:
1.程序可以进行登录。
2.登录后可以通知其他在线用户。
3.可以与其他用户进行对话。
4.可以注销或者关闭。
1.2对于用户需求进行提问,并进行总结
经常会有这样的情况:可能客户给出的需求仍然不够细致,或者客户自己本身对于需求就很模糊,此时我们需要做的就是针对用户上面给出的信息进行提问。接下来我就看看如何对上面的需求进行提问,我们至少可以向经理提出以下问题:
NOTE:这里我穿插一个我在见到的一个印象比较深刻的例子:客户往往向你表达了强烈的意愿他多么多么想拥有一个属于自己的网站,但是,他却没有告诉你网站都有哪些内容、栏目,可以做什么。而作为开发者,我们显然关心的是后者。
1.登录时需要提供哪些内容?需不需要提供密码?
2.允许多少人同时在线聊天?
3.与在线用户聊天时,可以将一条消息发给一个用户,还是可以一次将消息发给多个用户?
4.聊天时发送的消息包括哪些内容?
5.注销和关闭有什么区别?
6.注销和关闭对对方需不需要给对方提示?
由于这是一个范例程序,而我在为大家讲述,所以我只能再充当一下客户的角色,来回答上面的问题:
1.登录时只需要提供用户名称就可以了,不需要输入密码。
2.允许两个人在线聊天。(这里我们只讲述这种简单情况,允许多人聊天需要使用多线程)
3.因为只有两个人,那么自然是只能发给一个用户了。
4.聊天发送的消息包括:用户名称、发送时间还有正文。
5.注销并不关闭程序,只是离开了对话,可以再次进行连接。关闭则是退出整个应用程序。
6.注销和关闭均需要给对方提示。
好了,有了上面这些信息我们基本上就掌握了程序需要完成的功能,那么接下来做什么?开始编码了么?上面的这些属于业务流程,除非你对它已经非常熟悉,或者程序非常的小,那么可以对它进行编码,但是实际中,我们最好再编写一些用例,这样会使程序的流程更加的清楚。
1.3编写用例
通常一个用例对应一个功能或者叫需求,它是程序的一个执行路径或者执行流程。编写用例的思路是:假设你已经有了这样一个聊天程序,那么你应该如何使用它?我们的使用步骤,就是一个用例。用例的特点就每次只针对程序的一个功能编写,最后根据用例编写代码,最终完成程序的开发。我们这里的需求只有简单的几个:登录,发送消息,接收消息,注销或关闭,上面的分析是对这几点功能的一个明确。接下来我们首先编写第一个用例:登录。
在开始之前,我们先明确一个概念:客户端,服务端。因为这个程序只是在两个人(机器)之间聊天,那么我们大致可以绘出这样一个图来:
我们期望用户A和用户B进行对话,那么我们就需要在它们之间建立起连接。尽管“用户A”和“用户B”的地位是对等的,但按照约定俗称的说法:我们将发起连接请求的一方称为客户端(或叫本地),另一端称为服务端(或叫远程)。所以我们的登录过程,就是“用户A”连接到“用户B”的过程,或者说客户端(本地)连接到服务端(远程)的过程。在分析这个程序的过程中,我们总是将其分为两部分,一部分为发起连接、发送消息的一方(本地),一方为接受连接、接收消息的一方(远程)。
登录和连接(本地)
1.打开应用程序,显示登录窗口
2.输入用户名
3.点击“登录”按钮,登录成功
3.“登录”失败
如果用户名为空,重新进入第2步。
4.显示主窗口,显示登录的用户名称
5.点击“连接”,连接至远程
6.连接成功
6.1提示用户,连接已经成功。
6.连接失败
6.1 提示用户,连接不成功
5.在用户界面变更控件状态
5.2连接为灰色,表示已经连接
5.3注销为亮色,表示可以注销
5.4发送为亮色,表示可以发消息
这里我们的用例名称为登录和连接,但是后面我们又打了一个括号,写着“本地”,它的意思是说,登录和连接是客户端,也就是发起连接的一方采取的动作。同样,我们需要写下当客户端连接至服务端时,服务端采取的动作。
登录和连接(远程)
1-4 同客户端
5.等待连接
6.如果有连接,自动在用户界面显示“远程主机连接成功”
接下来我们来看发送消息。在发送消息时,已经是登录了的,也就是“用户A”、“用户B”已经做好了连接,所以我们现在就可以只关注发送这一过程:
发送消息(本地)
1.输入消息
2.点击发送按钮
2.没有输入消息,重新回到第1步
3.在用户界面上显示发出的消息
3.服务端已经断开连接或者关闭
3.1在客户端用户界面上显示错误消息
然后我们看一下接收消息,此时我们只关心接收消息这一部分。
接收消息(远程)
1.侦听到客户端发来的消息,自动显示在用户界面上。
注意到这样一点:当远程主机向本地返回消息时,它的用例又变为了上面的用例“发送消息(本地)”。因为它们的角色已经互换了。
最后看一下注销,我们这里研究的是当我们在本地机器点击“注销”后,双方采取的动作:
注销(本地主动)
1.点击注销按钮,断开与远程的连接
2.在用户界面显示已经注销
3.更改控件状态
3.1注销为灰色,表示已经注销
3.2连接为亮色,表示可以连接
3.3发送为灰色,表示无法发送
与此对应,服务端应该作出反应:
注销(远程被动)
1.自动显示远程用户已经断开连接。
注意到一点:当远程主动注销时,它采取的动作为上面的“本地主动”,本地采取的动作则为这里的“远程被动”。
至此,应用程序的功能分析和用例编写就告一段落了,通过上面这些表格,之后再继续编写程序变得容易了许多。另外还需要记得,用例只能为你提供一个操作步骤的指导,在实现的过程中,因为技术等方面的原因,可能还会有少量的修改。如果修改量很大,可以重新修改用例;如果修改量不大,那么就可以直接编码。这是一个迭代的过程,也没有一定的标准,总之是以高效和合适为标准。
2.分析与设计
我们已经很清楚地知道了程序需要做些什么,尽管现在还不知道该如何去做。我们甚至可以编写出这个程序所需要的接口,以后编写代码的时候,我们只要去实现这些接口就可以了。这也符合面向接口编程的原则。另外我们注意到,尽管这是一个聊天程序,但是却可以明确地划分为两部分,一部分发送消息,一部分接收消息。另外注意上面标识为自动的语句,它们暗示这个操作需要通过事件的通知机制来完成。关于委托和事件,可以参考这两篇文章:
- 委托和事件的入门文章,同时捎带讲述了Observer设计模式和.NET的事件模型
- 委托和事件更深入的一些问题,包括异常、超时的处理,以及使用委托来异步调用方法。
2.1消息Message
首先我们可以定义消息,前面我们已经明确了消息包含三个部分:用户名、时间、内容,所以我们可以定义一个结构来表示这个消息:
public struct Message {
private readonly string userN
private rea
private readonly DateTime postD
public Message(string userName, string content) {
this.userName = userN
this.content =
this.postDate = DateTime.N
public Message(string content) : this("System", content) { }
public string UserName {
get { return userN }
public string Content {
public DateTime PostDate {
get { return postD }
public override string ToString() {
return String.Format("{0}[{1}]:\r\n{2}\r\n", userName, postDate, content);
本文链接:
最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具socket实现的一个根本点对点聊天程序 - 编程当前位置:& &&&socket实现的一个根本点对点聊天程序socket实现的一个根本点对点聊天程序&&网友分享于:&&浏览:32次socket实现的一个基本点对点聊天程序多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。
服务器监听是指服务端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
客户端请求是由客户端的套接字提出连接请求,要连接的目标是服务器端套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器套接字的地址和端口号,然后再向服务器端套接字提出连接请求。
连接确认是当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的信息发送给客户端,一旦客户端确认了此连接,连接即可建立。而服务器端继续处于监听状态,继续接收其他客户端的连接请求。
套接字(socket)是套接口描述字的简称。和文件句柄相似,SOCKET提供了一咱通讯机制,是WINDOWS的一种通
讯方式。应用程序创建了一个套接字后,就能够获得这种机制提供的网络服务功能。对于服务器来说,它提供
了监听网络的连接请求;对于客户机来说,它可以连接到一个给定的主计算机和特定的端口上。客户端和服务
器端可以通过套接字对象来发送和接收数据。套接字提供了分别基于连接的协议(TCP)等和无连接的协议
(UDP)等,以满足网络连接的可靠性、稳定性以及高速性的要求。
WINSOCK是网络编程接口,它构成了WINDOWS平台下网络编程的基础。
开放系统互连七层模型(OSI)
应用层——表示层——会话层——传输层——网络层——数据链路层——物理层
应用层:用户的应用程序与网络之间的接口
表示层:协商数据交换格式
会话层:允许用户使用简单易记的名称建立连接
传输层:提供终端到终端的可靠连接
网络层:使数据路由经过大型互联网络
数据链路层:决定访问网络介质的方式
物理层:将数据转换为可通过物理介质传送的位
TCP、UDP协议是位传输层的协议,而IP协议则是位于网络层的协议。
TCP是传输控制协议,它是一种面向连接的协议,向用户提供可靠的全双工的字节流。
TCP关心数据传输的准确性。
应用程序利用TCP进行通讯时,发送方和接收方之间会建立一个虚拟连接,通过这一连接,双方可以把数据当作
一个双向的字节流来进行交流。它就像打电话。我们从摘机拨号开始,到拨通后建立连接、进行通话,再到挂
机断开连接这一过程,正是抽象的面向连接的具体表现。首先,在开始通话前,拨号,双方响应,从而建立一
条虚拟的“链路”。只有在双方都处于活动状态,这条“链路”才会保持存在。其次,我们可以通过这条“链
路”进行双向的会话,并且在一般情况下,我们可以通过对方的回答来确定对方是否已经正确听到了我们所说
的话,这相当于面向连接协议为保证传输正确而进行的额外校验。
UDP是用户数据报协议,这是一种无连接的协议。UDP是一种不可靠的数据报协议,它不能保证每一个UDP数据报
可以到达目的地。但是,正是由于它的不可靠性,减少了数据确认的过程,所以UDP传输数据的效率比较高。
就像是邮信。我们只需封好信封,然后将其投到邮筒中即可,但是我们不能保证邮局在把信件发送出去和信件
在发送过程中没有受到伤害。
总体看来,面向连接的服务可以保证双方传递数据的正确性,但却要为此进行额外的校验,通信双方建立通信
信道也需要许多系统开销。而无连接的服务最大的优点就是速度快,因为它不需要去验证数据的完整性,也不
会数据是否已接收而操心。
IP是网际协议,自20世纪80年代以来它一直都是网际协议的主力协议,它使用32位地址,为TCP、UDP、ICMP等
协议提供传送的分组服务。
在WINDOWS网络编程中,套接字接口主要有三种类型:流式套接字、数据报套接字以及原始套接字。
流式套接字定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输。对于建立在这种流式套
接字类型上的套接字来说,数据可以是双向传输的字节流,无长度限制。
数据报套字接口定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠。
原始套接字允许对低层协议如IP或ICMP直接访问,主要用于网络协议的测试,例如WINDOWS带的PING程序,就是
通过ICMP协议来实现的。
客户/服务器模式
在现在的网络应用中,通信双方最常见的交互模式便是客户/服务器模式。在这种模式下,客户向服务器发出服
务请求,服务器收到请求后为客户提供相应的服务。
客户/服务器模式通常采用监听/连接的方式实现。服务器端应用程序在一个端口监听对服务的请求,也就是说
,服务进程一直处于休眠状态,直到一个客户对这个服务提出了连接请求,此时服务线程被“唤醒”并且为客
户提供服务,即对客户的请求作出适当的反应。
面向连接的协议套接字的调用
面向连接的服务器端首先调用SOCKET()建立一个套接字S,用来监听客户端的连接请求。接着,调用bind()将此
套接字与本机地址、端口绑定起来。然后,调用listen()告诉套字S,对进来的连接进行监听并确认连接请求,
于是S被置于被动的监听模式。一个正在进行监听的套接字将给每个请求发送一个确认信息,告诉发送者主机已
经收到连接请求。当时监听套接字S实际上并不接受连接请求,在客户端请求被接受后,调用
accept()将返回一个与S具有相同属性,但不能被用来进行监听,只用来进行数据收发的数据套接字NS,作为与
客户端套接字相对应的连接的另一个端点。对于该客户端套接字后续的所有操作,都应该通过NS来完成。监听
套接字S仍然用于接收其他客户的连接请求。
面向连接的服务器一般是迸发服务器。在WINDOWS平台上,我们往往在调用accept()返回NS后,会创建一个请求
/应答执行线程,将NS作为参数之一传递给该线程,由该线程来完成客户端与服务器端复杂的请求应答工作,而
主线程会再次调用accept(),以接收新的客户端连接请求。
面向连接的客户端也会调用socket()建立一个套接字C,但使用像TCP这样的面向连接的协议时,客户端不必关
心协议使用什么样的本机地址,所以不用调用bind()。客户端调用connect()向服务器端发出连接请求,在与服
务器建立连接之后,客户端和服务器端就存在了一条虚拟的“管道”,客户端套字C和服务器端套接字NS构成了
“管道”的两个端点。客户端和服务器端通过这个“管道”进行数据交换,多次调用send()/recv()来进行请求
/应答,最终完成服务后关闭用于传输的套接字C和NS,并断开连接,结束此次会话。
面向无连接协议的套接字的调用
采用无连接协议(UDP)时,服务器一般都是面向事务的。一个请求和一个应答就完成了客户程序与服务器程序
之间的相互作用。
无边的服务器使用socket()和bind()来建立和绑定套接字S。与面向连接的服务器端不同,我们不必调用
listen()和accept(),只需要调用recvFrom()在套接字S上等待接收数据就可以了。因为是无连接的,因此网络
上任何一台机器发送的套接字S的数据都可以收到。从这一点上你可以想象,它们是无序的。
无连接的服务器一般都是迭代服务器。它们接收到一个数据报后,马上进行相应处理,直到处理完成后,才开
始下一个数据报的接收、处理。所以采用无连接协议时,客户端和服务器端的交互往往是很简单的,一问一答
或只问不答的方式很常见。
无连接的服务器端只有在停止服务时,才会关闭套接字。
无连接的客户端则更简单,只需要调用socket()建立一个套接字C,就可以利用sendto()和recvfrom()与服务器
的数据进行交换。在完成会话后调用closeSocket()关闭套接字C。
服务器与客户端通过已连接套接字进行接收与发送消息!
#include &unistd.h&
#include &sys/types.h&
#include &sys/socket.h&
#include &netinet/in.h&
#include &arpa/inet.h&
#include &signal.h&
#include &stdlib.h&
#include &stdio.h&
#include &errno.h&
#include &string.h&
#define ERR_EXIT(m) \
&&&&&&& do \
&&&&&&& { \
&&&&&&&&&&&&&&& perror(m); \
&&&&&&&&&&&&&&& exit(EXIT_FAILURE); \
&&&&&&& } while(0)
void handler(int sig)
&& &printf(&recv a sig=%d\n&, sig);
&& &exit(EXIT_SUCCESS);
int main(void)
&& &if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) & 0)
&& &&& &ERR_EXIT(&socket&);
&& &struct sockaddr_
&& &memset(&servaddr, 0, sizeof(servaddr));
&& &servaddr.sin_family = AF_INET;
&& &servaddr.sin_port = htons(5188);
&& &servaddr.sin_addr.s_addr = inet_addr(&127.0.0.1&);
&& &if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) & 0)
&& &&& &ERR_EXIT(&connect&);
&& &pid = fork();
&& &if (pid == -1)
&& &&& &ERR_EXIT(&fork&);
&& &if (pid == 0)
&& &&& &char recvbuf[1024];
&& &&& &while (1)
&& &&& &&& &memset(recvbuf, 0, sizeof(recvbuf));
&& &&& &&& &int ret = read(sock, recvbuf, sizeof(recvbuf));
&& &&& &&& &if (ret == -1)
&& &&& &&& &&& &ERR_EXIT(&read&);
&& &&& &&& &else if (ret == 0)
&& &&& &&& &{
&& &&& &&& &&& &printf(&peer close\n&);
&& &&& &&& &&& &
&& &&& &&& &}
&& &&& &&& &
&& &&& &&& &fputs(recvbuf, stdout);
&& &&& &close(sock);
&& &&& &kill(getppid(), SIGUSR1);
&& &&& &signal(SIGUSR1, handler);
&& &&& &char sendbuf[1024] = {0};
&& &&& &while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
&& &&& &&& &write(sock, sendbuf, strlen(sendbuf));
&& &&& &&& &memset(sendbuf, 0, sizeof(sendbuf));
&& &&& &close(sock);
&& &return 0;
#include &unistd.h&
#include &sys/types.h&
#include &sys/socket.h&
#include &netinet/in.h&
#include &arpa/inet.h&
#include &signal.h&
#include &stdlib.h&
#include &stdio.h&
#include &errno.h&
#include &string.h&
#define ERR_EXIT(m) \
&&&&&&& do \
&&&&&&& { \
&&&&&&&&&&&&&&& perror(m); \
&&&&&&&&&&&&&&& exit(EXIT_FAILURE); \
&&&&&&& } while(0)
void handler(int sig)
&& &printf(&recv a sig=%d\n&, sig);
&& &exit(EXIT_SUCCESS);
int main(void)
&& &if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) & 0)
/*&& &if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) & 0)*/
&& &&& &ERR_EXIT(&socket&);
&& &struct sockaddr_
&& &memset(&servaddr, 0, sizeof(servaddr));
&& &servaddr.sin_family = AF_INET;
&& &servaddr.sin_port = htons(5188);
&& &servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
&& &/*servaddr.sin_addr.s_addr = inet_addr(&127.0.0.1&);*/
&& &/*inet_aton(&127.0.0.1&, &servaddr.sin_addr);*/
&& &int on = 1;
&& &if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) & 0)
&& &&& &ERR_EXIT(&setsockopt&);
&& &if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) & 0)
&& &&& &ERR_EXIT(&bind&);
&& &if (listen(listenfd, SOMAXCONN) & 0)
&& &&& &ERR_EXIT(&listen&);
&& &struct sockaddr_
&& &socklen_t peerlen = sizeof(peeraddr);
&& &if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) & 0)
&& &&& &ERR_EXIT(&accept&);
&& &printf(&ip=%s port=%d\n&, inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
&& &pid = fork();
&& &if (pid == -1)
&& &&& &ERR_EXIT(&fork&);
&& &if (pid == 0)
&& &&& &signal(SIGUSR1, handler);
&& &&& &char sendbuf[1024] = {0};
&& &&& &while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
&& &&& &&& &write(conn, sendbuf, strlen(sendbuf));
&& &&& &&& &memset(sendbuf, 0, sizeof(sendbuf));
&& &&& &printf(&child close\n&);
&& &&& &exit(EXIT_SUCCESS);
&& &&& &char recvbuf[1024];
&& &&& &while (1)
&& &&& &&& &memset(recvbuf, 0, sizeof(recvbuf));
&& &&& &&& &int ret = read(conn, recvbuf, sizeof(recvbuf));
&& &&& &&& &if (ret == -1)
&& &&& &&& &&& &ERR_EXIT(&read&);
&& &&& &&& &else if (ret == 0)
&& &&& &&& &{
&& &&& &&& &&& &printf(&peer close\n&);
&& &&& &&& &&& &
&& &&& &&& &}
&& &&& &&& &
&& &&& &&& &fputs(recvbuf, stdout);
&& &&& &printf(&parent close\n&);
&& &&& &kill(pid, SIGUSR1);
&& &&& &exit(EXIT_SUCCESS);
&& &return 0;
.PHONY:clean all
CFLAGS=-Wall -g
BIN=echosrv echocli echosrv2 p2psrv p2pcli
all:$(BIN)
&& &$(CC) $(CFLAGS) -c $& -o $@
&& &rm -f *.o $(BIN)
12345678910
12345678910
12345678910 上一篇:下一篇:文章评论相关解决方案 12345678910 Copyright & &&版权所有泻药,其u实\e我也不知道0000要怎么说?
已有帐号?
无法登录?
社交帐号登录
励志成为段子手的程序员。聊天程序代码_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
聊天程序代码
上传于||暂无简介
阅读已结束,如果下载本文需要使用1下载券
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩10页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢

我要回帖

更多关于 java聊天程序源代码 的文章

 

随机推荐