网络基础知识

网络模型知识

OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准化框架,简称OSI。

TCP/IP模型:(tcp/ip reference model)TCP/IP参考模型,是一组用于实现网络互连的通信协议,其名称来源于该协议簇中两个重要的协议(IP协议和TCP协议)。

internet网络体系结构以TCP/IP为核心。基于TCP/IP的参考模型将协议分成四个层次,分别是:应用层、传输层、网络层、数据链路层(接口层)

TCP/IP模型

发送方 行为 接收方
应用层 进程产生一些数据
传输层 主机或代理服务器规划怎么传输数据,保证传输正确
网络层 网络中各个节点路由转发数据包
数据链路层 物理媒介,将数字信号转换成模拟信号,传递信息。

数据从应用层出发,各层为了实现自己的任务,需要传递一些数据,这些数据就以一种“外壳”的形式把上一层发来的数据包裹起来。

当数据到达目标主机后,各层再从经过的数据包上剥下“外壳”,这个外壳是数据出发时流经同一层时给添加上的,这样不同的进程,在相同的层之间便完成了数据的交换,从而可以完成本层的任务。

网络模型中一组协议协同工作时,我们称之为协议栈。

网络模型与协议

(1)应用层:相当于OSI模型的上面3层,是负责处理各类应用程序的发送数据包的所有细节问题;

(2)传输层:包括传输控制协议(TCP)和用户数据报协议(UDP) ;

(3)网络层:包括因特网协议(IP),地址解析协议(ARP),反向地址解析协议(RARP)和因特网控制消息协议(ICMP)等,IP提供的一种不可靠的服务;

(4)数据链路层:相当于OSI模型的下面两层,是TCP/IP协议与底层硬件设备的接口,负责网络层与物理设备之间的数据交换,在TCP/IP中主要为IP协议发送和接受数据。它隐藏了物理设备的实现细节,网络层只是简单地数据交给接口层/或从接口层接收数据,并不关心物理层的网络形式,网络接口层通常是在设备驱动程序或网络接口卡中实现的;

OSI与TCP/IP模型对应关系

应用层: HTTP、FTP、POP3、Telnet、DNS、DHCP;

传输层:TCP、UDP;

网络层:IP;

数据链路层(接口层):PPP、以太网驱动、GPRS;

网络编程基础

TCP协议

TCP协议是一种面向连接的,可靠的,基于字节流的传送层通信协议。

类似于拨打电话,使用该种方式进行网络通讯时,需要建立专门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则客户端会自动重发该数据。

UDP协议

UDP是一种无连接的传送层协议,提供不可靠信息传输服务。

UDP类似于发送短信,使用这种方式进行网络通讯时,不需要建立专门的虚拟连接,传输也不是很可靠,如果发送失败则客户端无法获得。

socket介绍

Socket是处在应用层与其下三层之间的一组抽象接口。是操作系统提供的一组编程API,它把复杂的TCP/IP协议簇封装在Socket接口后面,对用户来说,一组简单的接口就是全部。让socket去组织数据,以符合指定的协议。

目前实际应用的Windows Sockets 规范主要有1.1版和2.0版,其中1.1版只支持TCP/IP协议,而2.0版支持多协议,并具有良好的向后兼容性。

使用windows sockets的主要步骤:

1、加载WinSock库 WASStartup()

2、创建套接字 socket()

3、绑定套接字 bind()

4、开始监听 listen()

5、接收/发送处理 send()/recv()

6、接收客户的连接请求 accept()

7、关闭套接字 closesocket()

8、清理环境 WASclearend()

熟悉以上几个函数就可以做简单的网络编程了,下面是函数的使用说明。

加载WinSock函数 WSAStartup()

不管是客户端还是服务端,开发socket应用程序时,必须先加载windows sockets动态库,通常用WSAStartup函数实现此功能

//wVersionRequested参数用来指定想要加载winSock库的版本
WSAStartup (
_In_ WORD wVersionRequested,
_Out_ LPWSADATA lpWSAData
);

套接字函数 socket()

初始化WinSocket DLL后,创建套接字,socket函数和WSASocket函数将实现此功能。该函数调用成功后,返回一个新建的套接字句柄

SOCKET socket(
_In_ int af,
_In_ int type,
_In_ int protocol
); 所需要填入的参数:
af:
AF_INET //internet协议(IP V4)
AF_IRDA //红外协议
AF_BTH //蓝牙协议 type:
SOCK_STREAM //流式socket(使用TCP建立连接,通信过程可靠 TCP)
SOCK_DGRAM //数据报socket(无需建立连接,通讯过程不可靠 UDP)
SOCK_RAW //原始套接字,winsock接口并不使用某种特定的协议去封装它,而是由程序字形处理数据报和协议首部。

