SERVER端
代码:
#include <Winsock2.h>
#include <iostream>
using namespace std;
void main()
{
/*
1.加载套接字库函数WSAstartup ,
加载套接字库,进行套接字库的版本协商。
有两个参数,第一个是WORD,用来指定版本号。
第二个参数是返回值,指向WSAData结构体的指针,接收windows sockets实现的详细的细节
*/
WORD wVersionRequested;//定义WORD类型变量
WSADATA wsaData;//WORD类型,用于指定准备加载的winsock库的版本,可用MAKEWORD(x,y)方便的获得wVersionRequested的正确值。x高字节副版本,y低字节主版本
int err;
/*
补充:
WSADATA结构体定义如下:`在这里插入代码片`
typedef struct WSAData{
WORD wVersion;//低字节主版本
WORD wHighVersion;//高字节副版本
char szDescription [WSADECRIPTION_LEN+1];//没用
char szSystemStatus[WSASYS_STATUS_LEN+1];//没用
unsigned short iMaxSockets;//不可使用
unsigned short iMaxLdpDg;//不可使用
char FAR*lpVendorInfo;//没有用到
}WSADATA,*LPWSADATA;
对于每一个WSAStartup的成功调用(成功加载winsock.DLL后),在最后都对应一个WSACleanup调用,以便释放为该应用程序分配的资源。
*/
wVersionRequested=MAKEWORD(1,1);//MAKEWORD宏,请求1.1版本的winsock库
err=WSAStartup(wVersionRequested,&wsaData);//&wsaData指向WSADATA的指针,WSAStartup用其加载的库版本的相关信息填写在这个结构中。(后来程序出错,1.1版本不行了,换成2.2可以)
if(err!=0){
return;
}
//判断返回的wVerion值低字节是否为1和高字节是否为1,如果不是请求的winsock 1.1版本,程序调用WSACleanup(),终止对winsock库的使用,然后返回。
if(LOBYTE(wsaData.wVersion)!=1||
HIBYTE(wsaData.wHighVersion)!=1){
WSACleanup();
return;
}
/*2.创建套接字
SOCKET socket(int af,int type,int protocol);
socket类型返回套接字的描述符,这个类型实际上指定的是一个整型。
该函数接收三个参数。
af:指定地址族,对于TCP/IP协议的套接字,它只能是AF_INET(也可以写成PF_INET).
type:socket类型,对于1.1版本的socket,只支持两种类型的套接字:SOCK_STREAM 指定产生流式套接字
SOCK_RGRAM产生数据报套接字
protocol:与特定的地址家族相关的协议,如果指定为0,那么它就会根据地址格式和套接字类别,自动为你选一个合适的协议。
返回值:函数调用成功,返回一个新的SOCKET数据类型的套接字描述符。
失败,返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。
*/
SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);//定义一个变量接收返回的描述符
/*3.绑定本地地址和端口上
int bind(
SOCKET s,
const struct sockaddr FAR * name,
int namelen
);
将一个地址和一个套接字关联起来。
3个参数
s:指定要绑定的套接字。
*name:该套接字的本地地址信息,指向sockaddr结构的指针变量。
namelen:指定该地址结构的长度。
补充:
struct sockaddr{
u_short sa_family;//指定地址家族 AF_INET
char sa_data[14];//要求一块内存分配区,占位,区域并指定与协议相关的具体地址信息。
};
在TCP/IP中,可以使用sockaddr_in结构,替换sockaddr,以方便我们填写地址信息。
tips:
如果我们只想让套接字使用多个IP中的一个地址,就必须指定实际地址,
要做到这一点,可以使用inet_addr()函数,
这个函数需要一个字符串作为其参数,该字符串制定了以点分十进制格式表示的IP地址如(192.168.0.15),有返回值。
*/
SOCKADDR_IN addrSrv;//定义一个变量,地址结构体的变量。
/*
u_long htonl(
u_long hostlong
);//将u_long类型从主机字节序到TCP/IP网络字节序的转换。
u_=short htonl(
u_short hostshort
);//将u_short类型从主机字节序到TCP/IP网络字节序的转换。
*/
//对地址结构体的成员变量进行赋值,需要u_long类型。
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//将IP地址设置为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。
addrSrv.sin_family=AF_INET;//地址族
addrSrv.sin_port=htons(6000);//2个字节的用u_short类型的,注意端口选择,1024以上的端口
//绑定本地地址和端口上。
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//(套接字,需要指针 取地址 强制类型转换,指定地址结构大小)
//4.调用listen函数,将套接字设置为监听模式。
listen(sockSrv,5);
SOCKADDR_IN addrClient;
int len = sizeof(SOCKADDR);
//做一个死循环,作为服务器端程序来说,它需要不断的等待客户端连接请求的到来
while(1)
{
/*
SOCKET accept(
SOCKET s,//描述符
struct sockaddr FAR * addr,//指向buff的指针,用来接收连接实体的地址,
//即当客户端向服务端发起连接时,在接收连接时,通过这个参数保存发起连接的客户端的IP地址信息和端口信息
int FAR *addrlen //返回值,指向一个整型的指针,用来包含所返回的地址字节的长度。
);
int send(
SOCKET s;//套接字
const char FAR *buf,//包含了将要传送的数据
int len,//buf中数据长度
int flags //会影响send的调用行为,设为0即可。
);
*/
//调用accept函数以等待客户端连接,并接收客户端连接请求。
SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);//(监听套接字,结构体指针,指针 len的地址)
char sendBuf[100]=”你好,我是服务端。”;//定义一个字符数组
/*C语言的写法:
// 将一个数据格式化到Buf当中
sprintf(sendBuf,"Welcome %s to http://www.baidu.com",
inet_ntoa(addrClient.sin_addr));//inet_ntoa接收in_adrin类型的参数,返回IP地址。*/
//发送(套接字,数据,数据长度 +1是为了在接收端增加一个‘/0'结尾,0)
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
char recvBuf[100];//从客户端接收数据
//接收(套接字,数据,0)
recv(sockConn,recvBuf,100,0);
//将接收的数据打印出来
cout<<recvBuf<<endl;//printf("%s\n",recvBuf);
//完成后,调用函数关闭套接字释放资源(死循环),如果不是死循环,在后面要关闭监听套接字:调用WSAcleanup()终止winsock库使用。
closesocket(sockConn);
}
}
/*
注:
在程序中使用了winsock库的函数,要包含一个文件:Winsock2.h
还要链接一个库文件:ws2_32.lib
这是使用动态链接库需要做的事情。
在开始:
#include <Winsock2.h>
链接库文件:project->project settings->Link->object/library modules:最后输入 ws2_32.lib注意格式
*/
client端
增加新的工程到工作区
选择 Win32 Console Application
位置和TCPSRV平级
工程名:TcpClient
选择工程,现在有两个工程,注意工作区的主工程。
添加一个源文件tcpClient
代码:
#include <Winsock2.h>
#include <iostream>
using namespace std;
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested=MAKEWORD(2,2);
err=WSAStartup(wVersionRequested,&wsaData);
if(err!=0){
return;
}
if(LOBYTE(wsaData.wVersion)!=2||
HIBYTE(wsaData.wHighVersion)!=2){
WSACleanup();
return;
}
//以上部分和服务器端一模一样,不再赘述。
//接下来创建套接字,参数:指定地址族,类型 基于TCP的都采用流式套接字,第三个参数为0
SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
/*
对于客户端来说,他不需要去绑定,可以直接去连接服务器端,使用connect函数。
int connect(
SOCKET s, // 套接字
const struct sockaddr FAR * name, //地址结构体指针,用来设定你所连接的服务器的地址信息
int namelen // 地址结构体的长度
);
*/
//首先定义一个地址结构体的变量
SOCKADDR_IN addrSrv;
//接着,对它的成员进行赋值
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//注意S_addr是u_long类型,将点分十进制表示的IP地址的字符串转换成u_long类型,使用inet_addr.
/*地址设置成127.0.0.1 本地回路地址,即不管你的机器上有没有网卡,都可以用这个IP地址。有时在自己计算机上编写程序,可以采用127.0.0.1这个IP做一些测试。如果你编写的程序要在两台计算机上通信,这个地方则填写该服务器的IP地址。*/
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);//端口设为服务器应用程序在哪一个端口等待连接,与服务器保持一致。
//调用connect进行连接,参数:套接字,地址需要转换,长度
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
//当我们建立连接后就可以接受服务器端发过来的数据
char recvBuf[100];//定义一个字符数组
//调用recv接收数据
recv(sockClient,recvBuf,100,0);
//将我们收到的数据打印出来
cout<<recvBuf<<endl;//printf("%s\n",recvBuf);
//向服务器端发送数据,参数:注意长度多加1
send(sockClient,"This is 喵喵客户端",strlen("This is 喵喵客户端")+1,0);
//当通信完成后,需要调用close
closesocket(sockClient);
//终止对套接字的使用(非死循环)
WSACleanup();
}
最后注意先启动server端,再启动client端
测试结果:
设置启动顺序:
启动server:
设置启动顺序:
启动client,并查看客户端:
查看服务端:
客户端可以多次启动:
为了方便查看,下面是纯代码:
server:
#include <Winsock2.h>
#include <iostream>
using namespace std;
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested=MAKEWORD(2,2);
err=WSAStartup(wVersionRequested,&wsaData);
if(err!=0){
return;
}
if(LOBYTE(wsaData.wVersion)!=2||
HIBYTE(wsaData.wHighVersion)!=2){
WSACleanup();
return;
}
SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
listen(sockSrv,5);
SOCKADDR_IN addrClient;
int len = sizeof(SOCKADDR);
while(1)
{
SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);
char sendBuf[100]="你好,我是服务端";
//sprintf(sendBuf,"Welcome %s to http://www.baidu.com",
//inet_ntoa(addrClient.sin_addr));
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
char recvBuf[100];
recv(sockConn,recvBuf,100,0);
cout<<recvBuf<<endl;
closesocket(sockConn);
}
}
client:
#include <Winsock2.h>
#include <iostream>
using namespace std;
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested=MAKEWORD(2,2);
err=WSAStartup(wVersionRequested,&wsaData);
if(err!=0){
return;
}
if(LOBYTE(wsaData.wVersion)!=2||
HIBYTE(wsaData.wHighVersion)!=2){
WSACleanup();
return;
}
SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
char recvBuf[100];
recv(sockClient,recvBuf,100,0);
cout<<recvBuf<<endl;//printf("%s\n",recvBuf);
send(sockClient,"This is 喵喵客户端",strlen("This is 喵喵客户端")+1,0);
closesocket(sockClient);
WSACleanup();
}