网络编程 套接字.docx

上传人:b****5 文档编号:12372299 上传时间:2023-04-18 格式:DOCX 页数:14 大小:51.02KB
下载 相关 举报
网络编程 套接字.docx_第1页
第1页 / 共14页
网络编程 套接字.docx_第2页
第2页 / 共14页
网络编程 套接字.docx_第3页
第3页 / 共14页
网络编程 套接字.docx_第4页
第4页 / 共14页
网络编程 套接字.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

网络编程 套接字.docx

《网络编程 套接字.docx》由会员分享,可在线阅读,更多相关《网络编程 套接字.docx(14页珍藏版)》请在冰豆网上搜索。

网络编程 套接字.docx

网络编程套接字

TCP/IP体系结构与特点

TCP/IP协议实际上就是在物理网上的一组完整的网络协议。

其中TCP是提供传输层服务,而IP则是提供网络层服务。

TCP/IP包括以下协议:

IP:

网间协议(InternetProtocol)负责主机间数据的路由和网络上数据的存储。

同时为ICMP,TCP,UDP提供分组发送服务。

用户进程通常不需要涉及这一层。

  ARP:

地址解析协议(AddressResolutionProtocol)

   此协议将网络地址映射到硬件地址。

  RARP:

反向地址解析协议(ReverseAddressResolutionProtocol)

   此协议将硬件地址映射到网络地址

  ICMP:

网间报文控制协议(InternetControlMessageProtocol)

   此协议处理信关和主机的差错和传送控制。

  TCP:

传送控制协议(TransmissionControlProtocol)

   这是一种提供给用户进程的可靠的全双工字节流面向连接的协议。

它要为用户进程提供虚电路服务,并为数据可靠传输建立检查。

(注:

大多数网络用户程序使用TCP)

  UDP:

用户数据报协议(UserDatagramProtocol)

   这是提供给用户进程的无连接协议,用于传送数据而不执行正确性检查。

  FTP:

文件传输协议(FileTransferProtocol)

   允许用户以文件操作的方式(文件的增、删、改、查、传送等)与另一主机相互通信。

  SMTP:

简单邮件传送协议(SimpleMailTransferProtocol)

   SMTP协议为系统之间传送电子邮件。

  TELNET:

终端协议(TelnetTerminalProcotol)

   允许用户以虚终端方式访问远程主机

  HTTP:

超文本传输协议(HypertextTransferProcotol)  

  TFTP:

简单文件传输协议(TrivialFileTransferProtocol)

TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通常是在操作系统内核中实现。

因此用户一般不涉及。

编程时,编程界面有两种形式:

一、是由内核心直接提供的系统调用;二、使用以库函数方式提供的各种函数。

前者为核内实现,后者为核外实现。

用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。

套接字基本概念:

套接字是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。

可以将套接字看作不同主机间的进程进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。

套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。

套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。

各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。

套接字可以根据通信性质分类,这种性质对于用户是可见的。

应用程序一般仅在同一类的套接字间进行通信。

不过只要底层的通信协议允许,不同类型的套接字间也照样可以通信。

套接字有两种不同的类型:

流套接字和数据报套接字。

(1)流式套接字(SOCK_STREAM):

一种面向连接的Socket,针对于面向连接的TCP服务应用;

  

(2)数据报式套接字(SOCK_DGRAM):

一种无连接的Socket,对应于无连接的UDP服务应用。

  从用户的角度来看,SOCK_STREAM、SOCK_DGRAM这两类套接字似乎的确涵盖了TCP/IP应用的全部,因为基于TCP/IP的应用,从协议栈的层次上讲,在传输层的确只可能建立于TCP或UDP协议之上,而SOCK_STREAM、SOCK_DGRAM又分别对应于TCP和UDP,所以几乎所有的应用都可以用这两类套接字实现。

套接字工作原理:

要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户机端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。

根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:

服务器监听,客户端请求,连接确认。

所谓服务器监听,是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。

所谓客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。

为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。

而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

典型过程图

面向连接的套接字的系统调用时序图

无连接协议的套接字调用时序图

C#实现套接字编程的实例――聊天室程序

本程序是基于C/S(服务器/客户端)构架的,程序包含一个服务器端的应用程序和一个客户端的应用程序。

首先,在服务器上运行服务器端的应用程序,该程序一运行就开始服务器监听。

然后,在客户机上就可以打开客户端的应用程序。

程序打开后可以与服务器端应用程序进行连接,即进行客户端请求。

在连接确认后,客户端用户可以和其他的客户端用户进行聊天。

客户端人数没有限制,同时还支持“悄悄话”聊天模式,支持聊天记录。

所以这是一个学习套接字编程的相当不错的例子。

而且,程序中为了处理每个客户端的信息还用到了多线程机制。

在每个客户端与服务器端连接成功后,它们之间就建立一个线程。

这样运用了多线程之后,客户端之间就不会相互影响,即使其中一个出了错误也不会影响到另一个。

服务器端程序:

布置界面。

只需在界面上添加一个ListBox控件即可,该控件主要用于显示客户端的用户的一些信息的。

