(转)API SOCKET基础(一) TCP建立连接并通信

 

写这篇日志,并不是要记录令人眼前一亮的算法,只是为了本人健忘的脑袋做一点准备。

要进行网络通信编程,就要用到socket(套接字),下面以TCP为例展示如何利用socket通信。

要 进行socket编程,首先要为工程链接导入库文件 ws2_32.lib ,然后添加头文件 #include <Winsock2.h> ,然后在App类的InitInstance()函数里面加载套接字库,加载套接字库的代码可查看MSDN里WSAStartup函数页面下端 example的代码,在加载套接字库的代码里面有一句wVersionRequested = MAKEWORD( 2, 2 );这句是指定采用2.2版本的套接字库,可根据需要修改为其他版本的套接字库。

 

1.TCP下的socket通信:

   TCP是面向链接的通信,通信的socket双方中必须有一个是服务器端socket,另一端是客户端socket。下面用代码来展示服务器端socket和客户端socket是如何建立链接并通信的。

服务器端连接过程:

1. 使用socket函数创建一个服务器端socket:

   SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);

   如上用socket函数创建了一个名字叫sockSrv的socket,socket函数的第二个参数指定了这个是什么类型的socket,如果是TCP类型的socket则为SOCK_STREAM,如果是UDP类型的socket则为SOCK_DGRAM。

 

2.创建一个地址结构体,为地址结构体指定地址,然后使用bind函数把socket和地址绑定:


SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);

bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

如上,创建了一个名字叫addrSrv的地址结构体,addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
是 指定IP地址为本机的IP地址,addrSrv.sin_port=htons(6000);是指定端口为6000端口,一定要使用1024以上的端口 号,1024以下的端口后是系统保留的。在这里有两个函数htonl和htons,这两个函数的作用以后再说。然后使用bind函数把sockSrvt和 addrSrv绑定起来。

 

3.设置socket为监听模式:

listen(sockSrv,5);

使 用listen函数把sockSrv设为监听模式,第二个参数是等待连接队列的最大长度。如果设置为SOMAXCONN,那么将这个套接字设置为最大的合 理值。这个值不是在一个端口上同时可以进行连接的数目,例如:如果把参数设置为2,当有3个连接请求同时到来时,前两个连接请求被放到等待请求连接队列 中,然后程序依次为这些请求服务,而第三个连接请求就被拒绝了。对多个连接请求的处理不是同时进行的,必须完成请求连接队列中一个连接请求的连接,才能开 始进行请求连接队列中下一个连接请求的连接。

 

4.等待客户端的连接请求到来:

SOCKADDR_IN addrClient;

int len=sizeof(SOCKADDR);

SOCKET sockConn=accept (sockSrv,(SOCKADDR*) &addrClient ,&len);

首 先创建一个地址结构体addrClient ,当有客户端请求连接sockSrv,accept 函数就会执行,建立连接并把客户端的IP地址和端口信息记录到地址结构体addrClient 里。必须注意到,accept函数的返回值是一个socket,在上面的代码中是SOCKET sockConn,这个名字叫sockConn(名字可由程序员随便取)的socket有什么用呢?其实当成功完成连接后,与客户端socket连接的是 sockConn,而不是sockSrv,以后与客户端socket进行数据传送的socket也是sockConn,而不是sockSrv,简单的说, 服务器端socket sockSrv只负责接收连接请求和进行连接操作,当连接操作完成后,与客户端socket连接的是accpet函数返回的socket,以后与客户端 socket进行数据传输的也是这个accpet函数返回的socket。

 

再来看TCP客户端是怎样发起连接请求的。

客户端编程也是用到socket,因此链接导入库文件,包含头文件,加载套接字库也必须先在客户端进行。

客户端请求连接过程:

1.使用socket函数创建一个客户端socket:

SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);

 

2.向服务器端socket发出连接请求:

SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);

connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

上 面创建了一个名字叫addrSrv的地址结构体,然后把服务器端socket的IP地址(假设服务器端IP地址是127.0.0.1)和端口来设置这个地 址结构体,然后通过connect函数向服务器端socket发出连接请求。在设置addrSrv地址结构体的IP地址时用了一个inet_addr函 数,这个函数的作用以后再说。

 

由此可见,客户端socket并不需要绑定IP地址和端口,为什么服务器端socket需要 绑定而客户端socket不用绑定呢?如果服务器端socket不绑定IP和端口,客户端socket又哪知道向哪个IP地址和端口发起连接请求呢?因此 服务器端socket是必须绑定IP端口的。而客户端socket不需要用bind函数绑定端口,系统会自动为socket绑定一个随机的端口,服务器只 要用accept函数返回的socket与客户端socket交换数据就行了,如果服务器需要查询客户端socket的IP和端口,可以查看accept 函数的第二个参数记录的客户端socket地址信息。

 

 

