/*
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基础知识复习相关推荐

  1. tcp丢包率_网络编程 | TCP/IP基础知识

    在2017年10月深圳 Cocos 沙龙上,有幸结识了社区中大名顶顶的Colin,Shawn在论坛上第一次看到Colin的团队用CocosCreator制作的<热血暗黑>时就被深深地震撼到 ...

  2. 一篇经典的TCP/IP基础知识英文文章

    一篇经典的TCP/IP基础知识英文文章 INTRODUCTION When you configure the TCP/IP protocol on a Microsoft Windows compu ...

  3. TCP IP基础知识的复习

    TCP/IP网络协议栈分为应用层(Application).传输层(Transport).网络层(Network)和链路层(Link)四层.如下图所示 两台计算机通过TCP/IP协议通讯的过程如下所示 ...

  4. TCP/IP 基础知识总结

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 要说我们接触计算机网络最多的协议,那势必离不开 TCP/I ...

  5. TCP/IP协议(二)tcp/ip基础知识

    转载:http://www.cnblogs.com/imyalost/p/6139191.html 一.TCP/IP的标准化 1.TCP/IP的含义 一般来说,TCP/IP是利用IP进行通信时所必须用 ...

  6. 第二章 TCP/IP 基础知识

    概述: tcp和ip 是互联网很多协议中最著名的 这一章 重点介绍tcp/ip的发展历程和有关协议概况 tcp/ip出现的背景及其历史 1,首先是ARPANET  阿帕网的诞生 起先是美国国防部DoD ...

  7. (chap2 TCP/IP基础知识) TCP/IP协议分层模型-应用层

    应用层--会话层以上的分层 1. C/S模型 2. WWW (World Wide Web) WWW 中的HTTP是OSI应用层的协议,HTML属于表示层的协议. 3. 电子邮件 SMTP (Simp ...

  8. (chap2 TCP/IP基础知识) TCP/IP协议分层模型

    1. TCP ( Transmission Control Protocol)和IP( lnternet Proto-col )通信协议群 1.1 Internet互联网 互联网中的每个网络都是由骨干 ...

  9. 《图解TCP/IP》读书笔记二:TCP/IP基础知识

    TCP(Transmission Control Protocol,传输控制协议) IP(Internet Protocol,网络之间互连的协议) ICMP(Internet Control Mess ...

最新文章

  1. 2017年卖掉全副身家买比特币,全家人一起游牧……这个企业家好疯狂!
  2. ASPNET服务器控件之一
  3. centos 7 部署 open-falcon 0.2.0
  4. Winsock编程原理——面向连接
  5. Ubantu中安装sublime
  6. request获得请求头
  7. hive UDF函数
  8. java new 面试_java面试30问
  9. Hystrix能解决的问题
  10. python嵩天课后题及答案第二章_课后参考答案-第二章部分习题参考答案
  11. 用css3和ico图片实现火狐社区的分享图标
  12. CUDA 开启GPU之间的P2P通信功能
  13. 计算机由简单的二进制阴阳,二进制之美,大道至简,二生万物!
  14. php中读取session,php中如何注册和读取Session会话
  15. Android 多线程断点下载
  16. YOLOv5图像识别显示中文标签
  17. 我的脚本-一键禁用启用笔记本自带键盘
  18. Cent OS 使用nohup 启动 Springboot避坑
  19. 8.17 一个博客demo
  20. python猜单词游戏_17.Python猜单词游戏

热门文章

  1. selenium退出浏览器驱动方式
  2. 史上最精简Glide解析(一)
  3. 无线led显示屏的优势
  4. Sqoop 安装配置、指令介绍【导入、导出】
  5. winform picturebox 图片布满
  6. 华硕K40AB摄像头驱动的安装
  7. python课堂讨论_Python的课堂总结吧
  8. 关于浏览器兼容性问题
  9. STM32单片机(一)STM32简介
  10. samba服务器配置 虚拟光驱,折腾了几天的ESXI,基本顺畅了