1、tcp与udp的对比:
a)TCP是面向连接的传输控制协议,而UDP提供了无连接的数据报服务;
b)TCP具有高可靠性,确保传输数据的正确性,不出现丢失或乱序;.(不丢失不乱序)
UDP在传输数据前不建立连接,不对数据报进行检查与修改,无须等待对方的应答,
所以会出现分组丢失、重复、乱序,应用程序需要负责传输可靠性方面的所有工作;
(就算网络是正常的仍然不排除丢包的现象,不会丢半个包要丢就是一个包)
(可能丢包可能包与包的顺序先发的后到出现混乱)
c)也,正因为以上特征,UDP具有较好的实时性,工作效率较TCP协议高;
d)UDP段结构比TCP的段结构简单,因此网络开销也小。
2、TCP通讯流程:
接收端:
WSAStartup:初始化操作
socket:建立套接字
bind:绑定
listen:开始侦听
accept:接纳客户端连接(如同公司的前台)
send/recv/recvfrom:收发数据(如同公司的客户经理)
发送端:
WSAStartup:初始化操作
socket:建立套接字
bind:绑定
connect:连接
send/recv/recvfrom:收发数据(如同公司的客户经理)
=
接收端:
#include<Winsock.h>
#include<iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
WSADATA ws;
WSAStartup(0x0202, &ws); //连接
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
sockaddr_in sa = { 2 ,htons(3000)};
bind(sock, (sockaddr*)&sa, sizeof(sa));//绑定
listen(sock, 5);//监听
int nLen = sizeof(sa);
char c[1024] = { 0 };
while (true)
{
SOCKET socka = accept(sock, (sockaddr*)&sa, &nLen);//接待//后面俩个参数是将连接到的发送端的ip和端口记录下来,不录就填NULL
while (true)
{
// getpeername(socka, (sockaddr*)&sa, &nLen);//获取发送端信息ip和端口
int n = recv(socka, c, sizeof(c), 0);//收发数据
cout << inet_ntoa(sa.sin_addr) << "-" << htons(sa.sin_port) <<":"<< c << endl;
}
}
return 0;
}
发送端:
#include<Winsock.h>
#include<iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
WSADATA ws;
WSAStartup(0x0202, &ws); //连接
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
sockaddr_in sa = { 2 };
int n=bind(sock, (sockaddr*)&sa, sizeof(sa));//绑定
sa.sin_port = htons(3000);
sa.sin_addr.S_un.S_addr = inet_addr("192.168.1.9");
connect(sock, (sockaddr*)&sa, sizeof(sa));//连接到接收端
char c[1024] = {0};
while (true)
{
cout << "请输入:";
cin >> c;
n=send(sock, c, strlen(c), 0);//收发数据
}
return 0;
}
3.创建线程
_beginthread()需要#include<process.h>//创建线程函数
uintptr_t _beginthread( void( __cdecl *start_address )( void * ), unsigned stack_size, void *arglist );
第一个参数:
start_address为启动开始执行新线程的例程的地址,一般我们执行一个函数,这个参数就是你定义的函数名(这个函数就是一个新线程)。
第二个参数:
stack_size,新线程的堆栈大小或 0。一般我们使用0,代表跟主线程使用一样的堆栈。
第三个参数:
arglist,要传递到新线程的参数列表或 NULL。如果你要传递参数给新的线程,可以在这里写上参数的地址指针,如果不需要传递数据,就使用NULL。void Thread(void* p)//创建一个新线程就等价于创建一个main函数,而这个就相当于main函数
{
SOCKET* psock = (SOCKET*)p;//void是接受任何类型的,当使用需要强制转换一下
SOCKET socka = *psock;//psock的内容赋值给一个新的socka,如果不这样做那么。有多个线程的化,他们将指向同一个sock
sockaddr_in sa = { AF_INET };
int nLen = sizeof(sa);
char c[1024] = { 0 };
while (true)
{
getpeername(socka, (sockaddr*)&sa, &nLen);//获取发送端信息ip和端口
int n = recv(socka, c, sizeof(c), 0);//收发数据
if (n <= 0)
{
cout << inet_ntoa(sa.sin_addr) << "-" << htons(sa.sin_port) << "断开连接!" << endl;
break;
}
cout << inet_ntoa(sa.sin_addr) << "-" << htons(sa.sin_port) <<":"<< c << endl;
}
}
int main()
{
WSADATA ws;
WSAStartup(0x0202, &ws); //连接
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
sockaddr_in sa = { 2 ,htons(3000)};
bind(sock, (sockaddr*)&sa, sizeof(sa));//绑定
listen(sock, 5);//监听
int nLen = sizeof(sa);
while (true)
{
SOCKET socka = accept(sock, (sockaddr*)&sa, &nLen);//接待//后面俩个参数是将连接到的发送端的ip和端口记录下来,不记录就填NULL
_beginthread(Thread, 0, &socka);//带入函数名(新线程),0,再将socka带进来,因为要再新线程里面进行接收数据,当
//有俩个客户端连接进来就会创建俩个线程同时接收信息
cout << inet_ntoa(sa.sin_addr) << "-" << htons(sa.sin_port) << "连接成功!" << endl;
}
return 0;
}
|