服务器端程序的代码编写:

对于服务器端,主要的作用是监听客户端的连接请求并确认其请求。

程序一开始便打开一个StartListening()线程。

开始定义如下变量:

privateSystem.Net.Sockets.TcpListenertheListener;

privateSystem.Net.Sockets.SocketclientSocket;

privateSystem.Threading.ThreadclientService;

StartListening()函数如下:

privatevoidStartListening()

{

 theListener=newSystem.Net.Sockets.TcpListener(listenPort);//listenPort为端口号

 theListener.Start();

 while(true)

 {

try

{

 System.Net.Sockets.Sockets=theListener.AcceptSocket();

 clientSocket=s;

 clientService=newSystem.Threading.Thread(newSystem.Threading.ThreadStart(ServiceClient));

 clientService.Start();

}

catch(Exceptione)

{

 Console.WriteLine(e.ToString());

}

 }

}

该线程是一直处于运行状态的。

当服务器端接收到一个来自客户端的连接请求后,它就打开一个ServiceClient()线程来服务客户端。

当一个连接被建立后,每个客户端就被赋予一个属于它自己的套接字。

同时,一个Client类的对象被建立。

该对象包含了客户端的一些相关信息,该信息被保存在一个数组列表中。

Client类如下:

usingSystem;

usingSystem.Net;

usingSystem.Net.Sockets;

usingSystem.IO;

usingSystem.Threading;

namespaceChatServer

{

publicclassClient

{

privateThreadclientThread;

privateEndPointtheEndPoint;

privatestringname;

privateSocketsock;

publicClient(string_name,EndPoint_endpoint,Thread_thread,Socket_sock)

{

clientThread=_thread;

theEndpoint=_endpoint;

name=_name;

sock=_sock;

}

publicoverridestringToString()

{

returnTheEndpoint.ToString()+":

"+name;

}

publicThreadCLThread

{

get{returnclientThread;}

set{clientThread=value;}

}

publicEndPointHost

{

get{returntheEndpoint;}

set{theEndpoint=value;}

}

publicstringTheName

{

get{returnname;}

set{name=value;}

}

publicSocketTheSock

{

get{returnsock;}

set{sock=value;}

}

}

}

程序的主体部分应是ServiceClient()函数。

该函数是一个独立的线程,其主要部分是一个while循环。

在循环体内,程序处理各种客户端命令。

服务器端接收来自客户端的以ASCII码给出的字符串,其中包含了一个"|"形式的分隔符。

字符串中"|"以前的部分就是具体的命令,包括CONN、CHAT、PRIV、GONE四种类型。

CONN命令建立一个新的客户端连接,将现有的用户列表发送给新用户并告知其他用户有一个新用户加入。

CHAT命令将新的信息发送给所有用户。

PRIV命令将悄悄话发送给某个用户。

GONE命令从用户列表中除去一个已离开的用户并告知其他的用户某某已经离开了。

同时,GONE命令可以设置布尔型的变量keepAlive为false从而结束与客户端连接的线程。

ServiceClient()函数如下:

privatevoidServiceClient()

