Socket网络编程(备忘)

一、概述

基于TCP协议的Socket编程体验之

——在线客户系统用户登录

1、Socket简介

2、Socket通信原理

3、Socket类以及ServerSocket类如何使用

4、InetAddress类如何使用

基于UDP协议的Socket编程体验之

——在线客户系统客户咨询

1、DatagramSocket类如何使用

2、DatagramPacket类如何使用

1、TCP:Transmission Control Protocol传输控制协议,TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specified)。在简化的计算机网络OSI模型中,它完成地四层传输层所指定的功能。

类似于拨打电话,使用该种方式进行网络通讯时,需要建立专门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则客户端会自动重发该数据。

2、UDP:User Datagram Protocol的简称,中文名是用户数据报协议,是OSI参考模型中的一种无连接的传输层协议,提供面向事物的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。

UDP类似于发送短信,使用这种方式进行网络通讯时,不需要建立专门的虚拟连接,传输也不是很可靠,如果发送失败则客户端无法获得。

3、总结:

这两种传输方式都是实际的网络编程中进行使用,重要的数据一般使用TCP方式进行数据传输,而大量的非核心数据则都通过UDP方式进行传递,在一些程序中甚至结合使用这两种方式进行数据的传递。由于TCP需要建立专门的虚拟连接以及确认传输是否正确,所以使用TCP方式的速度稍微慢一些,而且传输时产生的数据量要比UDP稍微大一些。

4、区别:

TCP

UDP

由于它是一个面向连接的协议,在Socket之间进行数据传输之前必然要建立连接,所以在TCP中多了一个连接建立的时间。

使用UDP时,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接

TCP没有这方面的限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大量的数据

使用UDP传输数据时时有大小限制的,每个被传输的数据报必须限定在64KB之内。

TCP在网络同心上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输

相比之下,UDP操作简单,而且需要较少的监护,因此通常用于局域网高可靠性的分散系统中的client/server应用程序

TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据

UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。

二、学习方法

1、了解Socket的概念、起源、分类等相关知识,只需要听懂并看懂课件中的内容即可。

2、Socket通信原理以及Socket类、ServerSocket类的使用是重点内容

了解DatagramSocket类、DatagramPacket类的使用,看懂并完成任务即可

三、Socket

1、简介:

套接字;应用程序通过“套接字”向网络发出请求或者应答网络请求;

最早是UNIX上的一套网络程序通讯的标准;已被广泛移植到其它平台;

Socket实质上提供了进程通信的端点,网络上的两个程序通过一个双向的通讯链路实现数据的交换,这个双向链路的一端称为一个Socket。


Socket封装了应用层和传输层的功能,不需要我们自己去实现。

实际上Socket就相当于我们现实生活的一个插座,我们根据我们需要的来选取合适的插座。

2、分类:

一共有三类:

(1)流式套接字(SOCK_STREAM):提供了一个面向连接,可靠的数据传输服务,数据无差错、五重复的发送,且按发送顺序接收,其实它对应使用的是TCP协议。

(2)数据报式套接字(SOCK_DGRAM):无连接服务,数据报以独立包形式被发送,不提供无差错保证,数据可能丢失或重复,并且接收顺序无序,其实它对应使用的是UDP协议。

(3)原始式套接字(SOCK_RAM):该接口允许对较低层次协议,如IP直接访问。可以接受本机网卡上的数据帧或数据包,对监听网络流量和分析很有用。

我们主要是学习流式套接字和数据报式套接字

3、Socket通信原理概述

就相当于打电话一样


四、基于TCP协议的Socket编程

相关的类是Socket类、ServerSocket类,都被封装在java.net包中

1、Socket类

(1)Socket类的构造方法

构造方法

说明

Socket(String host,int port)

向host主机的port端口发起连接请求

Socket(String host,int port,InetAddress localAddr,int localPort)

