Windows 网络通信套接字技术

Windows 网络通信套接字技术一、TCP/IP介绍1、TCP/IP体系结构TCP/IP协议实际上就是在物理网上的一组完整的网络协议。其中TCP是提供传输 层服务,而IP则是提供网络层服务。TCP/IP协议包括如下协议,其结构如图所示。IP: 网间协议(Internet Protocol) 负责主机间数据的路由和网络上数据的存储。 同时为ICMP,TCP,UDP提供分组发送服务。用户进程通常不需要涉及这一层。ARP: …

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、TCP/IP介绍

1、TCP/IP体系结构

TCP/IP协议实际上就是在物理网上的一组完整的网络协议。其中TCP是提供传输 层服务,而IP则是提供网络层服务。TCP/IP协议包括如下协议,其结构如图所示。

在这里插入图片描述
IP: 网间协议(Internet Protocol) 负责主机间数据的路由和网络上数据的存储。 同时为ICMP,TCP,UDP提供分组发送服务。用户进程通常不需要涉及这一层。

ARP: 地址解析协议(Address Resolution Protocol),此协议将网络地址映射 到硬件地址。

RARP:反向地址解析协议(Reverse Address Resolution Protocol),此协议 将硬件地址映射到网络地址。

ICMP:网间报文控制协议(Internet Control Message Protocol),此协议处理 信关和主机的差错和传送控制。

TCP:传送控制协议(Transmission Control Protocol),这是一种提供给用户 进程的可靠的全双工字节流面向连接的协议。它要为用户进程提供虚电路服务, 并为数据可靠传输建立检查。
UDP:用户数据报协议(User Datagram Protocol),这是提供给用户进程的无 连接协议,用于传送数据而不执行正确性检查。
FTP:文件传输协议(File Transfer Protocol),允许用户以文件操作的方式
(文件的增、删、改、查、传送等)与另一主机相互通信。
SMTP:简单邮件传送协议(Simple Mail Transfer Protocol),SMTP协议为系 统之间传送电子邮件。

TELNET:终端协议(Telnet Terminal Procotol),允许用户以虚终端方式访问 远程主机。

HTTP:超文本传输协议(Hypertext Transfer Procotol)。

TFTP:简单文件传输协议(Trivial File Transfer Protocol)。

2、TCP/IP特点

TCP/IP协议的核心部分是传输层协议(TCP、UDP)、网络层协议(IP)和物理 接口层,这三层通常是在操作系统内核中实现,因此用户一般不涉及。编程时, 编程界面有两种形式:

第一种是由内核心直接提供的系统调用;

第二种是使用以库函数方式提供的各种函数。前者为核内实现,后者为核外实现。 用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。 TCP/IP协议核心与应用程序关系如图所示。
在这里插入图片描述

3、TCP/IP协议与WinSock网络编程接口的关系

WinSock并不是一种网络协议,它只是一个网络编程接口,也就是说,它不是协议,但是它可以访问很多种网络协议,可以把它当作一些协议的封装。现在的
WinSock已经基本上实现了与协议无关。可以使用WinSock来调用多种协议的功能。 那么,WinSock和TCP/IP协议到底是什么关系呢?实际上,WinSock就是TCP/IP
协议的一种封装,可通过调用WinSock的接口函数来调用TCP/IP的各种功能。

二、套接字原理

1、客户机/服务器模式

在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式
(Client/Server model)。该模式的建立基于以下两点:
第一,非对等作用;
第二,通信完全是异步的。

客户机/服务器模式在操作过程中采取的是主动请示方式:

服务器端:

首先服务器方要先启动,并根据请示提供相应服务,过程如下:

  • 打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收-客户请求;
  • 等待客户请求到达该端口;
  • 接收到重复服务请求,处理该请求并发送应答信号;
  • 返回第二步,等待另一客户请求;
  • 关闭服务器。

客户端:

  • 打开一通信通道,并连接到服务器所在主机的特定端口;
  • 向服务器发送服务请求报文,等待并接收应答;继续提出请求……;
  • 请求结束后关闭通信通道并终止。

2、基本套接字