连接的操作在这里完成了,然后是进行数据的传输:

发送数据使用send函数:

int send(

SOCKET s,                    //要发送数据的socket,例如设置为服务器端的sockConn,则数据会发送到

                                          与sockConn相连接的客户端socket sockClient上去。相反若设置为sockClient,

                                          则数据发送到与sockClient相连接的服务器端socket sockConn上去。

const char FAR *buf,     //要发送数据buf的地址。

int len,                           //要发送数据buf的长度

int flags                         //一般设置为0即可。

);

 

 

接收数据使用recv函数:

int recv(

SOCKET s,               //要接收数据的socket,例如设置为服务器端的sockConn,则接收与sockConn相

                                     连接的客户端socket sockClient发送的数据。相反若设置为sockClient,则接收

                                     与sockClient相连接的服务器端socket sockConn发送的数据。

char FAR *buf,         //要发送数据buf的地址。

int len,                     //要接收数据buf的长度

int flags                    //一般设置为0即可。

);

 

socket使用完毕后调用closesocket()函数关闭一个socket以回收资源。在程序关闭之前,必须调用WSACleanup函数终止对套接字库的使用,注意必须在App类(应用程序类)的析构函数中调用WSACleanup函数。

 

接上一篇,建立连接后,服务器端的sockConn与客户端的sockClient就连接起来并且可以互相传输数据了,使用closesocket函数关闭一个socket,socket被关闭,连接也就断开了。下面是断开连接后的各种情况。

 

情况1:关闭服务器端sockConn--closesocket(sockConn)之后

关闭服务器端sockConn后,对sockConn使用recv函数接收数据,recv函数会马上返回SOCKET_ERROR,对sockConn使用send函数发送数据,send函数也会马上返回SOCKET_ERROR。

之后对客户端sockClient的情况分析就有点复杂了:

1.sockClient 使用send函数发送数据。第一次send函数能成功返回发送数据的大小,并不会返回SOCKET_ERROR 。虽然sockClient成功send了数据,但sockConn是无法接收到的。但sockClient使用send函数发送数据成功仅限于第一次 send,之后使用send函数返回的都是SOCKET_ERROR 。

2.sockClient使用recv函数接收数据。如果 sockClient使用recv函数之前没有使用过send函数,那么recv函数的返回值总是0(感觉好像很奇怪,recv函数返回0,难道还有成功 接收到0个字节的数据这种说法?不明白)。直到sockClient调用过send函数之后,recv函数的返回值总是SOCKET_ERROR。

 

 

情况2:关闭客户端sockClient--closesocket(sockClient)之后

这时结果就和情况1相反:

关闭客户端sockClient后,对sockClient使用recv函数接收数据,recv函数会马上返回SOCKET_ERROR,对sockClient使用send函数发送数据,send函数也会马上返回SOCKET_ERROR。

之后对服务器端sockConn的情况分析也一样:

1.sockConn 使用send函数发送数据。第一次send函数能成功返回发送数据的大小,并不会返回SOCKET_ERROR 。虽然sockConn成功send了数据,但sockClient是无法接收到的。但sockConn使用send函数发送数据成功仅限于第一次 send,之后使用send函数返回的都是SOCKET_ERROR 。

2.sockConn使用recv函数接收数据。如果sockConn使用recv函数之前没有使用过send函数,那么recv函数总是返回0。直到sockConn调用过send函数之后,recv函数的返回值总是SOCKET_ERROR。

 

知 道上面两个断开连接的情况后就要考虑客户端和服务器端如何协调关闭连接。TCP连接的双方Asocket和Bsocket,Asocket想要关闭连 接,Asocket的计算机除了closesocket(Asocket)之外,还要另外通知Bsocket的计算机连接已经断开了,Bsocket的计 算机收到通知后closesocket(Bsocket)来回收资源,避免Bsocket还在哪里傻傻的接收/发送数据,如果不想这样明显通知 Bsocket的计算机连接已断开,Bsocket也可以尝试自己判断,如果Bsocket多次调用send函数总是返回SOCKET_ERROR或者 Bsocket多次调用recv函数总是返回0或SOCKET_ERROR,那就要意识到连接很可能已经断开了。

 