向host主机的port端口发起连接请求,发起请求的计算机为localAddr,端口为localPort

InetAddress类表示互联网协议地址,包含IP地址,InetAddress类是java对IP地址的封装。

(2)Socket类的常用方法

常用方法

说明

InetAddress getInetAddress()

返回与当前Socket对象关联的InetAddress对象

void shutdownInput()

此套接字的输入流置于“流的末尾”

void shutdownOutput()

禁用此套接字的输出流

InputStream getInputStream()

返回当前Socket对象关联的InputStream对象,它是服务器端向客户端发送回来的数据流。

OutputStream getOutputStream()

返回当前Socket对象关联的OutputStream对象,它是客户端向服务器端发送的数据流。

Void close()

关闭该Socket对象建立的连接


2、ServerSocket类

(1)ServerSocket类的构造方法

构造方法

说明

ServerSocket()

创建一个ServerSocket对象。

ServerSocket(int port)

创建一个ServerSocket对象,并绑定到绑定的端口。

(2)ServerSocket类的常用方法

常用方法

说明

Socket accept()

侦听并接收到此ServerSocket的连接,此方法在连接传入之前一直阻塞。

void close()

是服务器释放占用的资源,并断开所有客户端的连接。

InetAddress getInetAddress()

返回当前服务器绑定的IP地址信息。

3、实例

(1)用户登录——字符串方式

Server(服务器端)

1、创建一个服务器端的Socket(ServerSocket),指定绑定端口,并等待监听;

2、使用accept()方法来阻塞等待监听,获取新的连接;(实际上就是通过该方法获取Socket对象);

3、获取Socket的输入输出流,进行操作(读取并响应客户端请求);

4、关闭资源。

Client(客户端)

1、创建Socket对象,与服务器建立连接;

2、获取输入输出流,进行操作(给服务器端发送请求并获取服务器端的响应信息);

3、关闭资源

注意:方法shutdownOutput()和方法shutdownInput(),在客户端发送请求后,使用这两个方法,才能够完成整个的通信(即C与S都进行了请求相关的操作),如果没有这个两个方法中的一个,则整个通信就没完成。但是两个方法所得到的结果是不一样的。

(2)用户登录——对象方式

   实际过程跟字符串方式差不多,但是要注意:

① 就是操作对象的流与操作字符串的流是不一样的。操作字符串的读写流我们用的是BufferedReader/PriteWriter,操作对象的读写流我们用的是ObjectInputStream/ObjectOutputStream;

② 还有一点就是要注意我们操作的对象必须是实现了序列化的才行,不然会报错。

(3)用户登录——多客户端

创建一个线程类,放到Server端,就可以实现多个Client访问该Server了。


(4)服务器获取客户端的IP信息


4、总结(Socket编程步骤)

Server(服务器端)

1、建议一个服务器Socket(ServerSocket)绑定指定端口并开始监听;

2、使用accept()方法阻塞等待监听,获得新的连接;

3、建立输入和输出流;

4、在已有的协议上产生会话;

5、使用close()关闭资源。

Client(客户端)

1、建立客户端Socket连接,指定服务器的位置以及端口;

2、得到Socket的读写流;

3、利用流按照一定的协议对Socket进行读写操作;

4、使用close()关闭资源。

5、Socket通信原理详解


6、InetAddress类

如何在服务器端获取每个客户端的IP地址呢?

思路:

(1)InetAddress类:表示互联网协议地址,包含IP地址,是Java对IP地址的封装,它可以通过Socket的getAddress()方法获得。

(2)java.net.InetAddress类的常用方法:

常用方法

说明

getHostAddress()

返回IP地址字符串(以文本表现形式);

getHostName()

获得此IP地址的主机名。

五、基于UDP协议的Socket编程

1、基于UDP协议的Socket(数据报式套接字)

基于UDP协议;无连接;透出数据包快递高速;数据安全性不佳。