为了更好说明套接字编程原理,这里介绍几个基本的套接字。

  1. 创建套接字——socket()
    功能:使用前创建一个新的套接字。
    格式:SOCKET PASCAL FAR socket(int af,int type,int procotol);
    参数:af:通信发生的区域
    type:要建立的套接字类型
    procotol:使用的特定协议
  2. 指定本地地址——bind()
    功能:将套接字地址与所创建的套接字号联系起来。
    格式:int PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);
    参数:s是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
    其它:没有错误,bind()返回0,否则SOCKET_ERROR。
    在这里插入图片描述
  3. 建立套接字连接——connect()和accept()
    功能:共同完成连接工作。
    格式:int PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);
    SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR *name,int FAR * addrlen);
    参数:同上
  4. 监听连接——listen()
    功能:用于面向连接服务器,表明它愿意接收连接。
    格式:int PASCAL FAR listen(SOCKET s, int backlog);
  5. 数据传输——send()与recv()
    功能:数据的发送与接收。
    格式:int PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);
    int PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);
    参数:buf指向存有传输数据的缓冲区的指针。
  6. 多路复用——select()
    功能:用来检测一个或多个套接字状态。
    格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds, fd_set FAR * exceptfds,const struct timeval FAR * timeout);
    参数:readfds指向要做读检测的指针
    writefds指向要做写检测的指针
    exceptfds指向要检测是否出错的指针
    timeout为最大等待时间
  7. 关闭套接字——closesocket()
    功能:关闭套接字s。
    格式:BOOL PASCAL FAR closesocket(SOCKET s);

3、典型过程图

  1. 面向连接(基于TCP)socket 编程
    为了实现服务器与客户机的通信,服务器和客户机都必须建立套接字。服务器 与客户机的工作原理可以用下面的过程来描述:
    在这里插入图片描述
    在这里插入图片描述
    客户机与服务器建立面向连接的(基于TCP)套接字进行通信,请求与响应过程 及应用程序流程可用如图表示。
    在这里插入图片描述

  2. 面向无连接(基于UDP)的socket 编程
    无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与 服务程序之间的相互作用。
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

三、Windows Socket 程序设计

1、Windows Socket网络程序设计核心

1.启动与终止
在所有Windows Sockets 函数中,只有启动函数WSAStartup()和终止函数 WSACleanup()是必须使用的。启动函数必须是第一个使用的函数,而且它允许 指定 Windows Sockets API 的版本,并获得 SOCKETS的特定的一些技术细节。 函数原型如下:

int PASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA  lpWSAData);

其中:wVersionRequested保证SOCKETS可正常运行的DLL版本,如果不支持, 则返回错误信息。

下面这段代码,说明如何进行WSAStartup()的调用。