{

System.Net.Sockets.SocketclientS=clientSocket;

boolkeepAlive=true;

while(keepAlive)

{

Byte[]buffer=newByte[1024];

clientS.Receive(buffer);

stringclientCommand=System.Text.Encoding.ASCII.GetString(buffer);

string[]tokens=clientCommand.Split(newChar[]{'|'});

Console.WriteLine(clientCommand);

if(tokens[0]=="CONN")

{

for(intn=0;n

{

Clientcl=(Client)clients[n];

SendToClient(cl,"JOIN|"+tokens[1]);

}

System.Net.EndPointep=clientS.RemoteEndPoint;

Clientc=newClient(tokens[1],ep,clientService,clientS);

clients.Add(c);

stringmessage="LIST|"+GetChatterList()+"\r\n";

SendToClient(c,message);

lbClients.Items.Add(c);

}

if(tokens[0]=="CHAT")

{

for(intn=0;n

{

Clientcl=(Client)clients[n];

SendToClient(cl,clientCommand);

}

}

if(tokens[0]=="PRIV")

{

stringdestClient=tokens[3];

for(intn=0;n

{

Clientcl=(Client)clients[n];

if(cl.Name.CompareTo(tokens[3])==0)

SendToClient(cl,clientCommand);

if(cl.Name.CompareTo(tokens[1])==0)

SendToClient(cl,clientCommand);

}

}

if(tokens[0]=="GONE")

{

intremove=0;

boolfound=false;

intc=clients.Count;

for(intn=0;n

{

Clientcl=(Client)clients[n];

SendToClient(cl,clientCommand);

if(cl.Name.CompareTo(tokens[1])==0)

{

remove=n;

found=true;

lbClients.Items.Remove(cl);

}

}

if(found)

clients.RemoveAt(remove);

clientS.Close();

keepAlive=false;

}

}

}

这样,服务器端程序就基本完成了。

客户端程序的代码编写:

布置界面。

往界面上添加一个ListBox控件(用于显示用户列表),一个RichTextBox控件(用于显示聊天消息以及系统消息),一个TextBox控件(用于发送消息),一个CheckBox控件(确定是否为悄悄话),一个StatusBar控件(name=StatusBar1)以及四个Button控件(分别为“连接”、“断开连接”、“开始记录”、“发送”)。

当客户端试图和服务器端进行连接时,一个连接必须建立而且得向服务器端进行注册。

ConnectionServer()函数运用一个TcpClient来和服务器端取得连接,同时创建一个NetworkStream来发送消息。

还有,端口号和服务器端的是保持一致的。

先声明如下变量:

privateSystem.Net.Sockets.TcpClientclientSocket;

privateSystem.Net.Sockets.NetworkStreamns;

privateSystem.IO.StreamReadersr;

ConnectionServer()函数如下:

privatevoidConnectionServer()

{

StatusBar1.Text="正在连接到服务器……";

try

{

clientSocket=newSystem.Net.Sockets.TcpClient(serverAddress,serverPort);

ns=clientSocket.GetStream();

sr=newSystem.IO.StreamReader(ns);

connected=true;

}

catch(Exception)

{

MessageBox.Show("不能连接到服务器!

","错误",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);

StatusBar1.Text="已断开连接";

}

}

在和服务器端连接成功后,程序就用RegisterWithServer()函数向服务器端发送一个CONN命令。

该命令先是发送该用户的名称,然后从服务器端获得其他所有用户的列表,所有用户列表是在ListBox控件中显示的。

RegisterWithServer()函数如下:

privatevoidRegisterWithServer()

{

try

{

stringcommand="CONN|"+ChatOut.Text;

Byte[]outBytes=System.Text.Encoding.ASCII.GetBytes(command.ToCharArray());

ns.Write(outBytes,0,outBytes.Length);

stringserverResponse=sr.ReadLine();

serverResponse.Trim();

string[]tokens=serverResponse.Split(newChar[]{'|'});

if(tokens[0]=="LIST")

{

StatusBar1.Text="已连接";

btnCloseConnection.Enabled=true;

}

for(intn=1;n

lbChatters.Items.Add(tokens[n].Trim(newchar[]{'\r','\n'}));

this.Text=clientName+":

已连接到服务器";

}

catch(Exception)

{

MessageBox.Show("注册时发生错误!

","错误",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);

}

}

在此之后,当然就是用户之间的聊天了,由ReceiveChat()函数来完成。

该函数是一个独立的线程,它处理所有用户获得的消息和用户发送的消息。

它主要处理了CHAT、PRIV、JOIN、GONE、QUIT等命令,处理的方法和服务器端的类似。

ReceiveChat()函数实现如下:

privatevoidReceiveChat()

{

boolkeepAlive=true;

while(keepAlive)

{

try

{

Byte[]buffer=newByte[2048];

ns.Read(buffer,0,buffer.Length);

stringchatter=System.Text.Encoding.ASCII.GetString(buffer);

string[]tokens=chatter.Split(newChar[]{'|'});

if(tokens[0]=="CHAT")

{

rtbChatIn.AppendText(tokens[1]);

if(logging)

logwriter.WriteLine(tokens[1]);

}

if(tokens[0]=="PRIV")

{

rtbChatIn.AppendText("Privatefrom");

rtbChatIn.AppendText(tokens[1].Trim());

rtbChatIn.AppendText(tokens[2]+"\r\n");

if(logging)

{

logwriter.Write("Privatefrom");

logwriter.Write(tokens[1].Trim());

logwriter.WriteLine(tokens[2]+"\r\n");

}

}

if(tokens[0]=="JOIN")

{

rtbChatIn.AppendText(tokens[1].Trim());

rtbChatIn.AppendText("hasjoinedtheChat\r\n");

if(logging)

{

logwriter.WriteLine(tokens[1]+"hasjoinedtheChat");

}

stringnewguy=tokens[1].Trim(newchar[]{'\r','\n'});

lbChatters.Items.Add(newguy);

}

if(tokens[0]=="GONE")

{

rtbChatIn.AppendText(tokens[1].Trim());

rtbChatIn.AppendText("haslefttheChat\r\n");

if(logging)

{

logwriter.WriteLine(tokens[1]+"haslefttheChat");

}

lbChatters.Items.Remove(tokens[1].Trim(newchar[]{'\r','\n'}));

}

if(tokens[0]=="QUIT")

{

ns.Close();

clientSocket.Close();

keepAlive=false;

StatusBar1.Text="服务器端已停止";

connected=false;

btnSend.Enabled=false;

btnCloseConnection.Enabled=false;

}

}

catch(Exception){}

}

}

通过以上的一些函数,客户端程序之间就可以进行自由地聊天了,各个用户之间还可以互相发送悄悄话。

所以程序已经实现了聊天室的基本功能了,不过最后各个用户还要正常地退出,那就要用到QuitChat()函数了。

QuitChat()函数的具体实现如下:

privatevoidQuitChat()

{

if(connected)

{

try

{

stringcommand="GONE|"+clientName;

Byte[]outBytes=System.Text.Encoding

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 化学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1