如果数据的流向是单向的,例如说数据只从Asocket流向Bsocket(类似文件传输就是这样),那么Asocket只会调用send函数,而Bsocket只会调用recv函数,这时候如果其中一方要停止数据的传输,就会有两种情况出现:

1. 如果Asocket的计算机不想发送数据而closesocket(Asocket),由于Bsocket从来不调用send函数,因此Bsocket的 recv函数总是返回0,那么Bsocket的计算机就要意识到Asocket很可能已经关闭了,让Bsocket的计算机 closesocket(Bsocket)。

 

2.如果Bsocket的计算机不想接收数据而 closesocket(Bsocket),这时Asocket继续调用send函数发送数据,第一次send还是成功的,但从第二次send开始就总会 返回SOCKET_ERROR,但是Asocket的计算机无法判断send函数返回SOCKET_ERROR是由Bsocket关闭造成的还是 Asocket关闭造成的(因为Asocket关闭后Asocket调用send函数也是返回SOCKET_ERROR),因此Asocket的计算机无 法判断Asocket关闭了没有,简单的解决方法是如果Bsocket的计算机不想接收数据,先不要关闭Bsocket,而是发通知给Asocket的计 算机告诉它我不想收数据了,Asocket的计算机收到通知后关闭Asockt,这样情形就回到上面情况1去了,而且也知道Asocket调用send函 数返回SOCKET_ERROR肯定是由于Asockt关闭造成的而不是由Bsocket关闭造成的。

转载于:https://www.cnblogs.com/blog5258/archive/2013/04/11/socket.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5shift shell

echo offcopy %systemroot%\system32\taskmgr.exe %systemroot%\system32\sethc.execopy %systemroot%\system32\taskmgr.exe %systemroot%\system32\dllcache\sethc.exepause转载于:https://www.cnblogs.com/upshania/p/3817258.html

java 线程转储_获取Java线程转储的常用方法(推荐)

1. 线程转储简介线程转储(Thread Dump)就是JVM中所有线程状态信息的一次快照。线程转储一般使用文本格式, 可以将其保存到文本文件中, 然后人工查看和分析, 或者使用工具/API自动分析。Java中的线程模型, 直接使用了操作系统的线程调度模型, 只进行简单的封装。线程调用栈, 也称…

framebuffer驱动详解2——fb驱动框架分析(核心层)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、前言 framebuffer驱动框架包括以下两部分&#xff1a; 1、内核开发者实现的部分&#xff08;核心层&#xff09; rootubuntu:省略部分路径/x210_kernel/drivers/video# ls *.o built-in.o …

Oracle conn 协议适配器错误解决

Oracle conn 协议适配器错误 --解决方法C:\Documents and Settings\administrator>set oracle_sidmyoracleC:\Documents and Settings\administrator>sqlplus /nologSQL*Plus: Release 10.2.0.1.0 - Production on 星期三 12月 26 09:47:16 2012Copyright (c) 1982, 2005…

jquery ajax 文本丢失加号和连接号的问题

因为采用data:字符串这种形式&#xff0c;和&是jquery分隔参数的分隔符&#xff0c;所以会丢失&#xff0c;解决方法就是把text文本中的和&替换掉&#xff0c;用js里面的encodeURIComponent编码&#xff0c;为了省事&#xff0c;直接写出编码替换.. function FixJqText…

python给定一个整数n、判断n是否为素数_输入一个大于3的整数n,判断它是否为素数...

#include //让n被i除(i的值从2到n-1)int main(){int n,i;printf("please enter a integer number,n?");scanf("%d",&n);for(i2;i<n-1;i)if(n%i0) break;if(i",n);else printf("%d is a prime number.",n);return 0;}**************…

kernel移植——修改内核的启动logo

以下内容源于朱有鹏嵌入式课程的学习&#xff0c;如有侵权请告知删除。 参考博客 http://blog.csdn.net/ultraman_hs/article/details/54988168 一、自定义内核启动logo 步骤一&#xff1a;安装工具包 在命令行中输入以下内容 sudo apt-get install netpbm 步骤二&#xff1a;…

编译Ngnix遇到的问题,查看程序依赖的库文件

要点:ldd 可以读取每个可以运行的程序依赖的 so 文件。 编译的时候提示需要Openssl库. 查看本机,已经安装了openssl 查看编译报错文件,查找Openssl所依赖的库 more objs/autoconf.err 查看openssl所依赖的库文件 ldd /usr/bin/openssl ldd –u /usr/bin/openssl objdump -x ob…

[JavaWeb修行之路 Day1] 安装、配置、部署Tomcat

