TCP 客户端 、服务端的实现
在很多工程中都需要用到信息的发送和接收,因此在网上收集了些资料,实现了简单的发送文字、文件、图片,以及BASE64编码。(TCP/IP,IP协议这里就不细说了,着重与功能的实现)
套接字基础
套接字是一种网络API,可以使用它开发网络程序。套接字接口提供一种进程间的通信方法,使得在相同或不同的主机上的进程能以相同的规范进行双向信息传送。进程通过调用套接字接口来实现相互间的通信,而套接字接口又利用下层的网络通信协议功能和系统调用实现实际的通信工作。
进程之间要进行通信,首先要调用网络编程接口,由套接字负者将进程接收和发送的请求信息通过下层的网络通信协议服务接口(TCP/IP)向上或向下交付,所以套接字接口是应用层到传输层的接口。
IPv4套接字地址结构
IPv4套接字地址结构通常也称为“网络套接字地址结构”,它的名字为sockaddr_in,其结构定义如下:
- sin_family: Internet地址族,在IPv4中是AF_INET
- sin_port:是端口号,以网络字节序存储
- sin_addr:是一个结构,该结构中的成员存储的才是IP地址
- sin_zero:暂时还未用,但总将它置零
TCP套接字编程
使用TCP套接字编程可以实现基于TCP/IP协议的面向连接的通信,分为服务器和客户端,主要实现过程如下图所示:
客户端
1.加载初始化套接字
WSADATA wsaData;//初始化套接字
char buff[2048];
char buffs2[100];
memset(buff, 0, sizeof(buff));
memset(buffs2, 0, sizeof(buffs2));
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)//第一个参数表示winsock的版本,本例使用的是winsock2.2版本
{
printf("初始化Winsock失败");
return 0;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;//代表TCP/IP协议族
// htons函数:将一个16位无符号短整型数据由主机排列方式转化为网络排列方式,htonl函数的作用恰好相反。
addrSrv.sin_port = htons(8888);//端口号
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//IP地址,sin_addr存储IP地址,使用in_addr这个数据结构
//创建套接字
/*
af:一个地址家族,通常为AF_INET
type:套接字类型,SOCK_STREAM表示创建面向流连接的套接字。为SOCK_DGRAM,表示创建面向无连接的数据包套接字。为SOCK_RAW,表示创建原始套接字
protocol:套接字所用协议,不指定可以设置为0
返回值就是一个socket
SOCKET socket(int af,int type,int protocol);
*/
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);//协议、套接字类型、
if (SOCKET_ERROR == sockClient){
printf("Socket() error:%d", WSAGetLastError());
return 0;
}
2.向服务器发出连接请求
/*
connect函数:客户端socket发送连接请求的函数,
第一个参数是客户端的socket,
第二个参数是一个结构体指针,里面包括连接主机的地址和ip,
第三个参数为缓冲区的长度。
*/
connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv));
3.向服务器发送数据
/*
第一个参数为socket,
第二个参数为接收数据缓冲区,
第三个参数为缓冲区的长度,
第四个参数为函数的调用方式
*/
send(sockClient, buffs, sizeof(buffs), 0);
4.接收数据
/*
recv函数:接收数据,
第一个参数为socket,
第二个参数为接收数据缓冲区,
第三个参数为缓冲区的长度,
第四个参数为函数的调用方式。
*/
recv(sockClient, buff, sizeof(buff), 0);
printf("%s\n", buff);
5.关闭套接字
closesocket(sockClient);
服务器端
1.创建用于监听的套接字
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(port); //1024以上的端口号
/**
* INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。 一般来说,在各个系统中均定义成为0值。
*/
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
2.将套接字绑定到指定的端口和地址
/*
第一个参数为socket,第二个参数是一个结构指针,它包含了端口和IP地址信息,
第三个参数表示缓冲区长度。需要说明的是,第二个参数在API中表示为:const struct sockaddr FAR*
*/
bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));
3.将socket设置为监听模式(服务端的socket特有)
/*
必须将服务端的socket设置为监听模式才能和服务端简历连接。
里面有两个参数,第一个参数为socket,第二个参数为等待连接最大队列的长度。
*/
listen(sockSrv, 10);
4.accept()函数,等待用户请求到来
/*
accept函数:服务端socket接收客户端的连接请求,连接成功,
则返回一个socket,该socket可以在服务端发送和接收数据。
第一个参数为socket,
第二个参数为包含客户端端口IP信息的sockaddr_in结构指针,
第三个参数为接收参数addr的长度。
*/
accept(sockSrv, (SOCKADDR *)&addrClient, &len);
5.与客户端信息交互recv(),sent()
recv(sockConn, recvBuf, sizeof(recvBuf), 0);
send(sockConn, sendbuf, sizeof(sendbuf), 0);
6.关闭套接字
closesocket(sockSrv);