目录
14.1 计算机网络基本知识
14.1.7 TCP/IP模型
14.1.8 端口
14.1.10 网络字节顺序
sockets(套接字)编程有三种,
TCP流式套接字的编程步骤
在使用之前须链接库函数:工程->设置->Link->输入ws2_32.lib,OK!
服务器端程序:
客户端程序:
UDP型套接字
服务器端(接收端)程序:
客户端(发送端)程序:
扩展阅读:
TCP程序代码实现
新建项目
代码
TcpSrv.cpp
TcpClient.cpp
程序执行画面
14.6基于UDP的简单聊天程序
面试常考OSI七层参考模型
TCP/IP模型 |
OSI参考模型 |
Function |
应用层 |
应用层 |
处理网络应用 |
表示层 |
数据表示 |
|
会话层 |
主机间通信 |
|
传输层 |
传输层 |
端到端的连接 |
网络层 |
网络层 |
寻址和最短路径 |
网络接口 |
数据链路层 |
介质访问(接入) |
物理层 |
二进制传输 |
1. 端口使用一个16位的(二进制)数字来表示,范围0~65535(0XFFFF),1024以下的端口号保留给预定义的服务,如http使用端口80。我们在编写网络应用程序时,要为程序指定1024以上的端口号。
1. 基于Intel的CPU,我们常用的PC机采用的是低位先存。为保证数据的正确性,在网络协议中需要指定网络字节顺序,TCP/IP协议使用16位整数和32位整数的高位先存格式。
2. 网络中不同主机间进行通信时,要统一采用网络字节顺序。
流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);基于TCP的socket编程是采用的流式套接字(SOCK_STREAM)。基于UDP采用的数据报套接字(SOCK_DGRAM).
VS2013: Linker -> Input->Additional Dependencies->输入ws2_32.lib
1、加载套接字库
2、创建套接字(socket)。
3、将套接字绑定到一个本地地址和端口上(bind)。
4、将套接字设为监听模式,准备接收客户请求(listen)。
5、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
6、用返回的套接字和客户端进行通信(send/recv)。
7、返回,等待另一客户请求。
8、关闭套接字。
1、加载套接字库
2、创建套接字(socket)。
3、向服务器发出连接请求(connect)。
4、和服务器端进行通信(send/recv)。
5.关闭套接字。
1、创建套接字(socket)。
2、将套接字绑定到一个本地地址和端口上(bind)。
3、等待接收数据(recvfrom)。
4、关闭套接字。
1、创建套接字(socket)。
2、向服务器发送数据(sendto)。
3、关闭套接字。
为什么qq用的UDP为主,而TCP为辅助
https://blog.csdn.net/pmt123456/article/details/55216555
在当前项目TcpSrvs中添加TcpClient project
#include
#include
void main()
{
//Load socket library
WORD wVersionRequested;//版本号
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);//1.1版本的套接字
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return;
}//加载套接字库,如果失败返回0
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1)
{
return;
}//判断高低字节是不是1,如果不是1.1的版本则退出
//创建流式套接字,基于TCP(SOCK_STREAM)
SOCKET socSrv = socket(AF_INET, SOCK_STREAM, 0);
//Socket地址结构体的创建
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//转换Unsigned long型为网络字节序格式
addrSrv.sin_family = AF_INET;//指定地址簇
addrSrv.sin_port = htons(6000);
//指定端口号,除sin_family参数外,其它参数都是网络字节序,因此需要转换
//将套接字绑定到一个端口号和本地地址上
bind(socSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
//listen mode, ready for client request
listen(socSrv, 5);
SOCKADDR_IN addrClient;//字义用来接收客户端Socket的结构体
int len = sizeof(SOCKADDR);//初始化参数,这个参数必须进行初始化
//循环等待接受客户端发送请求
while (1)
{
//等待客户请求到来;当请求到来后,接受连接请求,
//返回一个新的对应于此次连接的套接字(accept)。
//此时程序在此发生阻塞
SOCKET sockConn = accept(socSrv, (SOCKADDR*)&addrClient, &len);
char sendBuf[100];
sprintf(sendBuf, "Welcome %s to http://sunxin.org",
inet_ntoa(addrClient.sin_addr));//格式化输出
//用返回的套接字和客户端进行通信
send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);//多发送一个字节
//接收数据
char recvBuf[100];
recv(sockConn, recvBuf, 100, 0);
printf("%s\n", recvBuf);
closesocket(sockConn);
}
}
#include
#include
void main()
{
WORD wVersionRequested;//版本号
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);//1.1版本的套接字
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return;
}//加载套接字库,如果失败返回0
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1)
{
return;
}//判断高低字节是不是1,如果不是1.1的版本则退出
//创建流式套接字,基于TCP(SOCK_STREAM)
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
//Socket地址结构体的创建
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(6000);
//指定端口号,除sin_family参数外,其它参数都是网络字节序,因此需要转换
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
char recvBuf[100];//和服务器端进行通信(send/recv)。
recv(sockClient, recvBuf, 100, 0);
printf("%s\n", recvBuf);
send(sockClient, "This is lisi", strlen("This is lisi") + 1, 0);
closesocket(sockClient);//关闭套接字。
WSACleanup();//必须调用这个函数清除参数
}
先执行服务器端,再执行客户端。客户端可以多次关闭打开。
对于聊天程序来说,即使丢失一些数据,也不会影响信息的交流,可以根据上下文的情况,知道对方所要表达的意思,或者根据对方的信息,重新发送我们所说的话。对于TCP来说,在通信前,需要经过三步握手协议以建立连接,而建立连接的过程往往是比较耗费时间的。连接建立后,在聊天过程中,可能经过好长一段时间,聊天的双方才会说一句话,那么连接是应该保持还是先断开,等对方说话时再建立连接呢?也就是说,TCP协议的面向连接、数据确认和重传机制将会影响聊天的效率。所以,对于聊天类的软件来说,通常都采用基于UDP的方式来实现。这种方式的特点是不需要建立连接,也没有数据确认和重传机制,因此实时性较高。
来源: VC++深入详解 孙鑫