比如:网络游戏,视频会话,QQ聊天信息

2、类

DatagramSocket类:发送和接收数据包的套接字,不维护连接状态,不产生输入输出流;

DatagramPacket类:数据包,封装了数据、数据长度、目标地址和目标端口。

注意:客户端要向外发送数据,必须首先创建一个DatagramPacket的对象,再使用DatagramSocket对象发送。

3、DatagramPacket类:

构造方法

说明

DatagramPacket(byte[]buf,int length,InetAddress address,int port)

构造DatagramPacket对象,用来将长度为length的包发送到指定主机上的指定端口号。

DatagramPacket(byte[]buf,int length)

构造DatagramPacket,用来接收长度为length的数据包。

DatagramPacket(byte[]buf,int length,SocketAddress sa)

构造数据报包,用来将长度为length的包发送到指定主机上的指定端口号。

4、DatagramSocket类

构造方法

说明

DatagramSocket()

创建一个DatagramSocket对象,并将其与本地主机上任何可用的端口绑定。

DatagramSocket(int port)

创建一个DatagramSocket对象,并将其与本地主机上的指定端口绑定。

常用方法

说明

void send(DatagramPacket p)

发送指定的数据报

void receive(DatagramPacket p)

接收数据报,手打数据以后,存放在指定的DatagramPacket对象中

void close()

关闭当前DatagramSocket对象

5、例子——在线客服系统客户咨询

用DatagramSocket来传输数据,而传输的数据是以数据报包(DatagramPacket)的形式存在的,基本的编程过程为:

Server(服务器)

Client(客户端)

1、创建接收方(服务器端)的套接字,也就是DatagramSocket对象;

2、创建接收类型的数据报包(其中数据报包中的数据是以字节数组的形式存在的);

3、通过套接字(DatagramSocket)来接收数据报包(也就是客户端的请求信息);

4、响应客户端的请求,把数据(响应信息)包装在数据报包中;

5、发送响应给客户端(响应客户端的请求);

6、释放资源。

1、创建发送方(客户端)的套接字;

2、创建发送的请求(客户端的请求)数据的数据报包,包括请求的信息、服务器地址以及端口号;

3、发送请求给服务器;

4、接收服务器的响应信息的数据报包;

5、通过套接字接收服务器的响应;

6、释放资源。


