孙鑫视频笔记——网络编程1(基础)

TCP服务器端:

 新建一个空的win32控制台项目,然后新建一个TcpSrv.cpp源文件,源文件内容如下:

 

#include <Winsock2.h> //包含socket的dll相关头文件
#include<stdio.h> //以下例子中用到C语言相关的库函数
void main()
{
	//1 加载套接字库dll
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested=MAKEWORD(1,1); //1.1分别表示主副版本号,将1.1转为Word类型的一个值

	err=WSAStartup(wVersionRequested, &wsaData);//初始化套接字dll,参数1指示版本,参数2输是输出参数,接收返回的描述信息
	if( err!= 0){
		return;
	}

	if(LOBYTE( wsaData.wVersion) != 1 ||  //分别取出低字节和高字节判断是不是需要的版本号
		HIBYTE( wsaData.wVersion) != 1 ){
		WSACleanup(); //清理套接字dll
		return;
	}

	//2 创建一个空的套接字
	SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0); //三个参数分别表示:地址族,流还是数据报类型,协议(0表示由系统选)

	//3 将空的套接字绑定到一个地址结构上(此结构包括3个信息:地址族,ip地址,端口号)
	//用到函数bind(空socket,一个地址结构SOCKADDR的指针,地址结构的大小),所以关键是建立一个地址结构,分以下3步
	//3.1 建立一个地址结构SOCKADDR_IN类型(简化了的地址结构,类似SOCKADDR,以后可以强转过去),不用考虑网络字节序
	SOCKADDR_IN addrSrv;
	//3.2 给该简化版地址结构的地址族成员赋值(所有成员前缀为sin_,由SOCKADDR_IN取两个单词首字母简化而来)
	addrSrv.sin_family= AF_INET;
	//3.3 给该简化版地址结构的ip地址成员赋值,但要注意,ip地址成员本身也是一个结构体类型,
	//该结构体里面包含一个联合类型成员,此成员名为S_un(由Struct的Union成员缩写而成,即父子组合)。
	//此联合成员里面又包含一个成员叫S_addr(缩写道理同上),它才最终指代一个ip地址。
	//另外,包中的数据如ip地址有一个网络字节序的问题,原因是有点电脑高位地址代表高位数据,其它的电脑可能相反
	//所以此时需要用htonl或htons函数将当前电脑的数据转换成网络字节序的数据(但地址族成员不用转换),
	//其中htonl表示将host主机字节序的u_long类型转成网络net字节序,而htons将host的u_shot(无符号短整形)转换为网络字节序
	//所以最终赋值写法如下:
	addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	//INADDR_ANY代表多网卡中任选一个ip地址,相当于192.168.xx.xx对应的无符号整形形式
	//3.4 给该简化版的地址结构(体)的端口号成员赋值,也要考虑网络字节序的问题
	addrSrv.sin_port=htons(6000);//用户自定义端口号要大于1024
	//3.5 将空套接字和上诉的地址结构(体)绑定(bind)起来
	//此时使用bind函数,注意地址类型强制转化的写法,最后一个参数表示地址结构体大小
	bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); 

	//4 将建立好的结构体套接字实体设置为监听状态
	listen(sockSrv,5);//参数2表示监听队列的长度

	//5 当监听到一个请求后,建立一个新的socket,由新的socket负责处理请求
	//注,以下两句是作为下面accept函数的输出参数,接收客户端的地址结构体信息数据用的
	SOCKADDR_IN addrClient; //简化版的地址结构体,含有地址族、ip地址、端口号信息
	int len=sizeof(SOCKADDR);

	while(1)
	{
		SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//后两个参数是输出参数,保存客户端ip地址、端口等信息
		//注意,accept可能处于阻塞状态,直到有客户端的请求,才继续向下执行
		char sendBuf[100];
		//下面用的sprintf函数,这里详细讲解一下
		//spintf字面意思是打印print一些东西到字符串string变量里头去并格式化format
		//而以前常用的pintf字面意思是打印一些东西到屏幕上去并格式化,省略了s表示屏幕
		sprintf(sendBuf,"Welcome %s to http://www.yg.cn",
			inet_ntoa(addrClient.sin_addr));//inet_ntoa表示讲网络用的ip地址转换为点分十进制的形式,并返回成字符串
		
		//测试发送函数,这里服务器接收请求后立即发送一个字符串给客户端
		send(sockConn,sendBuf,strlen(sendBuf)+1,0);//发送“欢迎192.x.x.x到www.yg.cn”给客户端,最后的参数是标记,一般为0
		//注意长度加1一般表示在最后加一个\0(实际上该字符串不满100,前面有好多\0,这里不追求严谨了),
		//“\0”是个转义字符,ascii码为0,常用来表示字符串的结尾
		//另外,要注意strlen是“Winsock2.h”库中的函数

		//测试接收函数,这里可能处于阻塞状态,直到客户端有数据传过来,才继续向下执行
		char recvBuf[100];
		recv(sockConn,recvBuf,100,0);//最后的参数是标记,表示接收行为,一般为0
		printf("%s\n",recvBuf); //输出客户端传来的字符串,pintf和spintf是C语言库“stdio.h”里的函数

		//关闭套接字sockConn
		closesocket(sockConn);
	}

	//退出while循环后,关闭sockSrv以及清理套接字dll即可,这里就略了
}


