Lesson10:网络编程基础
网络编程是计算机编程的一个重要分支,是网络通信的基础,网络编程主要用到了Windows系统系统提供的socket。网络通信又分为基于TCP和基于UDP两种。本文主要讲解基于TCP和UDP的网络编程基础知识。
1. 基于TCP的socket编程
1.1 TCP服务器
#include <winsock2.h>
#include <stdio.h>
//#include <iostream>
#pragma comment(lib,"ws2_32.lib") //静态加入一个lib文件,也就是库文件ws2_32.lib文件,它提供了对以下网络相关API的支持
void main()
{
/******** 首先设定通信版本 **************/
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested= MAKEWORD(1, 1); //请求的通信版本为(1,1)
err= WSAStartup(wVersionRequested, &wsaData); //调用WSAStartup函数
if(err != 0)
return; //如果返回值不为0,表示没有对应的WinSockDLL.则返回
if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //如果版本的高字节不为1,低字节不为1,则返回。
return;
}
//1创建套接字
SOCKETsockSrv = socket(AF_INET, SOCK_STREAM,0); //(1地址族AF_INET 或 PF_INET,2socket类型SOCK_STREAM或 SOCK_DGRAM,3与特定的地址家族相关的协议) 调用成功返回新的socket数据类型的套接字描述符
//2绑定套接字到本地地址和端口
/*structsockaddr_in{
short sin_family; //地址族
unsignedshort sin_port; //端口号
struct in_addr sin_addr; //套接字的主机IP地址
char sin_zero[8]; //填充数,使sockaddr_in和sockaddr结构长度一样
}*/
SOCKADDR_INaddrSrv;
addrSrv.sin_addr.S_un.S_addr= htonl(INADDR_ANY); //调用htonl函数进行转换主机字节序为TCP/IP网络字节序//addrSrv是一个结构体,里面有一个结构体变量sin_addr,里面包含共用体S_un,然后是共用体成员S_addr,inet_addr函数进行主机字节序转换为网络字节序
addrSrv.sin_family= AF_INET;
addrSrv.sin_port=htons(6000); //端口号为6000,需要1024以上端口号,网络字节序,需要转换,htonl和htons不同在于转换数据的范围
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //绑定套接字和地址的 函数 (1指定要绑定的套接字,2该套接字的本地地址信息,是指向sockaddr结构的指针变量,3指定该地址的长度)
//3调用listen函数,将套接字设置为监听模式,准备接受客户请求
listen(sockSrv,5); //(套接字描述符,最大的等待连接队列数)
//4等待客户请求到来
//5用返回的套接字和客户端进行通信
//6返回,等待另一个客户请求
//7关闭套接字
SOCKADDR_INaddrClient; //定义一个地址结构的变量,用来接受客户端的地址信息,在循环结构中用到
intlen = sizeof(SOCKADDR);
while(1) //死循环,一直监听,持续运行
{
SOCKETsockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len); //(1处于监听状态的主机服务器套接字描述符,2指向buffer指针连接实体地址,保存了发起客户端的ip信息和端口信息,3指向整型指针,包含返回地址结构的长度),返回一个新的连接的套接字描述符,用他和客户端通信,先前的套接字继续监听客户连接请求
//发送数据
/*intsend(
SOCKETs, //建立连接所对应的套接字描述符,不是监听的套接字
constchar FAR * buf, //一个buff指针,包含将要传送的数据
int len, //buffer中数据的长度
intflags //设定send的调用行为
);*/
charsendBuf[100];
sprintf_s(sendBuf,"Welcome%s to http://www.sunxin.org",inet_ntoa(addrClient.sin_addr)); //把格式化的数据写入某个字符串,将客户端地址传给字符串里的%s,
send(sockConn,sendBuf, strlen(sendBuf)+1, 0);
//接受数据
/*intrecv( The Windows Socketsrecv function receives data from a connected socket.
SOCKETs, //建立连接的套接字
charFAR* buf, //接受数据的buff
intlen, //buff的长度
intflags //设定接受函数的调用行为
);*/
charrecvBuf[100];
recv(sockConn,recvBuf, strlen(recvBuf) + 1, 0);
printf("%s\n",recvBuf);
closesocket(sockConn);
}
}
1.2 TCP客户端
#include <winsock2.h>
#include <stdio.h>
//#include <iostream>
#pragma comment(lib,"ws2_32.lib")
void main()
{
/******** 首先设定通信版本 **************/
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested= MAKEWORD(1, 1); //请求的通信版本为(1,1)
err= WSAStartup(wVersionRequested, &wsaData); //调用WSAStartup函数
if(err != 0)
return; //如果返回值不为0,表示没有对应的WinSockDLL.则返回
if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //如果版本的高字节不为1,低字节不为1,则返回。
return;
}
//1创建套接字
SOCKETsockClient= socket(AF_INET, SOCK_STREAM, 0); //(1地址族AF_INET 或 PF_INET,2socket类型SOCK_STREAM或 SOCK_DGRAM,3与特定的地址家族相关的协议) 调用成功返回新的socket数据类型的套接字描述符
/*intconnect( //The Windows Socketsconnect function establishes a connection to a specifed socket.
SOCKETs, //套接字
conststruct sockaddr FAR* name, //地址结构体指针,用来设定链接的服务器地址信息
intnamelen //地址结构体的长度
);*/
//2连接服务器
SOCKADDR_INaddrSrv; //SOCKADDR_IN是一个结构体变量
addrSrv.sin_addr.S_un.S_addr= inet_addr("127.0.0.1"); //addrSrv是一个结构体,里面有一个结构体变量sin_addr,里面包含共用体S_un,然后是共用体成员S_addr,inet_addr函数进行主机字节序转换为网络字节序
addrSrv.sin_family= AF_INET;
addrSrv.sin_port= htons(6000); //主机字节序转换为网络字节序
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
//3和服务器端口进行通信(接受服务器发送的信息)
charrecvBuf[100];
recv(sockClient,recvBuf, strlen(recvBuf) + 1, 0);
printf("%s\n",recvBuf);
send(sockClient,"This is zhangsan", strlen("This is zhangsan") + 1, 0);
//4关闭套接字
closesocket(sockClient);
WSACleanup(); //终止对套接字库的使用
system("pause"); //用于防止闪退
}
2. 基于UDP的socket编程
2.1 UDP服务器
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
/******** 首先设定通信版本 **************/
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested= MAKEWORD(1, 1); //请求的通信版本为(1,1)
err= WSAStartup(wVersionRequested, &wsaData); //调用WSAStartup函数
if(err != 0)
return; //如果返回值不为0,表示没有对应的WinSockDLL.则返回
if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //如果版本的高字节不为1,低字节不为1,则返回。
return;
}
//1创建套接字
SOCKETsockSrv = socket(AF_INET, SOCK_DGRAM, 0); //(1地址族AF_INET 或 PF_INET,2socket类型SOCK_STREAM或 SOCK_DGRAM,3与特定的地址家族相关的协议) 调用成功返回新的socket数据类型的套接字描述符
//2绑定套接字到本地地址和端口
SOCKADDR_INaddrSrv;
addrSrv.sin_addr.S_un.S_addr= htonl(INADDR_ANY); //调用htonl函数进行转换主机字节序为TCP/IP网络字节序
addrSrv.sin_family= AF_INET;
addrSrv.sin_port= htons(6000); //端口号为6000,1024以上端口号,需要网络字节序,需要转换,htonl和htons不同在于转换数据的范围
bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); //绑定函数 (1指定要绑定的套接字,2该套接字的本地地址信息,是指向sockaddr结构的指针变量,3指定该地址的长度)
/*intrecvfrom( The Windows Socketsrecvfrom function receives a datagram and stores the source address.
SOCKETs, //套接字
charFAR* buf, //接收数据的buffer
int len, //buffer的长度
int flags, //设定recvfrom函数的调用行为
structsockaddr FAR* from, //地址结构体指针,接收发送数据方的地址信息
intFAR* fromlen //函数返回值,返回地址结构的大小
);*/
//3等待接收数据recvfrom
SOCKADDR_INaddrClient; //定义一个地址结构的变量,用来接受客户端的地址信息,在循环结构中用到
intlen = sizeof(SOCKADDR);
charrecvBuf[100];
recvfrom(sockSrv,recvBuf, strlen(recvBuf) + 1, 0, (SOCKADDR*)&addrClient, &len);
printf("%s\n",recvBuf);
//4关闭套接字
closesocket(sockSrv);
WSACleanup(); //终止对套接字库的使用
system("pause"); //用于防止闪退
}
2.2 UDP客户端
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
/******** 首先设定通信版本 **************/
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested= MAKEWORD(1, 1); //请求的通信版本为(1,1)
err= WSAStartup(wVersionRequested, &wsaData); //调用WSAStartup函数
if(err != 0)
return; //如果返回值不为0,表示没有对应的WinSockDLL.则返回
if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //如果版本的高字节不为1,低字节不为1,则返回。
return;
}
//1创建套接字
SOCKETsockClient = socket(AF_INET, SOCK_DGRAM, 0); //(1地址族AF_INET 或 PF_INET,2socket类型SOCK_STREAM或 SOCK_DGRAM,3与特定的地址家族相关的协议) 调用成功返回新的socket数据类型的套接字描述符
/*intsendto( The Windows Sockets sendtofunction sends data to a specific destination.
SOCKETs, //套接字
constchar FAR * buf, //包含将要发送的数据
int len, //数据的长度
intflags, //设置sendto函数调用行为
conststruct sockaddr FAR * to, //地址结构体指针,设定目的套接字的地址信息
inttolen //地址结构体的长度
);*/
//2向服务器发送数据(sendto)
SOCKADDR_INaddrSrv;
addrSrv.sin_addr.S_un.S_addr= inet_addr("127.0.0.1");
addrSrv.sin_family= AF_INET;
addrSrv.sin_port= htons(6000);
sendto(sockClient,"Hello", strlen("Hello") + 1, 0, (SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
//3关闭套接字
closesocket(sockClient);
WSACleanup(); //终止对套接字库的使用
system("pause"); //用于防止闪退
}