绑定套接字函数 bind()

bind()函数将套接字绑定到一个已知的地址上。

int bind(
_In_ SOCKET s, //套接字
_In_reads_bytes_(namelen) const struct sockaddr FAR * name, //地址结构体变量(IP,端口,协议簇)
_In_ int namelen //Sockaddr 结构长度
); 示例:
sockaddr_in servAddr;
servAddr.sin_family = AF_INET; //internet协议(IP V4)
servAddr.sin_port = htons((short)5566); //指定了TCP或UDP通信服务的端口号
//记得把地址修改成自己的IP
ervAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//sin_addr域用来存储32位IP地址。这个结构中包含一个联合结构体S_un, //3. 绑定套接字
int retVal = bind(listenSock, (sockaddr *)&servAddr, sizeof(sockaddr)); 端口号的选取范围:
0-1023由IANA管理,保留为公共服务使用;
1024-48151是普通用户注册的端口号;
49152-65535是动态的端口号;

监听函数 listen()

listen函数将套接字设置为监听模式。

int
WSAAPI
listen(
_In_ SOCKET s, //套接字
_In_ int backlog //能够同时连接的最大数
);

Backlog:

如果该参数为3表示等待连接的最大数值3,如果有4个客户端同时向服务器发出请求,那么前3个连接会被放在等待处理的队列中,以便服务器依次为他们服务,而第4个连接将会造成错误。当服务器接受了一个连接请求,这个请求就从队列中删除。

示例:
re = listen(listenSock, //套接字
SOMAXCONN); //能够同时连接的最大数

接收客户的连接请求 accept()

accept函数实现接收一个连接请求;

SOCKET
accept(
_In_ SOCKET s, //套接字
_Out_writes_bytes_opt_(*addrlen) struct sockaddr FAR * addr, //返回请求连接主机的地址
_Inout_opt_ int FAR * addrlen //sockaddr_in的大小
);

接收/发送处理 send()/recv()

recv()函数用于接收数据,send()函数用于发送数据,参数基本相同!

int
recv(
_In_ SOCKET s, //发送数据的套接字
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf, //接收数据缓冲区
_In_ int len, //缓冲区的大小
_In_ int flags //影响该函数的行为
); int
send(
_In_ SOCKET s, //发送数据的套接字
_In_reads_bytes_(len) const char FAR * buf, //接收数据缓冲区
_In_ int len, //缓冲区的大小
_In_ int flags //影响该函数的行为
); 示例:
//6. 发数据
char buf[512] = "hello I am message";
retVal = send(sToClientSock, buf, sizeof(buf), 0); //7. 收数据
char recvBuf[1024] = { 0 };
retVal = recv(sToClientSock, recvBuf, 1024, 0);

关闭套接字 closesocket()

closesocket()函数只有一个参数,就是要关闭套接字句柄。

int
WSAAPI
closesocket(
_In_ SOCKET s //发送数据的套接字
);

connect()函数

客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。connect()成功返回0,出错返回-1。

int
connect(
_In_ SOCKET s, //套接字
_In_reads_bytes_(namelen) const struct sockaddr FAR * name, //地址结构体变量(IP,端口,协议簇)
_In_ int namelen //Sockaddr 结构长度
); 示例代码:
//3. 连接服务器 connect
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(short(1234));
servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//记得修改为服务器IP
int retVal = connect(sToServerSock, (sockaddr*)&servAddr, sizeof(sockaddr));

TCP编程实例代码

TCP服务端代码:

#include "stdafx.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib") /*
1. 初始化环境 wsastartup
2. 创建套接字 socket
3. 绑定套接字 bind
4. 监听套接字 listen
5. 处理套接字连接 accept
6. 发送接收数据 send/recv
7. 关闭套接字 closesocket
8. 清理环境 wsacleanup
*/
int main()
{
// 1. 初始化环境 wsastartup
WSADATA wsd = {0};
int re = WSAStartup(MAKEWORD(2,2),&wsd);
if (re != 0) { return 0; }
if (LOBYTE(wsd.wVersion) != 2||
HIBYTE(wsd.wVersion) != 2 )
{
return 0;
}
// 2. 创建套接字 socket
SOCKET listenSock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (listenSock == INVALID_SOCKET)
{
return 0;
}
// 3. 绑定套接字 bind
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(0x5566);
serverAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.96");//自己的IP或者127.0.0.1 re = bind(listenSock,(sockaddr *)&serverAddr, sizeof(serverAddr));
if (re == SOCKET_ERROR)
{
printf("bind error");
goto over;
}
// 4. 监听套接字 listen
re = listen(listenSock, SOMAXCONN);
if (re == SOCKET_ERROR)
{
printf("listen error");
goto over;
}
// 5. 处理套接字连接 accept
sockaddr_in clientAddr;
int size = sizeof(clientAddr);
SOCKET sToClientSock = accept(
listenSock, (sockaddr *)&clientAddr,&size);
if (sToClientSock == INVALID_SOCKET)
{
printf("listen error");
goto over;
}
// 6.1 发送数据 send
char buf[100] = "hello socket client,from server!";
re = send(sToClientSock, buf, strlen(buf), 0);
if (re == SOCKET_ERROR)
{
goto over;
}
// 6.2 接收数据 recv
re = recv(sToClientSock, buf, sizeof(buf), 0);
if (re == INVALID_SOCKET)
{
goto over;
}
over:
// 7. 关闭套接字 closesocket
closesocket(listenSock);
closesocket(sToClientSock);
// 8. 清理环境 wsacleanup
WSACleanup(); return 0;
}

TCP客户端代码:

#include "stdafx.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS #include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib") /*
1. 初始化环境 wsastartup
2. 创建套接字 socket
// 3. 绑定套接字 bind
// 4. 监听套接字 listen
// 5. 处理套接字连接 accept
3. 连接服务器 connect
4. X
5. X
6. 发送接收数据 send/recv
7. 关闭套接字 closesocket
8. 清理环境 wsacleanup
*/
int main()
{
// 1. 初始化环境 wsastartup
WSADATA wsd = { 0 };
int re = WSAStartup(MAKEWORD(2, 2), &wsd);
if (re != 0) { return 0; }
if (LOBYTE(wsd.wVersion) != 2 ||
HIBYTE(wsd.wVersion) != 2)
{
return 0;
}
// 2. 创建套接字 socket
SOCKET sToServSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sToServSock == INVALID_SOCKET)
{
return 0;
}
// 3. 绑定套接字 bind
// 4. 监听套接字 listen
// 5. 处理套接字连接 accept
//3. 连接服务器 connect
sockaddr_in servAddr = {0};
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(0x5566);
servAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.96");
re = connect(sToServSock, (sockaddr *)&servAddr, sizeof(servAddr));
if (re == SOCKET_ERROR)
{
goto over;
}
// 6. 发送接收数据 send/recv
char buf[100] = "hello socket server,from client!";
re = send(sToServSock, buf, strlen(buf), 0);
if (re == SOCKET_ERROR)
{
goto over;
}
re = recv(sToServSock, buf, sizeof(buf), 0);
if (re == INVALID_SOCKET)
{
goto over;
}
over:
// 7. 关闭套接字 closesocket
closesocket(sToServSock);
// 8. 清理环境 wsacleanup
WSACleanup(); return 0;
}

UDP编程

在UDP编程中,大部分函数一样;其中不同的地方为socket()函数创建套接字时需要指定数据报为UDP。发送与接收函数与TCP编程所使用的recv()和send()也有所不同;

接收数据 recvfrom()

recvfrom()函数用于接收数据,并且返回发送数据主机的地址。

注:函数的返回值,是接收到的大小。

int
recvfrom(
//用来接收数据的套接字
_In_ SOCKET s,
//接收数据的缓冲区
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf,
//接收缓冲区的长度
_In_ int len,
//最后一个参数通常为0,0表示无特殊行为;
_In_ int flags,
//接收的地址结构
_Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR * from,
//sockaddr结构的大小
_Inout_opt_ int FAR * fromlen
);

发送数据 sendto()

sendto()函数用于发送数据;

注:该函数成功则返回发送数据的字节数。

int
sendto(
//用来发送数据的套接字
_In_ SOCKET s,
//发送数据的缓冲区
_In_reads_bytes_(len) const char FAR * buf,
//要发送数据的长度
_In_ int len,
//一般为0
_In_ int flags,
//目标地址和端口
_In_reads_bytes_(tolen) const struct sockaddr FAR * to,
//sockaddr结构大小
_In_ int tolen
);

UDP编程实例代码

UDP服务端代码:

