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链接库。