TCP客户端:

新建一个空的win32控制台项目,然后新建一个TcpClient.cpp源文件,源文件内容如下:

#include <Winsock2.h> //包含socket的dll相关头文件
#include<stdio.h> //以下例子中用到C语言相关的库函数
void main()
{
	//1 加载套接字库dll
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested=MAKEWORD(1,1); //1.1分别表示主副版本号,将1.1转为Word类型的一个值

	err=WSAStartup(wVersionRequested, &wsaData);//初始化套接字dll,参数1指示版本,参数2输是输出参数,接收返回的描述信息
	if( err!= 0){
		return;
	}

	if(LOBYTE( wsaData.wVersion) != 1 ||  //分别取出低字节和高字节判断是不是需要的版本号
		HIBYTE( wsaData.wVersion) != 1 ){
		WSACleanup(); //清理套接字dll
		return;
	}

	//2 创建一个空的套接字,在客户端,可以用这个空套接字直接去连服务器,在连的过程中系统会自动绑定相关端口等
	SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0); //三个参数分别表示:地址族,流还是数据报类型,协议(0表示由系统选)

	//3 用套接字去connect服务器
	//3.1 建立服务器地址结构(体),以便客户端知道连接到哪里
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//inet_addr将点分十进制地址转换为一个无符号整形表示形式,
	//inet_addr函数和inet_ntoa刚好相反,另外注意S_un成员是个联合类型union,它有一个ULONG类型的成员是S_addr
	//再补充一点,使用inet_addr返回的就是网络字节序的数据了,不用再用htonl手动转换了
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(6000);
	connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

	//测试接收,此时可以立即接收到服务器发来的“欢迎192.x.x.x到www.yg.cn”信息,注意实际上发的都是英文,用中文为了叙述方便。
	char recvBuf[100];
	recv(sockClient,recvBuf,100,0);
	printf("%s\n",recvBuf);
	
	//测试发送
	send(sockClient,"This is ygClient",strlen("This is ygClient")+1,0);

	closesocket(sockClient);//关闭套接字
	WSACleanup();//清理套接字dll

	//getchar();//VC++用这个暂停住dos界面,或用system("pause");后者好在显示一行“按任意键继续”
      system("pause");
}


 TCP运行结果:

服务器端:

This is ygClient

客户端:

Welcome 127.0.0.1 to http://www.yg.cn

请按任意键继续...

 

 

UDP服务器:

新建一个空的win32控制台项目,然后新建一个UdpSrv.cpp源文件,源文件内容如下:

#include <Winsock2.h> //包含socket的dll相关头文件
#include<stdio.h> //以下例子中用到C语言相关的库函数
void main()
{
	//1 加载套接字库dll
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested=MAKEWORD(1,1); //1.1分别表示主副版本号,将1.1转为Word类型的一个值

	err=WSAStartup(wVersionRequested, &wsaData);//初始化套接字dll,参数1指示版本,参数2输是输出参数,接收返回的描述信息
	if( err!= 0){
		return;
	}

	if(LOBYTE( wsaData.wVersion) != 1 ||  //分别取出低字节和高字节判断是不是需要的版本号
		HIBYTE( wsaData.wVersion) != 1 ){
		WSACleanup(); //清理套接字dll
		return;
	}

	//2 创建一个空的套接字,注意此时类型为基于udp协议的数据包类型,而不再是基于tcp协议的流类型
	SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0); //三个参数分别表示:地址族,流还是数据报类型,协议(0表示由系统选)

	//3 将空的套接字绑定到一个地址结构上(此结构包括3个信息:地址族,ip地址,端口号)  
    //用到函数bind(空socket,一个地址结构SOCKADDR的指针,地址结构的大小),所以关键是建立一个地址结构,分以下3步  
    //3.1 建立一个地址结构SOCKADDR_IN类型(简化了的地址结构,类似SOCKADDR,以后可以强转过去),不用考虑网络字节序  
   SOCKADDR_IN addrSrv;  
    //3.2 给该简化版地址结构的地址族成员赋值(所有成员前缀为sin_,由SOCKADDR_IN取两个单词首字母简化而来)  
   addrSrv.sin_family= AF_INET;  
   //3.3 给该简化版地址结构的ip地址成员赋值,但要注意,ip地址成员本身也是一个结构体类型,  
   //该结构体里面包含一个联合类型成员,此成员名为S_un(由Struct的Union成员缩写而成,即父子组合)。  
   //此联合成员里面又包含一个成员叫S_addr(缩写道理同上),它才最终指代一个ip地址。  
   //另外,包中的数据如ip地址有一个网络字节序的问题,原因是有点电脑高位地址代表高位数据,其它的电脑可能相反  
   //所以此时需要用htonl或htons函数将当前电脑的数据转换成网络字节序的数据(但地址族成员不用转换),  
    //其中htonl表示将host主机字节序的u_long类型转成网络net字节序,而htons将host的u_shot(无符号短整形)转换为网络字节序  
    //所以最终赋值写法如下:  
    addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);  
    //INADDR_ANY代表多网卡中任选一个ip地址,相当于192.168.xx.xx对应的无符号整形形式  
    //3.4 给该简化版的地址结构(体)的端口号成员赋值,也要考虑网络字节序的问题  
    addrSrv.sin_port=htons(6000);//用户自定义端口号要大于1024  
    //3.5 将空套接字和上诉的地址结构(体)绑定(bind)起来  
    //此时使用bind函数,注意地址类型强制转化的写法,最后一个参数表示地址结构体大小  
    bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));   
   

	//测试接收
	SOCKADDR_IN addrClient;
	int len=sizeof(SOCKADDR);
	char recvBuf[100];
	//recvfrom共需要6个参数,参数1指示从本地那个套接字上取数据,参数2和参数3分别指示了接收数据的缓存和缓存大小
	//参数4为标记,指示接收行为,一般为0,参数5和参数6为输出参数,记录远程主机的地址结构体信息
	recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len); //没收到数据前会阻塞住,直到收到数据后才继续往下执行
	printf("%s\n",recvBuf);

	closesocket(sockSrv);//关闭套接字
	WSACleanup();//清理套接字dll

	//getchar();//VC++用这个暂停住dos界面,或用system("pause");后者好在显示一行“按任意键继续”
	system("pause");
}


Udp客户端:

新建一个空的win32控制台项目,然后新建一个UdpClient.cpp源文件,源文件内容如下:

#include <Winsock2.h> //包含socket的dll相关头文件
#include<stdio.h> //以下例子中用到C语言相关的库函数
void main()
{
	//1 加载套接字库dll
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested=MAKEWORD(1,1); //1.1分别表示主副版本号,将1.1转为Word类型的一个值

	err=WSAStartup(wVersionRequested, &wsaData);//初始化套接字dll,参数1指示版本,参数2输是输出参数,接收返回的描述信息
	if( err!= 0){
		return;
	}

	if(LOBYTE( wsaData.wVersion) != 1 ||  //分别取出低字节和高字节判断是不是需要的版本号
		HIBYTE( wsaData.wVersion) != 1 ){
		WSACleanup(); //清理套接字dll
		return;
	}

	//2 创建一个空的套接字,在客户端,可以用这个空套接字直接去连服务器,在连的过程中系统会自动绑定相关端口等
	SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0); //三个参数分别表示:地址族,流还是数据报类型,协议(0表示由系统选)

	//3 用套接字直接给服务器发送数据即可
	//3.1 建立服务器地址结构(体),以便客户端知道连接到哪里
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//inet_addr将点分十进制地址转换为一个无符号整形表示形式,
	//inet_addr函数和inet_ntoa刚好相反,另外注意S_un成员是个联合类型union,它有一个ULONG类型的成员是S_addr
	//再补充一点,使用inet_addr返回的就是网络字节序的数据了,不用再用htonl手动转换了
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(6000);
	//3.2 直接给服务器发数据即可,最后两个参数指示了发送的地址信息
	sendto(sockClient,"Hello",strlen("Hello")+1,0,
		(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

	closesocket(sockClient);//关闭套接字
	WSACleanup();//清理套接字dll

	//getchar();//VC++用这个暂停住dos界面,或用system("pause");后者好在显示一行“按任意键继续”
	system("pause");
}


Udp运行结果:

服务器端:

Hello

请按任意键继续...

客户端:

请按任意键继续...

 

最后补充一点:所有项目都在vs2010中测试运行过,所有项目都需要在属性-连接器-附加依赖项,添加ws2_32.lib引入库即可运行。系统会自动在系统目录下寻找到相关的dll链接库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值