TCP/IP基础知识复习
/*
2018-11-14 09:06:39
基本的准备工作,需要注意的点
在windows环境下
*/
头文件:winsock2.h
链接库:ws2_32.lib
预处理器 加上下面这个
_WINSOCK_DEPRECATED_NO_WARNINGS
server: 基本结构
WSAStartup 由进程启动使用Winsock DLL
Step1 socket 创建套接字
Step2 bind 绑定端口和地址
Step3 listen 监听客户调
Step4 accept 等待客户请求到来 并且返回客户端的相关信息 成功时 返回套接字句柄
根据得到的socket和地址信息 可以进行给客户端传递信息
进行send 或者 recv 操作
WSACleanup() 对Winsock dll 资源的释放
*************************
Client操作
WSAStartup 由进程启动使用Winsock DLL
Step1 socket 创建套接字
Step2 connect 向服务器发出连接请求
然后就可以使用send 和 recv进行相应的 操作
WSACleanup() 对Winsock dll 资源的释放
//现在最高的版本是2.2 向下兼容
/*
2018-11-14 10:02:47
ch02 套接字类型和协议设置
*/
协议:计算机间对话必备通信规则
说通俗点:协议就是为了完成数据交换而定好的约定
函数详细解释
例子:SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
函数原型:
SOCKET socket(
int af, //套接字中使用的协议族 信息
int type, //套接字数据传输类型信息
int protocol //计算机间通信中使用的协议信息
);
参数1详细解释,协议族(可取的值)
PF_INET ipv4互联网协议族
PF_INET6 ipv6互联网协议族
PF_LOCAL 本地通信的UNIX协议族
PF_PACKET 底层套接字协议族
PF_IPX IPX Novell协议族
参数2详细解释 套接字类型
套接字类型1 => SOCK_STREAM 面向连接的套接字
特征:
a:传输过程中数据不会丢失
b:按序传输数据
c:传输的数据不存在数据边界
概括:
套接字连接必须 一一对应
可靠的 按序传递 基于字节的面向连接数据的传输方式的套接字
套接字类型2 => SOCK_DGRAM 创建面向消息的套接字
支持datagrams, datagrams是无连接的、不可靠的最大长度(通常很小)的缓冲区。
使用UDP的互联网地址族
参数3详细解释
选择1:IPPROTO_TCP 满足条件参数1为PF_INET; 参数2为 SOCK_STREAM
选择2:IPPROTO_UDP 满足条件参数1为PF_INET; 参数2为 SOCK_DGRAM
参照第一章的demo
/*
2018-11-14 10:51:19
ch03 地址族与数据序列
*/
ip:网络协议的缩写 为收发网络数据而分配给计算机的值
端口号:区分程序中创建的套接字而分配的序号
端口号由16位构成:0 - 65535
0 - 1023 一般分配给特定的程序使用
TCP和UDP套接字不会共用端口号 所以允许重复
bind函数原型
int bind(
SOCKET s,
const struct sockaddr FAR *name,
int namelen
);
主要介绍参数2:
struct sockaddr
{
u_short sa_family;
char sa_data[14];
};
在绑定参数的时 一般使用下面的结构 来进行参数的分配
上下两个结构体 大小相同
struct sockaddr_in
{
short sin_family; // 2 bytes e.g. AF_INET, AF_INET6
unsigned short sin_port; // 2 bytes e.g. htons(3490)
struct in_addr sin_addr; // 4 bytes see struct in_addr, below
char sin_zero[8]; // 8 bytes zero this if you want to
};
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
字节序和网络字节序
CPU保存数据的方式有两种
大端序:高位字节存放到低位地址
小端序:高位字节存放到高位地址
有数:0x12345678
大端显示:12 34 56 78
小端显示:78 56 34 12
通过网络数据传输时 约定统一的传输方式 统一大端
给结构体 SOCKADDR_IN 赋值的时候 需要相应的字节序转换
代码片段:
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(8888); //端口
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.168.0.1");
函数 htons,htonl 详细介绍
h表示主机字节库
n表示网络字节库
s表示short类型
l表示long类型
htons:表示将short类型数据从主机字节序 转换为 网络字节序
除了向sockaddr_in结构体中填充数据外,其他情况无需考虑字节序问题
inet_addr函数 将字符串形式的IP地址 转换成32位的网络字节序
相反的函数
inet_ntoa函数 将32位的网络字节 转换为以字符串显示的ip
/*
2018-11-14 10:51:19
ch04 基于TCP的服务器/客户端(一)
*/
TCP/IP 协议栈 分成4层
由下往上:链路层 -> IP层 -> TCP/UDP -> 应用层
1.链路层
物理链接,LAN、WAN、MAN 等网络标准
2.IP层
IP本身是面向消息的、不可靠的协议;IP协议无法应对数据错误‘
解决传输中路径选择的问题
3.TCP/UDP层
TCP :保证数据传输的可靠性 以IP层为基础
4.应用层
根据程序特点决定服务器端和客户端之间的数据传输协议
**进入连接请求 等待状态
关键函数listen
函数原型:
int listen(
SOCKET s, //服务端套接字
int backlog //最大的连接请求个数
);
接收客户端的连接请求
SOCKET accept(
SOCKET s, //服务端的套接字
struct sockaddr FAR *addr, //SOCKADDR_IN 数据结构
int FAR *addrlen //数据结构的长度
);
返回客户端的套接字 和 相应的SOCKADDR_IN 的结构信息
接下来就可以在服务端上进行收发消息
**客户端的结构
客户端 最关键的点就是请求连接
函数原型:
int connect(
SOCKET s, //客户端套接字的描述符
const struct sockaddr FAR *name, //SOCKADDR_IN 数据结构 保存目标服务器端的地址信息和变量地址信息
int namelen // 数据结构的长度
);
客户端调用connect函数 发生以下情况才能返回
1.服务器端接收连接请求
2.发生断网异常
补充:关于recv函数
函数原型:
int recv(
SOCKET s, //指定发送端套接字描述符
char FAR *buf, //数据
int len, //数据长度
int flags //指定调用的方式标志 一般为0
);
如果成功 则返回接收到的字节数
send函数
函数原型:(数据类型同上)
int send(
SOCKET s,
const char FAR *buf,
int len,
int flags
);
/*
2018-11-14 16:46:53
ch05 基于TCP的服务器/客户端(二)
*/
灵活的使用send和recv返回的字节长度 可以有效的使用数据
协议:制定一些规则来做一些事情
IO缓冲区概念:
1.IO缓冲在每个TCP套接字中单独存在
2.IO缓冲在创建套接字的时候自动生成
3.即使关闭套接字也会继续传递输出缓冲中遗留的数据
4.关闭套接字将丢失缓冲区的数据
TCP 内部工作原理1: 与对方套接字的连接
TCP套接字创建到消失所经过程分为如下3步
Step1 与对方套接字建立连接
Step2 与对方套接字进行数据交换
Step3 断开与对方套接字的连接
TCP 内部工作原理2: 与对方主机的数据交换
通过第一步三次握手过程完成了数据交换准备
按照以下公式传递 ACK消息
ACK号 -> SEQ号 + 传递的字节数 + 1
TCP 内部工作原理3: 断开与套接字的连接
先由套接字A向套接字B传递断开连接的消息,套接字B发出确认收到的消息,然后向套接字A传递断开的消息
套接字A同样发出确认消息
各发一次消息 然后断开连接
/*
2018-11-14 19:17:55
ch06 基于UDP的服务器/客户端
*/
UDP套接字的一个特点:性能比TCP高出很多(这里指的是效率)
在更注重性能而非可靠性的情况下 UDP 是一种很好的选择
UDP的内部工作原理
UDP最重要的作用就是根据端口号 将 传到主机的数据包交付给最终的UDP套接字
TCP比UDP慢的原因
1.收发数据前后进行的连接设置及消除过程
2.收发数据过程中为保证可靠性而添加的流控制
UDP的交互方式
=> UDP中只有创建套接字的过程和数据交换的过程
=> UDP服务器端 和 客户端 均只需1个套接字
关于UDP通讯的相关函数
sendto函数 发送消息的函数
函数原型:
int sendto(
SOCKET s, //用于传输数据的UDP套接字文件描述符
const char FAR *buf, //保存待传输数据的缓冲地址值
int len, //传输数据的长度 以字节为单位
int flags, //可选项参数 若没有则 传递为0
const struct sockaddr FAR *to, //存有目标地址信息sockaddr结构体变量的地址值
int tolen //传递给参数to的长度
);
成功返回发送字节的长度 失败返回-1
UDP客户端套接字的地址分配
=>调用sendto函数时自动分配IP和端口号,因此,UDP客户端中通常无需额外的地址分配过程。
UDP的数据传输特性和调用connect函数
=> UDP是具有数据边界的协议,传输中调用I/O函数的次数非常重要。因此输入函数的调用次数
和输出函数的调用次数 应该完全一致,这样才能保证接收全部已发送的数据。
通过sendto传输数据大致分为以下3个阶段
Step1 向UDP套接注册目标IP和端口号
Step2 传输数据
Step3 删除UDP套接字中注册的目标地址信息
分析基于windows实现的 过程 (linux暂时放一下)
windows下的sendto函数和readfrom函数
int sendto(
SOCKET s,
const char FAR *buf,
int len,
int flags,
const struct sockaddr FAR *to,
int tolen
);
上面已经有函数的各个介绍了 现在我们来撸例子
TCP/IP例子
//Hello_Server 服务端
#include <iostream>
#include <winsock2.h>
#include <string>
#include <vector>using namespace std;void ErrorHandling(const char *message);int Comunicator();
void TestOrder();int main()
{Comunicator();//TestOrder();system("pause");return EXIT_SUCCESS;
}void ErrorHandling(const char * message)
{std::cout << message << std::endl;
}int Comunicator()
{std::vector<std::string> vctCollect;WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() Error");return EXIT_FAILURE;}//Step1 创建套接字SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(8888); //端口addrSrv.sin_addr.s_addr = htonl(INADDR_ANY); //地址//Step2 绑定端口和地址int retVal = bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));if (retVal == SOCKET_ERROR){ErrorHandling("bind() Error");return EXIT_FAILURE;}//Step3 监听客户调if (listen(sockSrv, 5) == SOCKET_ERROR){ErrorHandling("listen() Error");return EXIT_FAILURE;}char message[] = "收到请求";SOCKADDR_IN addrClient;//Step4 等待客户请求到来 int length = sizeof(SOCKADDR);SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrClient, &length);if (sockConn == SOCKET_ERROR){ErrorHandling("accept() Error");return EXIT_FAILURE;}std::cout << "接收客户端的IP为:" << inet_ntoa(addrClient.sin_addr);std::cout << std::endl;//接收客户端发回的消息while (1){char recvBuf[100] = {};std::string strBuf = "";memset(recvBuf, 0, sizeof(recvBuf));int length = recv(sockConn, recvBuf, sizeof(recvBuf), 0);for (int i = 0; i < length; ++i){strBuf += recvBuf[i];}//std::cout << "接收客户端的消息:" << strBuf << std::endl;vctCollect.push_back(strBuf);//send(sockConn, message, sizeof(message), 0);//Step5 发送数据给客户端//std::string message = "";//std::cout << "发送给客户端的消息:";//std::cin >> message;send函数 成功时 返回发送的字符长度 失败的时候 返回SOCKET_ERROR//int iSend = send(sockConn, message.c_str(), message.length(), 0);//if (iSend == SOCKET_ERROR)//{// ErrorHandling("send() Error");// return EXIT_FAILURE;//}char ch = vctCollect[vctCollect.size() - 1][0];int iResult = 0;int num = 0;switch (ch){case '+':num = stoi(vctCollect[0]);if ((num + 2) == vctCollect.size()){for (size_t i = 1; i < vctCollect.size() - 1; ++i){iResult += stoi(vctCollect[1]);}}break;default:break;}if (iResult != 0){string strResult = to_string(iResult);send(sockConn, strResult.c_str(), strResult.length(), 0);break;}if (strBuf == "exit" || "exit" == message){break;}std::cout << std::endl;}closesocket(sockConn);closesocket(sockSrv);WSACleanup();return EXIT_SUCCESS;
}//测试大小端
void TestOrder()
{unsigned short x = 0x12345678;short x2 = *((char*)&x);if (*((char*)&x) == 0x12)std::cout << "大端" << std::endl;elsestd::cout << "小端" << std::endl;//通过函数 可以进行相应的转换unsigned short temp = htons(x);std::cout.setf(std::ios::hex);std::cout << temp << std::endl;}
TCP客户端
// Hello_Client.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include "pch.h"
#include <iostream>
#include <winsock2.h>
#include <stdio.h>
#include <string>void ErrorHandling(const char *message);int main()
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() Error");return EXIT_FAILURE;}std::cout << "输入客户端的IP地址:";std::string strIP = " ";std::getline(std::cin, strIP);SOCKADDR_IN addrSrv;addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(8888);addrSrv.sin_addr.S_un.S_addr = inet_addr(strIP.c_str());//Step1 创建套接字SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);if (SOCKET_ERROR == sockClient){ErrorHandling("socket() Error");return EXIT_FAILURE;}char buff[1024] = { 0 };//Step2 向服务器发出连接请求if (connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET){ErrorHandling("connect() Error");return EXIT_FAILURE;}while (1){//Step3 发送数据std::string message = " ";std::cout << "发送给服务端的消息:";std::getline(std::cin, message);send(sockClient, message.c_str(), message.length(), 0);if (message[0] == '+'){//接收数据recv(sockClient, buff, sizeof(buff), 0);std::cout << "接收服务端的消息:" << buff << std::endl;break;}memset(buff, 0, sizeof(buff));std::cout << std::endl;if (buff == "exit" || "exit" == message){break;}}//关闭套接字closesocket(sockClient);WSACleanup();system("pause");return EXIT_SUCCESS;
}void ErrorHandling(const char * message)
{std::cout << message << std::endl;
}
下面是UDP的例子
UDP服务端
// UDP_server.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#include <string>#define BUF_SIZE 30void ErrorHandling(const char *message);int main()
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() Error");return EXIT_FAILURE;}//Step1 创建套接字SOCKET servSock = socket(PF_INET, SOCK_DGRAM, 0);if (servSock == INVALID_SOCKET){ErrorHandling("UDP socket creation error");return EXIT_FAILURE;}SOCKADDR_IN servAdr, clntAdr;memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = htonl(INADDR_ANY);servAdr.sin_port = htons(8888);if (bind(servSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR){ErrorHandling("bind() Error");}int clntAdrSz = 0;int strLen = 0;char message[BUF_SIZE] = { 0 };while (1){clntAdrSz = sizeof(clntAdr);strLen = recvfrom(servSock, message, BUF_SIZE, 0, (SOCKADDR*)&clntAdr, &clntAdrSz);sendto(servSock, message, strLen, 0, (SOCKADDR*)&clntAdr, sizeof(clntAdr));std::cout << message << std::endl;}closesocket(servSock);WSACleanup();system("pause");return EXIT_SUCCESS;
}void ErrorHandling(const char * message)
{std::cout << message << std::endl;
}
UDP客户端
// UDP_Client.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#include <string>#define BUF_SIZE 30void ErrorHandling(const char *message);int main()
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() Error");return EXIT_FAILURE;}//Step1 创建套接字SOCKET clientSock = socket(PF_INET, SOCK_DGRAM, 0);if (clientSock == INVALID_SOCKET){ErrorHandling("UDP socket creation error");return EXIT_FAILURE;}SOCKADDR_IN servAdr, clntAdr;memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = inet_addr("127.0.0.1");servAdr.sin_port = htons(8888);connect(clientSock, (SOCKADDR*)&servAdr, sizeof(servAdr));int clntAdrSz = 0;int strLen = 0;char messageRecv[BUF_SIZE] = { 0 };std::string message = " ";while (1){std::cout << "输入消息:";std::getline(std::cin, message);send(clientSock, message.c_str(), message.length(), 0);strLen = recv(clientSock, messageRecv, BUF_SIZE, 0);std::cout << "输出从服务端传送过来的数据:" << messageRecv << std::endl;//sendto(clientSock, message, strLen, 0, (SOCKADDR*)&clntAdr, sizeof(clntAdr));memset(messageRecv, 0, BUF_SIZE);if ("exit" == message){break;}}closesocket(clientSock);WSACleanup();system("pause");return EXIT_SUCCESS;
}void ErrorHandling(const char * message)
{std::cout << message << std::endl;
}
知识整理来自书籍《《TCP IP网络编程》.((韩)尹圣雨)》
TCP/IP基础知识复习相关推荐
- tcp丢包率_网络编程 | TCP/IP基础知识
在2017年10月深圳 Cocos 沙龙上,有幸结识了社区中大名顶顶的Colin,Shawn在论坛上第一次看到Colin的团队用CocosCreator制作的<热血暗黑>时就被深深地震撼到 ...
- 一篇经典的TCP/IP基础知识英文文章
一篇经典的TCP/IP基础知识英文文章 INTRODUCTION When you configure the TCP/IP protocol on a Microsoft Windows compu ...
- TCP IP基础知识的复习
TCP/IP网络协议栈分为应用层(Application).传输层(Transport).网络层(Network)和链路层(Link)四层.如下图所示 两台计算机通过TCP/IP协议通讯的过程如下所示 ...
- TCP/IP 基础知识总结
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 要说我们接触计算机网络最多的协议,那势必离不开 TCP/I ...
- TCP/IP协议(二)tcp/ip基础知识
转载:http://www.cnblogs.com/imyalost/p/6139191.html 一.TCP/IP的标准化 1.TCP/IP的含义 一般来说,TCP/IP是利用IP进行通信时所必须用 ...
- 第二章 TCP/IP 基础知识
概述: tcp和ip 是互联网很多协议中最著名的 这一章 重点介绍tcp/ip的发展历程和有关协议概况 tcp/ip出现的背景及其历史 1,首先是ARPANET 阿帕网的诞生 起先是美国国防部DoD ...
- (chap2 TCP/IP基础知识) TCP/IP协议分层模型-应用层
应用层--会话层以上的分层 1. C/S模型 2. WWW (World Wide Web) WWW 中的HTTP是OSI应用层的协议,HTML属于表示层的协议. 3. 电子邮件 SMTP (Simp ...
- (chap2 TCP/IP基础知识) TCP/IP协议分层模型
1. TCP ( Transmission Control Protocol)和IP( lnternet Proto-col )通信协议群 1.1 Internet互联网 互联网中的每个网络都是由骨干 ...
- 《图解TCP/IP》读书笔记二:TCP/IP基础知识
TCP(Transmission Control Protocol,传输控制协议) IP(Internet Protocol,网络之间互连的协议) ICMP(Internet Control Mess ...
最新文章
- 2017年卖掉全副身家买比特币,全家人一起游牧……这个企业家好疯狂!
- ASPNET服务器控件之一
- centos 7 部署 open-falcon 0.2.0
- Winsock编程原理——面向连接
- Ubantu中安装sublime
- request获得请求头
- hive UDF函数
- java new 面试_java面试30问
- Hystrix能解决的问题
- python嵩天课后题及答案第二章_课后参考答案-第二章部分习题参考答案
- 用css3和ico图片实现火狐社区的分享图标
- CUDA 开启GPU之间的P2P通信功能
- 计算机由简单的二进制阴阳,二进制之美,大道至简,二生万物!
- php中读取session,php中如何注册和读取Session会话
- Android 多线程断点下载
- YOLOv5图像识别显示中文标签
- 我的脚本-一键禁用启用笔记本自带键盘
- Cent OS 使用nohup 启动 Springboot避坑
- 8.17 一个博客demo
- python猜单词游戏_17.Python猜单词游戏