为什么TCP服务端需要调用bind函数而客户端通常不需要呢
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
那一年, 某哥让我写个tcp服务端客户端程序, 我草草写完, 然后他检查,并问我, 为什么客户端不用bind呢? 然后, 我卡壳了, 好尴尬啊。 现在, 我们来一起彻底了解一下这个问题。
先看看bind函数是干啥的。bind函数就是绑定, 将一个socket绑定到一个地址上, 也可以这么说:bind函数对一个socket进行命名(注意:socket名称包括三要素: 协议, ip, port)。
我们来看看最常见的方式, tcp服务端用bind, 但是tcp的客户端不用bind.
服务端程序为:
#include <stdio.h>#include <winsock2.h> // winsock接口#pragma comment(lib, "ws2_32.lib") // winsock实现int main(){ WORD wVersionRequested; // 双字节,winsock库的版本 WSADATA wsaData; // winsock库版本的相关信息 wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257 // 加载winsock库并确定winsock版本,系统会把数据填入wsaData中 WSAStartup( wVersionRequested, &wsaData ); // AF_INET 表示采用TCP/IP协议族 // SOCK_STREAM 表示采用TCP协议 // 0是通常的默认情况 unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_family = AF_INET; // TCP/IP协议族 addrSrv.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // socket对应的IP地址 addrSrv.sin_port = htons(8888); // socket对应的端口 // 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程) bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); // 将socket设置为监听模式,5表示等待连接队列的最大长度 listen(sockSrv, 5); SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); while(1) { // sockSrv为监听状态下的socket // &addrClient是缓冲区地址,保存了客户端的IP和端口等信息 // len是包含地址信息的长度 // 如果客户端没有启动,那么程序一直停留在该函数处 unsigned int sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len); char sendBuf[100] = {0}; sprintf(sendBuf,"%s", inet_ntoa(addrClient.sin_addr)); // 将客户端的IP地址保存下来 send(sockConn, sendBuf, strlen(sendBuf) + 1, 0); // 发送数据到客户端,最后一个参数一般设置为0 char recvBuf[100] = {0}; recv(sockConn, recvBuf, 100 - 1, 0); // 接收客户端数据,最后一个参数一般设置为0 printf("%s\n", recvBuf); getchar(); closesocket(sockConn); } closesocket(sockSrv); WSACleanup(); return 0;}
启动服务端。
再看客户端程序:
#include <winsock2.h>#include <stdio.h>#pragma comment(lib, "ws2_32.lib")int main(){ WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(1, 1); WSAStartup( wVersionRequested, &wsaData ); SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); 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(8888); connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); char recvBuf[100] = {0}; recv(sockClient, recvBuf, 100, 0); printf("%s\n", recvBuf); send(sockClient, "hello world", strlen("hello world") + 1, 0); getchar(); closesocket(sockClient); WSACleanup(); return 0;}
启动它。
然后, 我们在cmd中查看连接情况, 结果如下:
C:\Documents and Settings\Administrator>netstat -nao | findstr 8888
TCP 0.0.0.0:8888 0.0.0.0:0 LISTENING 11256
TCP 127.0.0.1:2964 127.0.0.1:8888 ESTABLISHED 13688
TCP 127.0.0.1:8888 127.0.0.1:2964 ESTABLISHED 11256
可以看到, 客户端的端口号是2964. 实际上, 这个端口号是操作系统随机分配的, 在分配的时候, 操作系统会保证不与现有的端口冲突。 好, 关掉这两个进程。 我们再重启服务端, 然后再重启客户端, 建立新的tcp连接, 我们再在cmd中查一次, 结果为:
C:\Documents and Settings\Administrator>netstat -nao | findstr 8888
TCP 0.0.0.0:8888 0.0.0.0:0 LISTENING 11256
TCP 127.0.0.1:2964 127.0.0.1:8888 ESTABLISHED 13688
TCP 127.0.0.1:8888 127.0.0.1:2964 ESTABLISHED 11256
C:\Documents and Settings\Administrator>netstat -nao | findstr 8888
TCP 0.0.0.0:8888 0.0.0.0:0 LISTENING 13296
TCP 127.0.0.1:3156 127.0.0.1:8888 ESTABLISHED 13628
TCP 127.0.0.1:8888 127.0.0.1:3156 ESTABLISHED 13296
我们发现, 客户端的端口编程了3156, 和上次的 2964不一致, 这就印证了操作系统会随机分配客户端端口这个说法。
以上就是最经典的服务端bind(这个是必须的), 客户端不bind. 那么, 我们自然要问, 客户端可不可以bind呢? 我们来实践一下:
服务端测程序还是不变,依然为:
#include <stdio.h>#include <winsock2.h> // winsock接口#pragma comment(lib, "ws2_32.lib") // winsock实现int main(){ WORD wVersionRequested; // 双字节,winsock库的版本 WSADATA wsaData; // winsock库版本的相关信息 wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257 // 加载winsock库并确定winsock版本,系统会把数据填入wsaData中 WSAStartup( wVersionRequested, &wsaData ); // AF_INET 表示采用TCP/IP协议族 // SOCK_STREAM 表示采用TCP协议 // 0是通常的默认情况 unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_family = AF_INET; // TCP/IP协议族 addrSrv.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // socket对应的IP地址 addrSrv.sin_port = htons(8888); // socket对应的端口 // 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程) bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); // 将socket设置为监听模式,5表示等待连接队列的最大长度 listen(sockSrv, 5); SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); while(1) { // sockSrv为监听状态下的socket // &addrClient是缓冲区地址,保存了客户端的IP和端口等信息 // len是包含地址信息的长度 // 如果客户端没有启动,那么程序一直停留在该函数处 unsigned int sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len); char sendBuf[100] = {0}; sprintf(sendBuf,"%s", inet_ntoa(addrClient.sin_addr)); // 将客户端的IP地址保存下来 send(sockConn, sendBuf, strlen(sendBuf) + 1, 0); // 发送数据到客户端,最后一个参数一般设置为0 char recvBuf[100] = {0}; recv(sockConn, recvBuf, 100 - 1, 0); // 接收客户端数据,最后一个参数一般设置为0 printf("%s\n", recvBuf); getchar(); closesocket(sockConn); } closesocket(sockSrv); WSACleanup(); return 0;}
启动它。
客户端采用bind, 如下:
#include <winsock2.h>#include <stdio.h>#pragma comment(lib, "ws2_32.lib")int main(){ WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(1, 1); WSAStartup( wVersionRequested, &wsaData ); SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); // tcp客户端也要玩玩bind啦, 童鞋们!!! SOCKADDR_IN addrClient; addrClient.sin_family = AF_INET; addrClient.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 客户端地址 addrClient.sin_port = htons(7777); // 客户端为7777端口 bind(sockClient,(SOCKADDR*)&addrClient, sizeof(SOCKADDR)); // 客户端也来玩玩绑定 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(8888); connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); char recvBuf[100] = {0}; recv(sockClient, recvBuf, 100, 0); printf("%s\n", recvBuf); send(sockClient, "hello world", strlen("hello world") + 1, 0); getchar(); closesocket(sockClient); WSACleanup(); return 0;}
好, 启动它。
这样, tcp的还是建立了正常的连接, 我们在cmd中查看一下:
C:\Documents and Settings\Administrator>netstat -nao | findstr 8888
TCP 0.0.0.0:8888 0.0.0.0:0 LISTENING 11256
TCP 127.0.0.1:2964 127.0.0.1:8888 ESTABLISHED 13688
TCP 127.0.0.1:8888 127.0.0.1:2964 ESTABLISHED 11256
C:\Documents and Settings\Administrator>netstat -nao | findstr 8888
TCP 0.0.0.0:8888 0.0.0.0:0 LISTENING 13296
TCP 127.0.0.1:3156 127.0.0.1:8888 ESTABLISHED 13628
TCP 127.0.0.1:8888 127.0.0.1:3156 ESTABLISHED 13296
C:\Documents and Settings\Administrator>netstat -nao | findstr 8888
TCP 0.0.0.0:8888 0.0.0.0:0 LISTENING 12916
TCP 127.0.0.1:7777 127.0.0.1:8888 ESTABLISHED 13176
TCP 127.0.0.1:8888 127.0.0.1:7777 ESTABLISHED 12916
可以看到, 客户端的端口号为7777, 反复多次做这个实验, 发下端口号都是7777. 由此可见, 客户端自己指定端口是成功的, 客户端也是可以bind的。
现在, 我们可算是看清楚了, tcp服务端必须有bind, 客户端通常不用bind, 当然如果你够无聊, 那也可以用一下bind. 在这里, 我要说一下了: 客户端用bind的程序很容易出问题, 你想想啊, 操作系统指定的不会冲突的随机端口难道不比你自己指定的容易冲突的固定端口好?
在很多场景下, 我们要在一个pc上开启多个客户端进程, 如果指定固定端口, 必然会造成端口冲突, 影响通信!所以, 我们就不要费力不讨好了, 客户端就不要指定端口了, 让操作系统来搞。这样,实际上就是操作系统对客户端的socket进行了隐式的命名(名称中的端口是随机的)。
ok, 该休息了, 明天继续聊其他话题。
给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks |
'Isn't this fun?'
|
‘Isn’t this fun?’ |
Quotes |
"Isn't this fun?"
|
“Isn’t this fun?” |
Dashes |
-- is en-dash, --- is em-dash
|
– is en-dash, — is em-dash |
创建一个自定义列表
- Markdown
- Text-to-HTML conversion tool
- Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ(n)=(n−1)!∀n∈N\Gamma(n) = (n-1)!\quad\forall n\in\mathbb NΓ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ(z)=∫0∞tz−1e−tdt .\Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
ganttdateFormat YYYY-MM-DDtitle Adding GANTT diagram functionality to mermaidsection 现有任务已完成 :done, des1, 2014-01-06,2014-01-08进行中 :active, des2, 2014-01-09, 3d计划一 : des3, after des2, 5d计划二 : des4, after des3, 5d
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
mermaid语法说明 ↩︎
注脚的解释 ↩︎
为什么TCP服务端需要调用bind函数而客户端通常不需要呢相关推荐
- windows Socket编程之TCP服务端与客户端
在前面的文章中有一篇讲到了命名管道通信,它是创建一根管道来进行进程之间或网络之间通信的.但是它有些缺陷,比如说效率较低等.而从这篇文章开始将介绍socket编程.socket是通过TCP,UDP,IP ...
- Go语言实现TCP服务端和客户端
Go语言实现TCP服务端和客户端 Go语言实现TCP通信 TCP协议 TCP服务端 TCP客户端 本文转载自Go语言实现TCP通信 Go语言实现TCP通信 TCP协议 TCP/IP(Transmiss ...
- 2-3 建立简易TCP服务端、客户端【socket server/client】【socket、bind、listen、accept、send、closesocket】【conect、recv】
2-3 建立简易TCP服务端.客户端 文章目录 2-3 建立简易TCP服务端.客户端 0-前言 1-服务端简易功能 2-客户端简易功能 3-代码逻辑 4-服务端 4-1 建立socket 4-2 绑定 ...
- [TCP/IP]TCP服务端accept发生在三次握手的哪一个阶段
TCP服务端accept发生在三次握手之后 客户端 socket()==>connect()==>write()==>read() 服务端 socket()==>bind()= ...
- MFC:Socket编程—TCP服务端和多个客户端通信
前言 MFC是微软基础类库,于 C++ 对于 C语言来说,MFC对于window API ,MFC 就相当于C++,window API 相当于C.MFC 封装了 window API 使用起来更加的 ...
- Python基于socket实现的TCP服务端
''' 基于socket实现的TCP服务端 '''import socket # 建立socket对象 server_socket=socket.socket(socket.AF_INET,socke ...
- netty tcp服务端主动断开客户端_「Netty核心技术」6-ChannelPipeline源码
ChannelPipeline是Channelhandler的容器,它负责ChannelHandler的管理和事件拦截与调度. 土话: ChannelPipeline就是用来管理Channelhand ...
- 六步创建TCP服务端
//六步创建TCP服务端 #include<iostream> #include<Windows.h> using namespace std;int main() {//初始 ...
- Qt:Qt实现Winsock网络编程—TCP服务端和客户端通信(多线程)
Qt实现Winsock网络编程-TCP服务端和客户端通信(多线程) 前言 感觉Winsock网络编程的api其实和Linux下网络编程的api非常像,其实和其他编程语言的网络编程都差不太多.博主用Qt ...
最新文章
- java 子进程输出流_具有输入/输出流的Java进程
- 删除vss文件批处理
- <table/>设置列宽度无效的问题
- jtable隐藏全部_全部隐藏!
- 云计算实战系列十三(Linux计划任务)
- maven 项目 missing jdk.tools.jar
- 关于GDAL180中文路径不能打开的问题分析与解决
- Git(15)- Git 分支 - 分支开发工作流
- 黑色的php编辑器,五款常用的免费php编辑器推荐
- 计算机二级excel试题练习网盘,计算机二级练习试题excel
- SQLServer数据库可疑的解决方法
- C语言 修改JPEG图片属性
- 东大22春《马克思主义基本原理概论》在线平时作业1百分非答案
- 银行业务--负债业务
- poj-2115 C Looooops 扩展欧几里德算法求最小非负整数解
- c语言36块砖36人搬答案编程,在C语言中.36块砖,36人搬;男搬4,女搬3,两个小孩抬一块.要求一次搬完,问男女小孩各多少?设计程序....
- (十四)覆盖率类型、覆盖率组
- python 读写csv文件(创建,追加,覆盖)
- 离散数学图论期末复习
- 语音算法笔记(3)——从序列建模的角度理解ASR