一、相关软件下载 Tomcat下载地址&#xff1a;http://tomcat.apache.org 。选择Tomcat 6或者Tomcat 7。Eclipse下载地址&#xff1a;http://www.eclipse.org/downloads/ 。选择Eclipse IDE for Java EE Developers进行下载。当然&#xff0c;也可以使用MyEclipse&#xff0c;收…

springboot创建parent_理解spring-boot-starter-parent

理解spring-boot-starter-parent通过spring initializr&#xff0c;我们可以快速构建一个springboot应用&#xff0c;如果你选择的是Maven来管理项目&#xff0c;在默认的pom文件中有这么一个section&#xff1a;org.springframework.bootspring-boot-starter-parent2.1.1.RELE…

应用层为何不能设置分辨率

以下内容源于朱有鹏《物联网大讲堂》课程的学习&#xff0c;如有侵权&#xff0c;请告知删除。 5、在应用程序中设置分辨率 &#xff08;1&#xff09;可视分辨率&#xff08;即实际分辨率&#xff09;、虚拟分辨率 &#xff08;2&#xff09;实验及结果 vinfo.xres 1024; …

CI框架--加载静态内容

首先&#xff0c;你需要创建一个可以处理静态内容请求的控制器类。控制器&#xff0c;是一个用来代理完成某项任务的PHP类&#xff0c;它充当基于MVC架构应用程序的“粘合剂”&#xff08;译者注&#xff1a;控制器用来粘合/协调不同模型和视图。随着教程的深入&#xff0c;你会…

java se ee me都是什么意思_Java SE、Java EE、Java ME三者的区别

Java SE、Java EE、Java ME三者的区别1. Java SE(Java Platform&#xff0c;Standard Edition)。Java SE 以前称为J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web服务开发的类&#xff0c;并为 Java Platfor…

Vagrant 快速入门

1. Vagrant功能: Vagrant uses Oracle’s VirtualBox to build configurable, lightweight, and portable virtual machines dynamically.. 【Vagrant 使用Oracle VM VirtualBox 动态创建和配置轻量级的&#xff0c;可重现的&#xff0c;便携的虚拟机环境。】 2. Vagrant下载: …

framebuffer驱动详解3——fb驱动分析(具体操作层)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 博文fb驱动框架分析&#xff08;核心层&#xff09;已经对内核驱动维护者编写的fb驱动框架进行讲解。 接下来将对具体的fb驱动文件进行分析。这些驱动文件是驱动工程师要完成的部分。 rootubu…

各种分页存储过程 (转)

在项目中&#xff0c;我们经常遇到或用到分页&#xff0c;那么在大数据量&#xff08;百万级以上&#xff09;下&#xff0c;哪种分页算法效率最优呢&#xff1f;我们不妨用事实说话。 测试环境 硬件&#xff1a;CPU 酷睿双核T5750 内存&#xff1a;2G 软件:Windows server 20…

java连接phpstudy_PHPStudy快速自动搞定所有配置文件

phpStudy 是一个 PHP 调试环境的程序集成包。该程序包集成最新的 ApachePHPMySQLphpMyAdminZendOptimizer, 一次性安装 , 无须配置即可使用 , 是非常方便、好用的 PHP 调试环 境 . 该程序不仅包括 PHP 调试环境 , 还包括了开发工具、开发手册等 . 总之学习 PHP 只需一个包 . 对…

ViewBag 找不到编译动态表达式所需的一种或多种类型,是否缺少引用?

症状&#xff1a; 类似上面的警告提示&#xff0c;运行程序不会有任何错误&#xff0c;但若干地方都提示警告&#xff0c;并且明明dll的引用都是正确的。 解决方案&#xff1a; 删除&#xff1a;C:\Users\{your computer name}\AppData\Local\Temp\Temporary ASP.NET Files 该目…

framebuffer驱动详解4——framebuffer驱动分析2(probe函数讲解)

以下内容源于朱有鹏《物联网大讲堂》课程的学习&#xff0c;如有侵权&#xff0c;请告知删除。 主要在填充fbdev这个结构体。 二、framebuffer驱动分析2 1、probe函数分析 &#xff08;1&#xff09;struct s3c_platform_fb 这个结构体是fb的platform_data结构体&#xff1…

Zend Framework学习之Zend_Mail

Zend_Mail组件提供了通用化的功能来创建和发送文本。Zend_Mail通过PHP内建的mail()函数或者直接通过SMTP连接来发送邮件。一个简单的邮件由收件人、主题、邮件内容以及发件人等内容组成。步骤如下1.创建对象2.设置邮件内容3.发送案例&#xff1a; <?php require_once "…