WORD wVersionRequested;	// 定义版本信息变量 
WSADATA wsaData;		//定义数据信息变量
int err;				//定义错误号变量
wVersionRequested = MAKEWORD(1,1;	//给版本信息赋值
err = WSAStartup(wVersionRequested, &wsaData); //给错误信息赋值 if(err!=0)
{ 
   
return;			//通知用户找不到合适的版本
}
//确认 Windows Sockets DLL 支持 1.1 版本
//DLL 版本可以高于 1.1

//系统返回的版本号始终是最低要求的 1.1,即应用程序与DLL 中可支持的最低版 本号
if(LOBYTE(wsaData.wVersion)!= 1|| HIBYTE(wsaData.wVersion)!=1{ 
   
	WSACleanup(); //通知用户找不到合适的版本
 	return;
}

//Windows Sockets DLL 被进程接受,可以进入下一步操作

关闭函数使用时,任何打开并已连接的 SOCK_STREAM 套接字被复位,但那些 已由 closesocket()函数关闭的但仍有未发送数据的套接字不受影响,未发送的数 据仍将被发送。程序运行时可能会多次调用WSAStartuo()函数,但必须保证每次 调用时的 wVersionRequested 的值是相同的。

2.异步请求服务

Windows Sockets 除支持Berkeley Sockets中同步请求,还增加了一类异步请求 服务函数WSAAsyncGerXByY()。该函数是阻塞请求函数的异步版本。应用程序 调用它时,由 Windows Sockets DLL初始化这一操作并返回调用者,此函数返 回一个异步句柄,用来标识这个操作。当结果存储在调用者提供的缓冲区,并且 发送一个消息到应用程序相应窗口。

常用结构如下:

HANDLE taskHnd;
char hostname="rs6000";
taskHnd = WSAAsyncBetHostByName(hWnd,wMsg,hostname,buf,buflen);

需要注意,由于Windows 的内存对像可以设置为可移动和可丢弃,因此在操作 内存对象时,必须保证WIindows Sockets DLL对象是可用的。

3.异步数据传输
使用send()或sendto()函数来发送数据,使用recv()或recvfrom()来接收数据。 Windows Sockets不鼓励用户使用阻塞方式传输数据,因为那样可能会阻塞整个 Windows 环境。下面看一个异步数据传输实例:
假设套接字s 在连接建立后,已经使用了函数 WSAAsyncSelect()在其上注册了 网络事件FD_READ和FD_WRITE,并且wMsg值为UM_SOCK,那么就可以在 Windows 消息循环中增加如下的分支语句:

case UM_SOCK:
switch(lParam)
{ 
   
case FD_READ:
len = recv(wParam,lpBuffer,length,0;  break;
case FD_WRITE:  while(send(wParam,lpBuffer,len,0!=SOCKET_ERROR)  break;
}
break;

4.出错处理
Windows提供了一个函数来获取最近的错误码WSAGetLastError(),推荐的编写 方式如下:

len = send (s,lpBuffer,len,0;
of((len==SOCKET_ERROR)&&WSAGetLastError()==WSAWOULDBLOCK))
{ 
    
	...
}

2、WinSock编程基本流程

WinSock编程分为服务器端和客户端两部分,TCP服务器端的大体流程如下:

  1. 对于任何基于WinSock的编程首先必须要初始化WinSock DLL库,使用接口函数:
int WSAStarup( WORD wVersionRequested,LPWSADATA lpWsAData )

初始化WinSock 。参数wVersionRequested是要求使用的WinSock的版本。

  1. 然后必须创建一个套接字(Socket),使用函数:
SOCKET Socket(int af,int type,int protocol)。

来实现。套接字可以说是WinSock通讯的核心。WinSock通讯的所有数据传输, 都是通过套接字来完成的,套接字包含了两个信息,一个是IP地址,一个是Port 端口号,使用这两个信息,就可以确定网络中的任何一个通讯节点。

  1. 当调用了Socket()接口函数创建了一个套接字后,必须把套接字与你需要进行通讯的地址建立联 系,可以通过绑定函数来实现这种联系:
int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);  struct sockaddr_in
{ 
   
short sin_family ;  u_short sin_prot ;
struct in_addr sin_addr ;  char sin_sero[8] ;
}

这就包含了需要建立连接的本地的地址,包括地址族、IP和端口信息。 sin_family字段必须把它设为AF_INET,这是告诉WinSock使用的是IP地址族。 sin_prot就是要用来通讯的端口号。sin_addr就是要用来通讯的IP地址信息。
由于各种不同的计算机处理数据时的方法是不一样的,Intel X86处理器表示多字 节的编号时,把低字节放在前面,把高字节放在后面,而互联网标准却正好相反, 所以,必须把主机字节转换成网络字节的顺序。

WinSock API提供了几个函数:

把主机字节转化成网络字节的函数:
u_long htonl(u_long hostlong);
u_short htons(u_short hostshort);
把网络字节转化成主机字节的函数:
u_long ntohl(u_long netlong);
u_short ntohs(u_short netshort) ;



这样,设置IP地址和port端口时,就必须把主机字节转化成网络字节后,才能用 Bind()函数来绑定套接字和地址。当绑定完成之后,服务器端必须建立一个监听 的队列来接收客户端的连接请求。
int listen(SOCKET s,int backlog);
这个函数可以把套接字转成监听模式。

如果客户端有了连接请求,还必须使用:
int accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);
来接受客户端的请求。
现在基本上已经完成了一个服务器的建立,而客户端的建立的流程则是初始化WinSock,然后创建Socket套接字,再使用:
int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);
来连接服务端。



下面是一个最简单的创建服务器端和客户端的例子。

服务器端的创建:

WSADATA wsd;
SOCKET sListen;  
SOCKET sclient;  
UINT port = 800;  
int iAddrSize;
struct sockaddr_in local , client;  
WSAStartup(0x11 , &wsd);
sListen = Socket ( AF_INET , SOCK_STREAM , IPPOTO_IP);  local.sin_family = AF_INET;
local.sin_addr = htonl(INADDR_ANY );  
local.sin_port = htons( port );
bind( sListen , (struct sockaddr*&local , sizeof( local ));  listen(sListen , 5);
sClient = accept(sListen , (struct sockaddr*)&client ,&iAddrSize);

客户端的创建:

WSADATA wsd;  
SOCKET sClient;  
UINT port = 800;// 此端口根据服务器提供
char szIp[] = "127.0.0.1";  
int iAddrSize;
struct sockaddr_in server;
WSAStartup( 0x11 , &wsd);

sClient = Socket( AF_INET , SOCK_STREAM , IPPOTO_IP);  server.sin_family = AF_INET;
server.sin_addr = inet_addr( szIp );  
server.sin_port = htons( port );
connect(sClient , (struct sockaddr*)&server , sizeof( server));

当服务器端和客户端建立连接以后,无论是客户端,还是服务器端都可以使用:

int send(SOCKET s,const char FAR* buf,int len,int flags); 
int recv( SOCKET s,char FAR* buf,int len,int flags);

函数来接收和发送数据,因为,TCP连接是双向的。

当要关闭通讯连接的时候,任何一方都可以调用:

int shutdown(SOCKET s,int how);

来关闭套接字的指定功能,再调用:

int closeSocket(SOCKET s);

来关闭套接字句柄,这样一个通讯过程就算完成了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/170932.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • oracle dba和sysdba的区别

    oracle dba和sysdba的区别之前老是把dba和sysdba混为一体,今天看到论坛在讨论两者的区别,特记录如下:SYSDBA不是权限,当用户以SYSDBA身份登陆数据库时,登陆用户都会变成SYS。sysdba身份登陆可以打开,

    2022年7月3日
    23
  • bool函数使用「建议收藏」

    bool函数使用「建议收藏」bool函数的使用

    2022年6月13日
    28
  • vs 序列号密钥「建议收藏」

    vs 序列号密钥「建议收藏」2003序列号: D64GG-GXY6T-V6FTR-WCPBB-2YDYB T7KXG-78HXC-JYRF8-72VH2-6DM7M2005序列号: KGR3T-F2C26-RRTGT-D6DQT-QBBB32008序列号: XMQ2Y-4T3V6-XJ48Y-D3K2V-6C4WT WPX3J-BXC3W-BPYWP-PJ8CM-F7M8T2013序列号: BWG7X-J98B3-W34RT-33B3R-JVYW92015序列号:专业版:HMGNV-WCYXV-X7G9W-YCX6

    2022年5月24日
    63
  • 相机标定——张正友棋盘格标定法

    相机标定——张正友棋盘格标定法目录为什么需要相机标定?相机标定可以做什么?相机标定后可以得到什么?什么情况下需要借助相机标定的方法?相机标定的原理实现相机标定的方法 为什么需要相机标定? 一个是由于每个镜头的在生产和组装过程中的畸变程度各不相同,通过相机标定可以校正这种镜头畸变,生成矫正后的图像——矫正透镜畸变; 另一个是根据标定后的到的相机参数建立相机成像几何模型,由获得的图像重构…

    2022年5月8日
    315
  • springboot打包成jar jsp文件无法访问

    springboot打包成jar jsp文件无法访问出现这种情况的原因是静态资源没有打包到 jar 如何解决 ps 本编博客不是解决 thymeleaf 模板引擎的问题第一步在 pom xml 文件的 amp lt build amp gt amp lt build amp gt 标签下添加如下 amp lt resources amp gt amp lt resou

    2025年11月10日
    3
  • 源码分析ElasticJob分片机制(带分片机制流程图)

    源码分析ElasticJob分片机制(带分片机制流程图)本文将重点分析ElasticJob的分片机制:ElasticJob分片工作机制:1、ElasticJob在启动时,首先会启动是否需要重新分片的监听器。代码见:ListenerManager#startAllListeners{…;shardingListenerManager.start();…}。2、任务执行之前需要获取分片信息,如果需要重新分片,主服务器执行分片算法,其他从…

    2025年7月3日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号