一个简单的socket网络编程例子: 服务器代码: #include #include #include #include #pragma comment(lib,"ws2_32.lib") //这句话的意思是加载ws2_32.lib这个静态库 #define NETWORK_EVENT WM_USER+100 //如果你用mfc做开发,你可以点击菜单project-〉setting-〉link-〉object/library中添加这个静态库。 //如果你用c语言,你需要通过#pragma comment(命令来连接静态库 int main(int argc, char* argv[]){ HANDLE hThread = NULL; //判断是否输入了端口号 if(argc!=3){ printf("Usage: %sPortNumber\n",argv[1]); exit(-1); } //把端口号转化成整数 short port; if((port = atoi(argv[2]))==0){ printf("端口号有误!"); exit(-1); } WSADATA wsa; //初始化套接字DLL if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){ //高字节指定了次版本号,低字节指定了主版本号,两个字节加到一起,就是你想要的Winsock库的版本号了 printf("套接字初始化失败!"); exit(-1); } //创建套接字 SOCKET serverSocket; if((serverSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){ printf("创建套接字失败!"); exit(-1); } struct sockaddr_in serverAddress; memset(&serverAddress,0,sizeof(sockaddr_in)); serverAddress.sin_family=AF_INET; serverAddress.sin_addr.S_un.S_addr = htonl(INADDR_ANY); serverAddress.sin_port = htons(port); //绑定 if(bind(serverSocket,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR){ printf("套接字绑定到端口失败!端口: %d\n",port); exit(-1); } //进入侦听状态 if(listen(serverSocket,SOMAXCONN)==SOCKET_ERROR){ printf("侦听失败!"); exit(-1); } printf("Server %d is listening......\n",port); SOCKET clientSocket[5],maxSocket;//用来和客户端通信的套接字 struct sockaddr_in clientAddress;//用来和客户端通信的套接字地址 memset(&clientAddress,0,sizeof(clientAddress)); int addrlen = sizeof(clientAddress); fd_set fd_read; int i=0; int j; char buf[4096]; char buff[4096]="exit"; while(1) { FD_ZERO(&fd_read); maxSocket=serverSocket; FD_SET(serverSocket,&fd_read); //FD_SET(clientSocket[i-1],&fd_read); for(j=0;j<i;j++) { FD_SET(clientSocket[j],&fd_read); if(maxSocket"); //gets(buff); if(select(maxSocket+1,&fd_read,NULL,NULL,NULL)>0) { if(FD_ISSET(serverSocket,&fd_read)) { if(buff=="") { if((clientSocket[i++]=accept(serverSocket,(sockaddr*)&clientAddress,&addrlen))==INVALID_SOCKET) { printf("接受客户端连接失败!"); exit(-1); } else { for(j=0;j5) { printf("超过最大客户端数"); exit(-1); } } else { int bytes; for(int k=0;k<i;k++) { if(FD_ISSET(clientSocket[k],&fd_read)) { bytes=recv(clientSocket[k],buf,sizeof(buf),0); if(bytes==-1) { //listen(serverSocket,SOMAXCONN); for (int l=k;l<i;l++) clientSocket[l]=clientSocket[l+1]; i--; } /*if(bytes==0) { //printf("fdsdf"); listen(serverSocket,SOMAXCONN); for (int l=k;l0) { buf[bytes]='\0'; printf("Message from %s: %s\n",inet_ntoa(clientAddress.sin_addr),buf); if(send(clientSocket[k],buf,bytes,0)==SOCKET_ERROR) { printf("发送数据失败!"); exit(-1); } } } } } } } //清理套接字占用的资源 WSACleanup(); return 0; } 客户端代码: #include #include #include #pragma comment(lib,"ws2_32.lib") int main(int argc, char* argv[]){ //判断是否输入了IP地址和端口号 if(argc!=4){ printf("Usage: %s IPAddress PortNumber\n",argv[1]); exit(-1); } //把字符串的IP地址转化为u_long unsigned long ip; if((ip=inet_addr(argv[2]))==INADDR_NONE){ printf("不合法的IP地址:%s",argv[1]); exit(-1); } //把端口号转化成整数 short port; if((port = atoi(argv[3]))==0){ printf("端口号有误!"); exit(-1); } printf("Connecting to %s:%d......\n",inet_ntoa(*(in_addr*)&ip),port); WSADATA wsa; //初始化套接字DLL if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){ printf("套接字初始化失败!"); exit(-1); } //创建套接字 SOCKET sock,serverSocket; if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){ printf("创建套接字失败!"); exit(-1); } struct sockaddr_in serverAddress; memset(&serverAddress,0,sizeof(sockaddr_in)); serverAddress.sin_family=AF_INET; serverAddress.sin_addr.S_un.S_addr = ip; serverAddress.sin_port = htons(port); //建立和服务器的连接 if(connect(sock,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR) { printf("建立连接失败!"); exit(-1); } char buf[4096]; while(1){ printf(">"); //从控制台读取一行数据 gets(buf); if(send(sock,buf,strlen(buf),0)==SOCKET_ERROR){ printf("发送c数据失败!"); exit(-1); } int bytes; if((bytes=recv(sock,buf,sizeof(buf),0))==SOCKET_ERROR) { printf("接收c数据失败!\n"); exit(-1); } else { buf[bytes]='\0'; printf("Message from %s: %s\n",inet_ntoa(serverAddress.sin_addr),buf); } } //清理套接字占用的资源 WSACleanup(); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值