#include "stdafx.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib") /*
1. 初始化环境 wsastartup
2. 创建套接字 socket
3. 绑定套接字 bind // 4. 监听套接字 listen
// 5. 处理套接字连接 accept
6. 发送接收数据 sendto/recvfrom 7. 关闭套接字 closesocket
8. 清理环境 wsacleanup
*/
int main()
{
// 1. 初始化环境 wsastartup
WSADATA wsd = { 0 };
int re = WSAStartup(MAKEWORD(2, 2), &wsd);
if (re != 0) { return 0; }
if (LOBYTE(wsd.wVersion) != 2 ||
HIBYTE(wsd.wVersion) != 2)
{
return 0;
}
// 2. 创建套接字 socket
SOCKET sToClientSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sToClientSock == INVALID_SOCKET)
{
return 0;
}
// 3. 绑定套接字 bind
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET; //internet网络协议
serverAddr.sin_port = htons((short)0x5566); //端口号
serverAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.96");//自己的IP或者127.0.0.1 re = bind(sToClientSock, (sockaddr *)&serverAddr, sizeof(serverAddr));
if (re == SOCKET_ERROR)
{
printf("bind error");
goto over;
}
// 4. 监听套接字 listen
// 5. 处理套接字连接 accept
// TCP编程需要使用两个套接字,而UDP只需要一个,这是TCP的通信方式和UDP的方式决定的; // 6. 发送接收数据 sendto/recvfrom
char buf[100] = "hello socket client,from server!";
sockaddr_in clientAddr ;
int size = sizeof(clientAddr);
re = recvfrom(sToClientSock, buf, sizeof(buf), 0,
(sockaddr*) &clientAddr,&size);
if (re == INVALID_SOCKET)
{
goto over;
}
char buf2[100] = "hello socket client,from server!";
re = sendto(sToClientSock, buf2, strlen(buf2), 0,
(sockaddr*)&clientAddr,sizeof(clientAddr)); if (re == SOCKET_ERROR)
{
goto over;
} over:
// 7. 关闭套接字 closesocket
closesocket(sToClientSock);
closesocket(sToClientSock);
// 8. 清理环境 wsacleanup
WSACleanup(); return 0;
}

UDP客户端代码:

#include "stdafx.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib") /*
1. 初始化环境 wsastartup
2. 创建套接字 socket
//3. 绑定套接字 bind // 4. 监听套接字 listen
// 5. 处理套接字连接 accept
6. 发送接收数据 sendto/recvfrom 7. 关闭套接字 closesocket
8. 清理环境 wsacleanup
*/
int main()
{
// 1. 初始化环境 wsastartup
WSADATA wsd = { 0 };
int re = WSAStartup(MAKEWORD(2, 2), &wsd);
if (re != 0) { return 0; }
if (LOBYTE(wsd.wVersion) != 2 ||
HIBYTE(wsd.wVersion) != 2)
{
return 0;
}
// 2. 创建套接字 socket
SOCKET sToClientSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sToClientSock == INVALID_SOCKET)
{
return 0;
}
// 3. 绑定套接字 bind
// 4. 监听套接字 listen
// 5. 处理套接字连接 accept // 6. 发送接收数据 sendto/recvfrom
char buf[100] = "hello socket client,from client!";
sockaddr_in clientAddr;
clientAddr.sin_family = AF_INET;
clientAddr.sin_port = htons(0x5566);
clientAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.96");//自己的IP或者127.0.0.1 int size = sizeof(clientAddr); re = sendto(sToClientSock, buf, strlen(buf), 0,
(sockaddr*)&clientAddr, sizeof(clientAddr)); if (re == SOCKET_ERROR)
{
goto over;
}
re = recvfrom(sToClientSock, buf, sizeof(buf), 0,
(sockaddr*)&clientAddr, &size);
if (re == INVALID_SOCKET)
{
goto over;
}
over:
// 7. 关闭套接字 closesocket
closesocket(sToClientSock);
// 8. 清理环境 wsacleanup
WSACleanup(); return 0;
}

