面向连接与面向消息(TCP/UDP)
面向连接的套接字(TCP):
1.传输过程中数据不会丢失
2.按顺序传输数据
3.传输过程中没有数据边界
面向消息的套接字(UDP):
1.强调快速传递而非顺序
2.传递过程中数据可能丢失或者损坏
3.限制每次传输的数据大小
4.传输过程中有数据边界
网络编程的基本函数
套接字类型
SOCK_STREAM[流套接字] TCP
面向连接、可靠的数据传输 适合传输大量的数据,不支持广播、多播
SOCK_DGRAM[数据包套接字] UDP
无连接 支持广播、多播
SOCK_RAW[原始套接字]
可以读写内核没有处理的 IP 数据报
避开 TCP/IP 处理机制,被传送的数据报可以被直接传送给需要它的的应用程序
协议设置
引用头文件 winsock2.h
导入 ws2_32.lib 库
window 下 socket 编程都要首先进行 Winsock 的初始化
基本函数
send和recv是TCP时用
sendto和recvfrom是UDP时用
数据结构
struct sockaddr
{
u_short sa_family; //16 位地址类型 2 字节
char sa_data[14]; //14 字节地址数据:ip + port
};
struct sockaddr_in
{
short sin_family; //16 位地址类型
u_short sin_port; //16 位端口号 65535 2 的 16 次方
struct in_addr sin_addr; //32 位 IP 地址 4 字节
char sin_zero[8]; //8 字节填充
}
二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。
sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。
TCP套接字(服务器与客户端流程)
服务器端`
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
//选中函数按下F1可以跳到函数对应的文档
int main()
{
printf("TCP Server\n");
//0 初始化网络库
// 加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
//初始化套接字库
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("LOBYTE errorNum = %d\n", GetLastError());
WSACleanup(); return -1;
}
//1.SOCKET 创建
SOCKET socksrv = socket(AF_INET, SOCK_STREAM, 0);
if (socksrv == SOCKET_ERROR)
{
printf("SOCKET errorno = %d\n", GetLastError());
return -1;
}
//2.分配IP端口和绑定IP端口
SOCKADDR_IN addrsrv;
addrsrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY)//htonl用来将主机字节顺序转换为网络字节顺序;
addrsrv.sin_family = AF_INET;
addrsrv.sin_port = htons(6666)//htons用来将主机字节顺序转换为网络字节顺序;
if (bind(socksrv, (SOCKADDR*)