最后更新:2022-06-12 17:23:01 手机定位技术交流文章
你对TCP/IP、UDP、插座编程不熟悉。随着网络技术的发展,这些词汇已经填满了我们的耳朵。
1.什么是TCP/IP、UDP?
2.插座在哪里?
插座是什么?
你会用它们吗?
TCP/IP,UDP是什么?
TCP/IP(Transmission Control Protocol/Internet Protocol)是用于宽带网络(WAN)的工业标准协议集。
UDP(User Data Protocol)是一个与TCP相关的协议,属于TCP/IP协议家族。
这是一幅图,显示了这些协议之间的关系。

图1
TCP/IP协议家族包括传输层、网络层和链路层,现在您知道TCP/IP和UDP之间的关系。
插座在哪里?
在图1中,我们不能看到索克特的影子,所以它在哪里?或者用图表,一瞥就说。

图2
索克在这里。
插座是什么?
接口是与TCP/IP协议家族的应用程序层通信中间软件抽象层在设计模式中,索克是一个隐藏索克接口背后复杂的TCP/IP协议家族的门户模式,对于用户来说,一个简单的接口是整体,允许索克根据指定的协议组织数据。
你会使用它们吗?
这位老人为我们做了很多事,网络间的通信也更加简单,但毕竟还有许多工作要做。我以前听说过索克的编程,我认为这是更深层次的编程知识,但只要你理解索克特编程的工作原理,神秘的面纱被揭开了.
一个生活中的场景。你要打电话给朋友,先拨号,一个朋友听到电话铃后,就拿起电话,此时,你和你的朋友们已经建立了联系,就可以讲话了。等交流结束,把电话关掉,结束谈话。生活中的场景解释了这项工作原则,也许TCP/IP协议的家族在生命中诞生,这也不一定。

