1.先总结一些会用到的知识
IP地址:IPV4中计算机都分配一个地址,该地址是一个32位的数值表示;客户端和服务器通信的时候需要指定一个IP地址和端口。IP地址一般是用“Internet标准点分表示法” 像a.b.c.d一样指定的,每一个字母代表一个字节的数字(十进制、八进制或十六进制)从左到右分配了一个无符号长整数的4个字节。unsigned long inet_addr(_In_ const char *cp) :把一个点分IP地址转换成一个32位的无符号长整数。网络字节序:TCP/IP协议中规定好的数据表示方法,顺序采用big endian排序方式;
主机字节序:主机表示数据的方法,不同的操作系统字节序不同,有big endian,有的是little endian ;若是主机字节序与网络字节序不同,则需要转换成一致的字节序;转换字节序用到的函数:
网络字节序——主机字节序 主机字节序——网络字节序ntohl htonl WSANtohl WSAHtonl
ntohs htons
WSANtohs WSAHtons
SOCKADDR_IN结构: 用来指定IP地址和服务端口信息。
struct sockaddr_in
{
short sin_family; // 协议族 一般为AF_INET
u_short sin_port; // 端口号,必须使用网络字节序
struct in_addr sin_addr; //IP地址信息
char sin_zero[8]; //为了使sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节
}
2.面向连接的TCP协议编程1)服务器端程序
#include<iostream> #include<WinSock2.h> #include<stdio.h> #pragma comment(lib,"Ws2_32")//添加库文件 using namespace std; int main() { //1.创建套接字(socket) WORD version =MAKEWORD(2,2);//先确定使用套接字的版本 WSADATA wdata; int erro =WSAStartup(version,&wdata);//启动套接字 if(erro!=0) { cout<<"套接字启动失败!"<<endl; } if((LOBYTE(wdata.wVersion)!=2)||(HIBYTE(wdata.wVersion)!=2))//如果版本不对,清除当前的套接字 { WSACleanup(); } SOCKET sockSer=socket(AF_INET,SOCK_STREAM,0);//指定协议族、套接字类型(流式套接字)、协议初始化 //2.将套接字绑定到一个本地地址和端口上(bind) //指定服务器IP SOCKADDR_IN Servaddr; Servaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//IP Servaddr.sin_family=AF_INET;//协议族,必须是AF_INET Servaddr.sin_port=htons(6000);//IP端口 bind(sockSer,(sockaddr*)&Servaddr,sizeof(sockaddr));//将socket与本地的一个IP关联 //3.将套接字设置成监听模式,准备接收客户的请求(listen) listen(sockSer,10); //4.等待客户请求到来,当请求到来后,接收连接请求,返回一个新的对应于此次连接的套接字(accept) SOCKADDR_IN clientadd; int len = sizeof(sockaddr); while(1) { Sleep(10000); char sendbuf[100]; SOCKET sockCon =accept(sockSer,(sockaddr*)&clientadd,&len);//返回一个新的对应于此次连接的套接字,将地址和端口空出来 //5.用返回的套接字和客户端进行通信(send/recv) sprintf(sendbuf,"This is: Server!"); send(sockCon,sendbuf,strlen(sendbuf)+1,0);//发送用连接的套接字,不能用正在监听的那个套接字 char recvbuf[100]; recv(sockCon,recvbuf,100,0); cout<<"Server Receive :"<<recvbuf<<endl; closesocket(sockCon); } cout<<"a: "<<endl; system("pause"); return 0; }
2.客户端的程序
#include<WinSock2.h>
#include<iostream>
#pragma comment(lib,"Ws2_32")
using namespace std;
int main()
{
//客户端
//1.创建套接字
WSADATA wsd;
wsd.wVersion=MAKEWORD(2,2);//确定套接字的版本
int erro;
erro=WSAStartup(wsd.wVersion,&wsd);
if(erro!=0)
{
cout<<"套接字启动失败!"<<endl;
}
if((LOBYTE(wsd.wVersion)!=2)||(HIBYTE(wsd.wVersion)!=2))
{
cout<<"套接字版本错误!"<<endl;
WSACleanup();
}
SOCKET Clientsocket = socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN Servadd;
Servadd.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
Servadd.sin_family=AF_INET;
Servadd.sin_port=htons(6000);
//2.向服务器发送连接请求(connect)
connect(Clientsocket,(sockaddr*)&Servadd,sizeof(sockaddr));//套接字,服务器IP地址,地址结构体的长度
//3.与服务器通信(send/recv)
char sendbuf[100],recvbuf[100];
while(1)
{
Sleep(10000);
recv(Clientsocket,recvbuf,100,0);
cout<<"receive :"<<recvbuf<<endl;
send( Clientsocket,"this is client",strlen("this is client")+1,0);
closesocket(Clientsocket);
// WSACleanup();
}
system("pause");
return 0;
}
以上是使用TCP协议的简单编程,TCP是面向连接的通信,是流协议,UDP是无连接通信,几乎是基于消息的,因此在接收函数的调用必须提供一个足够大的缓冲空间,如果缓冲区不够大,接收调用会失败,出现错误WSAEMSGSIZE,缓冲区填满,未接收完的数据会丢弃。
//Udp服务器
#include<WinSock2.h>
#include<iostream>
#pragma comment(lib,"Ws2_32")
using namespace std;
int main()
{
//服务器端
//1.创建套接字
DWORD wversion;
wversion =MAKEWORD(2,2);
WSADATA wsa;
wsa.wVersion=wversion;
int erro=0;
erro= WSAStartup(wversion,&wsa);
if(erro!=0)
{
cout<<"套接字启动失败!"<<endl;
}
if((LOBYTE(wsa.wVersion)!=2)||(HIBYTE(wsa.wVersion)!=2))
{
cout<<"套接字版本错误!"<<endl;
WSACleanup();
}
SOCKET socket_uServer = socket(AF_INET,SOCK_DGRAM,0);//协议族、格式、初始化
//2.绑定套接字
SOCKADDR_IN SockAddr,sendaddr;
SockAddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
SockAddr.sin_family=AF_INET;
SockAddr.sin_port=htons(7000);
int len = sizeof(sockaddr);
bind(socket_uServer,(sockaddr*)&SockAddr,len);
//3.等待数据连接
char recvbuf[100];
int sendlen= sizeof(sendaddr);
recvfrom(socket_uServer,recvbuf,100,0,(sockaddr*)&sendaddr,&sendlen);
cout<<"recv : "<<recvbuf<<endl;
cout<<"the IP is"<<inet_ntoa(sendaddr.sin_addr)<<endl;
char sendbuf[]="I received your message \0";
sendto(socket_uServer,sendbuf,strlen(sendbuf),0,(sockaddr*)&sendaddr,sizeof(sendaddr));
closesocket(socket_uServer);
WSACleanup();
system("pause");
return 0;
}
UDP客户端
#include<WinSock2.h>
#include<iostream>
#pragma comment(lib,"Ws2_32")
using namespace std;
int main()
{
//服务器端
//1.创建套接字
DWORD wversion;
wversion =MAKEWORD(2,2);
WSADATA wsa;
wsa.wVersion=wversion;
int erro=0;
erro= WSAStartup(wversion,&wsa);
if(erro!=0)
{
cout<<"套接字启动失败!"<<endl;
}
if((LOBYTE(wsa.wVersion)!=2)||(HIBYTE(wsa.wVersion)!=2))
{
cout<<"套接字版本错误!"<<endl;
WSACleanup();
}
SOCKET socket_uClient = socket(AF_INET,SOCK_DGRAM,0);//协议族、格式、初始化
//2.发送数据
SOCKADDR_IN SockAddr,sendaddr;
SockAddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
SockAddr.sin_family=AF_INET;
SockAddr.sin_port=htons(7000);
int len = sizeof(sockaddr);
char sendbuf[]="I received ";
sendto(socket_uClient,sendbuf,strlen(sendbuf)+1,0,(sockaddr*)&SockAddr,sizeof(SockAddr));
char recvserbuf[100];
recvfrom(socket_uClient,recvserbuf,100,0,(sockaddr*)&SockAddr,&len);
cout<<"recv from server: "<<recvserbuf<<endl;
closesocket(socket_uClient);
WSACleanup();
system("pause");
return 0;
}
这些是最基础,最简单的编程,实际应用中要更加复杂,慢慢 总结。