【网络编程1】网络编程基础-TCP、UDP编程的更多相关文章

  1. TCP/UDP编程步骤和区别

    一. 概念解析 套接字:一种特殊的文件描述符.一头指向套接字地址(用户),一头指向套接字结构(内核). 套接字结构:由内核维持的一种数据结构,可通过套接字来操作. 套接字地址:ip和port. 二. ...

  2. 网络编程 套接字socket TCP UDP

    网络编程与套接字 网络编程 网络编程是什么: ​ 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 ​ 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...

  3. ~~网络编程(三):TCP/UDP~~

    进击のpython ***** 网络编程--TCP/UDP协议 其实你也发现了,应用层是交给应用来处理的,我们什么也做不了 相较于网络编程来说,我们更重要的是在做应用层和传输层的对接 因为你也看到了, ...

  4. 牛客网Java刷题知识点之TCP、UDP、TCP和UDP的区别、socket、TCP编程的客户端一般步骤、TCP编程的服务器端一般步骤、UDP编程的客户端一般步骤、UDP编程的服务器端一般步骤

    福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑         Java全栈大联盟   ...

  5. Day09: socket网络编程-OSI七层协议,tcp/udp套接字,tcp粘包问题,socketserver

    今日内容:socket网络编程    1.OSI七层协议    2.基于tcp协议的套接字通信    3.模拟ssh远程执行命令    4.tcp的粘包问题及解决方案    5.基于udp协议的套接字 ...

  6. 【LINUX/UNIX网络编程】之使用SOCKET进行UDP编程

    先看任务需求: 实验二 UDP数据发送与接收 [实验目的] 1.熟练掌握套接字函数的使用方法. 2.应用套接字函数完成基本UDP通讯,实现服务器与客户端的文件传送 [实验学时] 4学时 [实验内容] ...

  7. socket与TCP/UDP编程~

    ket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序.要学Internet上的TCP/IP网络编程,必须理解Socket接口. ...

  8. Java基础教程——UDP编程

    UDP:User Datagram Protocol,用户数据报协议 服务端: import java.net.*; import java.io.*; public class UdpServer ...

  9. 网络编程—网络基础概览、socket,TCP/UDP协议

    网络基础概览 socket概览 socket模块—TCP/UDP的实现 TCP/UDP总结 网络基础概览 osi七层协议各层主要的协议 # 物理层传输电信号1010101010 # 数据链路层,以太网 ...

随机推荐

  1. Ambiguous handler methods mapped for HTTP

    前端访问的 controller 地址没有加方法名导致找不到. Servlet.service() for servlet [spring] in context with path [/ssmDem ...

  2. MT【216】韦达定理

    设$n$为正整数,$a_1,a_2,\cdots,a_n;b_1,b_2,\cdots,b_n;A,B$都是正数, 满足$a_i\le b_i,a_i\le A,i=1,2,\cdots,n$ 且$\ ...

  3. 自学Zabbix6.1 Event acknowledgment 事件确认

    自学Zabbix6.1 Event acknowledgment 事件确认 1 概述以往服务器出现报警,运维人员处理完事之后,报警自动取消,但是下一次出现同样一个错误,但是换了一个运维人员,他可能需要 ...

  4. 【BZOJ3811】玛里苟斯(线性基)

    [BZOJ3811]玛里苟斯(线性基) 题面 BZOJ 题解 \(K=1\)很容易吧,拆位考虑贡献,所有存在的位出现的概率都是\(0.5\),所以答案就是所有数或起来的结果除二. \(K=2\)的情况 ...

  5. Stream基础知识

    Stream API Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,但是将执行操作的时间交给具体实现来决定.例如,如果你希望计算某个方法的平均值,你可以在每个元素 ...

  6. 洛谷 P4378 [USACO18OPEN]Out of Sorts S(树状数组求冒泡排序循环次数)

    传送门:Problem P4378 https://www.cnblogs.com/violet-acmer/p/9833502.html 要回宿舍休息了,题解明天再补吧. 题解: 定义一数组 a[m ...

  7. 下拉列表JComboBox,列表框JList

    1.下拉列表JComboBox public class Demo extends JFrame { public Demo() { setBounds(100, 100, 200, 100); se ...

  8. Java 读取文件的内容

    Java 读取文件的内容 1) CLASS_NAME: 换成自己真实的类名 2) /page/test.json: 换成自己真实的page 3) FileUtils: 来自于org.apache.co ...

  9. 跨平台设置NODE_ENV(兼容win和linux)

    通过NODE_ENV可以来设置环境变量(默认值为development).一般我们通过检查这个值来分别对开发环境和生产环境下做不同的处理.可以在命令行中通过下面的方式设置这个值: linux & ...

  10. GO语言的进阶之路-Golang字符串处理以及文件操作

    GO语言的进阶之路-Golang字符串处理以及文件操作 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们都知道Golang是一门强类型的语言,相比Python在处理一些并发问题也 ...