图3
先从服务器端说起。服务器终止以初始化接口,然后绑到端子上,听着港口,调用接受块,等待客户端连接。此时,如果客户端启动一个接口,然后连接服务器,如果连接成功,此时,客户端与服务器之间的连接已经建立。客户发送数据请求,在服务器端接收请求和处理请求,然后向客户发送响应数据,客户端读取数据,最后关闭连接,一次交互结束。
============================================
我们知道交流信息的价值,如何在网络中的进程之间进行沟通,如果我们每天打开浏览器浏览网页,浏览器程序如何与网络服务器通信?当你用QQ聊天时,QQ进程如何与服务器或朋友的QQ进程进行通信?所有这些都放在插座上?什么是插座?接头的类型是什么?还有插座的基本功能,这些都是我想在本文中向您介绍的。本文的主要内容如下:
目录
1.如何在网络中的进程之间进行通信?
索克是什么?
索克的基本操作
3.索克()函数
3.2,bind()函数
3.听()和连接()函数
3.accept()函数
3.读()和写()等功能
3.Close()函数
3手的TCP在插座中建立连接细节
TCP在插座中的四手释放
6、一个实例
服务器端代码
客户端代码
本地进程间通信(IPC)有多种方式,但可以归纳为以下四个类别:
消息传输(管道、FIFO、消息队列)
同步(相互数量,条件变量,读写锁,文件和写记录锁,信号数量)
共享记忆(匿名和匿名)
远程过程调用(Solaris Gate和Sun RPC)
但这并不是本文的主题!我们将讨论如何在网络中的进程之间进行沟通?解决的第一个问题是如何以独特的方式确定一个过程,否则通信无从谈起!只有一个进程可以由进程PID lokally识别,但它在网络上并不有效。事实上,TCP/IP协议家族帮助我们解决这个问题,网络层的"ip地址"只能识别网络中的主机,传输层的“协议+端口”只能识别主机中的应用程序(进程)。使用一个三维群(IP地址,协议,能够识别网络的进程,网络中的进程通信可以使用这个符号与其他进程交互。
使用TCP/IP协议的应用程序通常使用应用程序编程接口:UNIX BSD的插座和UNIX System V的TLI(已取消);实现网络过程之间的通信。就目前而言,几乎所有应用程序都使用插座,现在又回到了互联网时代,进程通讯在网络中到处都有,这就是为什么我说一切都是插座。
上面我们已经知道,网络中的进程通过接口进行通信,什么是插座?索克源自Unix,而UNIX/Linux的基本理念之一就是,所有东西都是文件。都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解是,索克是这一模型的实现,索克是一个特殊的文件,一些插座函数是操作(读/写IO,打开,关闭),这些功能后来被引入。
字根插座的来源
1970年2月12日,在群网领域首次使用。 IETF RFC33 中发现的,作者是Stephen Carr、Steve Crocker和Vincent Cerf。根据美国计算机历史博物馆的记录,Croker写道:“所有命名空间的元素都可以称为插座接口。一个插座字符接口构成连接的末端,一个接口可以由两个接口类型的接口完全定义。计算机历史博物馆补充道:“这个定义大约比BSD插座接口早12年。”
由于接口是开写/读闭模式的实现,因此接口为这些操作提供了相应的函数接口。
插座函数与正常文件的开放操作相符.正常文件打开操作返回文件描述单词,Socket()用于创建一个索克描述符。它只识别一个插座。这个索克的描述与文件的描述相同,它在随后的操作中使用。把它作为参数,通过它做一些阅读和写作操作.
当创建一个接口时,还可以指定不同的参数来创建不同的接口描述符。 接口函数的三个参数分别是:
注:上述类型和协议不是任意结合的,例如SOCK_STREAM不能与IPPROTO_UDP结合。
当我们叫一个接口来创建一个接口时,返回的插座描述了它存在于地址家族中的字符。AF_XX)在空间中,但没有具体的地址。如果你想分配给它一个地址,你必须调用 bind()函数,否则,当调用连接()或听()时,系统会自动分配一个端口。
如上面所述, bind()函数将一个特定地址在地址家族中分配给插座。 例如,与AF_INET相对应的AF_INET6是将ipv4或ipv6地址和端口号码组合分配给插座。
该函数的三个参数分别是:
通常当服务器启动时,它会绑定到一个众所周知的地址(例如IP地址+端口号码)。用于提供服务,客户端可以通过它连接到服务器;客户端不需要指定,系统自动分配一个端口号和自己的IP地址组合.这就是为什么服务器通常在听话之前会呼叫 bind(),而且客户不会打电话,相反,当连接时,系统会随机生成一个。
网络序列和主机序列
主机序列通常被称为大端和小端模式:不同的CPU有不同的类型的序列,这些序列指存储在内存中的整数的顺序,称为主机序列。 大端和小端标准的定义如下:
(a)小节点是低字节丢弃存储器的低地址端和高字节丢弃存储器的高地址端。
(b)Big-Endian是高位丢弃存储器的低地址端和低位丢弃存储器的高地址端。
网络字符序列:4个字符的32位值在下面的序列中传输:第一个是0到7位,接下来的8到15位,然后16到23位,最後是24-31個位數。这种传输序列被称为大端节点序列。因为在网络上传输时,TCP/IP头部的所有二进制整数都必须按此顺序,因此,它也被称作网络节点序列。字节序,考虑到语义部分的顺序,是存储在内存中超过单个字型的数据的顺序,一个节点的数据没有序列问题。
所以: 当将地址绑定到一个接口时,请先将主机序列转换为网络序列,不要假设主机序列被使用,因为网络序列是Big-Endian。因为这个问题造成了一场流血!由于公司项目代码的问题,导致许多神秘的问题,所以请记住,不应该对主人的序列作出任何假设,确保将其转换为网络字符串序列,并将其分配到插座上.
如果服务器调用索克()或绑定(),听()到索克,如果客户端调用连接()发送连接请求,服务器端接收请求。
听函数的第一个参数是所要听的索克描述单词,第二个参数是相应的接口可以排列的最大连接数。索克由索克()函数创建的索克是默认的主动类型,听函数将接口转换为被动类型,等待客户连接请求。
连接函数的第一个参数是客户端的索克描述,第二个参数是服务器的索克地址,第三个参数是索克地址的长度。
在TCP服务器端调用索克(),bind(),listen()之后,指定的插座地址将被监视。TCP客户端在接口()和连接()调用后,想向TCP服务器发送连接请求。在TCP服务器监控请求后,将调用接受()函数接收请求,因此,连接是建立的。然后可以启动网络接入操作,这相当于一个正常文件的读写I/O操作。
接受函数的第一个参数是服务器的索克描述字符,第二个参数是指向结构的索卡德尔*,返回客户端的协议地址,第三个参数是协议地址的长度。如果成功,然后它的返回值是一个由内核自动生成的新描述词,代表与返回客户端的TCP连接.
注:接受的第一个参数是服务器的索克描述符,当服务器开始调用索克()函数时生成,它被称为监视接口描述单词;接受函数返回连接接口描述单词。服务器通常只创建一个监视接口的描述,它存在于服务器的整个生命周期中。内核为每个服务器进程接受的客户端连接创建一个接口描述,当服务器向客户完成服务时,相应的接口描述词被关闭。
所有需要的都是风,服务器现在与客户端有良好的连接。 您可以调用网络I/O来执行读写操作,即实现网络中不同进程之间的通信! 网络I/O操作有下列组别:
我建议使用 therecvmsg()/sendmsg()函数,两者都是最常见的I/O函数,并且实际上可以取代上面的所有其他函数。 它们的声明如下:
读函数负责从fd中读取内容。 成功读取时,读取返回读取字节的实际数目,如果返回值为0,则表示已读取的文件的末端,少于0表示发生错误。如果读取EINTR声明的错误是由中断引起的,如果ECONNREST显示了连接问题。
写函数将Buf的nbyte内容写入文件描述符fd中。 成功写入的字节数返回。失败时返回-1,然后设置Errno变量。在网络程序中,当我们写到一个插座文件描述符时,有两种可能性。1)写字的返回值大于0,表示部分或全部数据的写字。2)返回值低于0,此时出现了错误。我们将根据错误类型来处理。如果错误是EINTR,则在写时会发生中断错误。如果存在EPIPE的网络连接问题(另一方已关闭连接)。
我不打算谈论其他两个I/O函数,请参阅人文档或百度,谷歌,详细。
在服务器与客户端之间建立连接后,执行一些读写操作,当读写操作完成时,相应的接口描述被关闭,而不是在操作完成时叫fclose关闭打开文件。
当一个TCP插座缺失时,标记插座为关闭,然后立即返回调用过程。
注意:关闭操作只会使相应的索克描述字符引用-1号,并且只有当参考数为0时,TCP客户端才会向服务器发送终止请求。
我们知道tcp建立连接需要“三个握手”,即交换三个分组。
三个振动是完成的,但是这些三个振动发生在插座的多少功能? 见下面:

图1,在插座上发送的TCP三个手
从图中可以看出,当客户调用连接时,触发了连接请求,向服务器发送SYN J包,这是当连接进入阻塞状态时;服务器听取连接请求时,收到SYNJ包,调用接受函数接收发送SYNK到客户端的请求,ACK J+1,客户端接收服务器的SYN K。在ACKJ+1之后,然后连接返回,并确认SYN K;当服务器接收ACK K+1时,accept返回,至此三次握手完毕,连接建立。
摘要:客户端的连接返回三个握手中的第二个,而服务器的接受返回三个握手中的第三个。
本文介绍了接口中的TCP三手建立过程及有关的接口功能,介绍了接口内四个手无接口的建立过程,请参阅以下图:

图2,在插座上发送的TCP的四个握手
图示过程如下:
一个应用程序进程首先呼叫,以积极关闭连接,而TCP发送 FIN M;
在另一端接收FINM后,执行消极关闭来确认该FIN。 该FIN的接收也作为文件终止器传递到应用程序进程中,因为FIN的接收意味着应用程序进程不能再接收有关连接上的额外数据。
经过一段时间后,收到文件终止器调用的申请过程关闭了其接口,导致其TCP也发送了 FIN N;
接收此 FIN的源TCP确认它。
所以每个方向都有一个 FIN和一个 ACK。
首先,给出实现的实例

[cpp] view plain copy print ?
- #include "InitSock.h"
- #include
- #include
- using namespace std;
- CInitSock initSock; // 启动Winsock库
- int main()
- {
- // 创建套节字
- SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- //用于指定接口的地址格式,通常是AF_INET
- // 指定接口类型,如果SOCK_DGRAM,则使用不可靠的UDP传输
- //使用类型参数,指定要使用的协议类型(在指定接口类型之后可以设置为0,因为默认为UDP或TCP)
- if(sListen == INVALID_SOCKET)
- {
- printf("Failed socket() n");
- return 0;
- }
- //填充 sockaddr_in结构,一个结构
- sockaddr_in sin;
- sin.sin_family = AF_INET;
- Sin.sin_port = htons (4567); //1024 ~ 49151: 普通用户注册的港口号码
- sin.sin_addr.S_un.S_addr = INADDR_ANY;
- //将此节点集合绑定到本地地址
- if(::bind(sListen, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
- {
- printf("Failed bind() n");
- return 0;
- }
- // 进入监听模式
- //2指允许在听队列中保持的未处理的连接的最大数目
- if(::listen(sListen, 2) == SOCKET_ERROR)
- {
- printf("Failed listen() n");
- return 0;
- }
- //循环接受客户连接请求
- sockaddr_in remoteAddr;
- int nAddrLen = sizeof(remoteAddr);
- SOCKET sClient = 0;
- char szText[] = " TCP Server Demo! rn";
- while(sClient==0)
- {
- //接受新的连接
- //((SOCKADDR*)&remoteAddr)一个指向sockaddr_in结构的指针,用于获取对方地址
- sClient = ::accept(sListen, (SOCKADDR*)&remoteAddr, &nAddrLen);
- if(sClient == INVALID_SOCKET)
- {
- printf("Failed accept()");
- }
- printf("接受连接: %s rn", inet_ntoa(remoteAddr.sin_addr));
- continue ;
- }
- while(TRUE)
- {
- //向客户端发送数据
- gets(szText) ;
- ::send(sClient, szText, strlen(szText), 0);
- //从客户接收数据
- char buff[256] ;
- int nRecv = ::recv(sClient, buff, 256, 0);
- if(nRecv > 0)
- {
- buff[nRecv] = ' ';
- printf("接收数据: %sn",缓冲);
- }
- }
- //关闭与客户端的连接
- ::closesocket(sClient);
- //关闭听众节点
- ::closesocket(sListen);
- return 0;
- }
[cpp] view plain copy print ?
- #include "InitSock.h"
- #include
- #include
- using namespace std;
- CInitSock initSock; // 启动Winsock库
- int main()
- {
- // 创建套节字
- SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if(s == INVALID_SOCKET)
- {
- printf(" Failed socket() n");
- return 0;
- }
- //也可以在这里调用绑定函数来绑定本地地址
- //否则系统将自动安排
- //完成远程地址信息
- sockaddr_in servAddr;
- servAddr.sin_family = AF_INET;
- servAddr.sin_port = htons(4567);
- //请注意,这里您必须填写服务器程序(TCPServer程序)所在的机器的IP地址
- //如果您的电脑没有连接,您可以直接使用
- servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
- if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
- {
- printf(" Failed connect() n");
- return 0;
- }
- char buff[256];
- char szText[256] ;
- while(TRUE)
- {
- //从服务器接收数据
- int nRecv = ::recv(s, buff, 256, 0);
- if(nRecv > 0)
- {
- buff[nRecv] = ' ';
- printf("接收数据: %sn",缓冲);
- }
- //向服务器发送数据
- gets(szText) ;
- szText[255] = ' ';
- ::send(s, szText, strlen(szText), 0) ;
- }
- // 关闭套节字
- ::closesocket(s);
- return 0;
- }
The Init Sock.h
[cpp] view plain copy print ?
- #include
- #include
- #include
- #include
- #pragma comment(lib, "WS2_32") // Link to WS2_32.lib
- class CInitSock
- {
- public:
- CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
- {
- // WS2_32.dll启动
- WSADATA wsaData;
- WORD sockVersion = MAKEWORD(minorVer, majorVer);
- if(::WSAStartup(sockVersion, &wsaData) != 0)
- {
- exit(0);
- }
- }
- ~CInitSock()
- {
- ::WSACleanup();
- }
- };
本文由 在线网速测试 整理编辑,转载请注明出处。