socket,实现服务器和客户端对话
服务器:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string>
#include<WinSock2.h> //网络库用2.2版本 目前系统的最高版本
#pragma comment(lib,"Ws2_32.lib") //加载动态连接库 x32.x64都是用ws2_32.lib这一个版本
//上面为第二版 第一版头文件为WinSock.h 动态库为wsock32.lib
using namespace std;
int main()
{
/*调用的库版本 如果直接赋值2.1 则出错(WORD为短整型,2.1为浮点型)
用该宏去储存,转到内存MAKEWORD(2,1)为258 转换成2进制为0001 0000 0010
即将主版本号2,存放在低字节位(1个字节占8位),副版本号1,存放在高字节0001 <因为在内存中是小头方式存储 高位数据在前> */
WORD m_versions = MAKEWORD(2,2); //MAKEWORD(A,B)参数1为副版本号,参数2为主版本号
WSADATA m_data;//用于接受返回的socket信息
//***1*** 打开网络库
int answer = WSAStartup(m_versions, &m_data); //解析参数2 1为我们要使用的版本 2为系统能提供的最高版本 3为当前库的信息 4为打开结果 或当前套接字状态
if (answer != 0)//打开失败的结果
{
switch (answer)
{
case WSASYSNOTREADY:
printf("网络通信依赖的网络子系统还没有准备好。");
break;
case WSAVERNOTSUPPORTED:
printf("系统不支持当前所需库版本。");
break;
case WSAEINVAL:
printf("无法找到当前库版本所需DLL。");
break;
case WSAEPROCLIM:
printf("当前端口数量已达限制。");
break;
case WSAEINPROGRESS:
printf("当前初始化函数被阻塞。");
break;
case WSAEFAULT:
printf("当前指针(参数2)为无效指针。");
break;
}
}
if (m_data.wVersion != 514) //即未能打开2.2版本 2.2版本以单字节存储即为十进制的514
{//HIBYTE(m_data.wVersion)获取该成员的高字节(即高版本号) LOBYTE()获取该成员的低字节
WSACleanup(); //未能打开指定版本,则关闭该网络库
system("pause");
return 0;
}
//***2*** 创建服务器socket 每个返回的值都是唯一的 返回的值为TCP/IP协议的总编号,代表着各协议的特点 。
SOCKET m_ServerSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //类似于MFC的窗口句柄,对该端口进行操作都需使用该套接字值
//参数1地址类型(IPV4形式),2套接字类型(顺序,可靠,双向,基于连接的字节流),3协议的类型(TCP协议 与参数1对应)
if (m_ServerSocket == INVALID_SOCKET)//创建失败
{
int m_ErrorNum=WSAGetLastError();
printf("创建服务器socket失败,错误代码:%d\n", m_ErrorNum);
WSACleanup(); //未能打开指定版本,则关闭该网络库
system("pause");
return 0;
}
sockaddr_in m_ServerSocketaddr;//结构体,存储地址类型,地址和端口 (sockaddr_in与sockaddr结构一样,但前者方便赋值,后者方便作为参数传递)
m_ServerSocketaddr.sin_family = AF_INET;//与创建时的地址类型相同
m_ServerSocketaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//将字符串形式转换成IP地址形式存储,127.0.0.1为回环地址,在本地(不出电脑)测试使用
m_ServerSocketaddr.sin_port = htons(12345);//端口号 使用时应将其转换成主机字节顺序,且端口号是唯一的,不能重复绑定
//***3*** 绑定对应的地址(找到电脑)和端口号(找到具体应用)
int m_bind = bind(m_ServerSocket, (sockaddr *)&m_ServerSocketaddr, sizeof(m_ServerSocketaddr));
if (m_bind !=0)//创建失败
{
int m_ErrorNum = WSAGetLastError();
printf("绑定失败,错误代码:%d\n", m_ErrorNum);
closesocket(m_ServerSocket);
WSACleanup();
system("pause");
return 0;
}
//***4*** 启动监听,监听是否有客户端连接(类似启动服务器 让客户端可以进行连接)
int m_listen=listen(m_ServerSocket,5);//服务器的sockt。挂起连接队列最大长度(假设服务器能同时处理5个,现在进来了8个,剩下的进入队列等待处理)SOMAXCONN则让系统自行选择合适个数
if (m_listen != 0)//启动失败
{
int m_ErrorNum = WSAGetLastError();
printf("启动失败,错误代码:%d\n", m_ErrorNum);
closesocket(m_ServerSocket);
WSACleanup();
system("pause");
return 0;
}
sockaddr_in m_ClientSocketaddr;//同步骤3一致,为存放客户端数据的结构体
int m_ClientLen = sizeof(m_ClientSocketaddr);//结构体大小
//***5*** 接受连接。当有客户端连接进来后,服务器为其创建一个socket,用于维持通信.参数2.3也可设置为NULL 不得到客户端的信息
SOCKET m_ClientSocket = accept(m_ServerSocket, (sockaddr *)&m_ClientSocketaddr, &m_ClientLen);
//accept函数会一直处于阻塞状态(等待连接),不会往下执行 且一次只能连一个,多个客户端连接则需要循环,且数量要相对应(否则一直阻塞)
if (m_ClientSocket == INVALID_SOCKET)//返回的为无效socket
{
int m_ErrorNum = WSAGetLastError();
printf("客户端连接失败,错误代码:%d\n", m_ErrorNum);
closesocket(m_ServerSocket);
WSACleanup();
system("pause");
return 0;
}
printf("客户端连接成功\n");
int a = 5;
while (a!=0,a--)
{
//***6***接受客户端发送的数据
char StoCbuf[1500] = { 0 };//用于存储服务器接受客户端发来的字符
int a = sizeof(StoCbuf);
int m_ClientStrLen = recv(m_ClientSocket, StoCbuf, sizeof(StoCbuf), 0); //1接受来自哪的sockt,存放在哪,放多少,读取方式(0为默认) 也会一直处于阻塞状态,直到客户端对其操作
if (m_ClientStrLen == 0)//即客户端已下线
{
printf("连接中断,客户端已下线");
}
else if (m_ClientStrLen == SOCKET_ERROR)
{
int m_ErrorNum = WSAGetLastError();
printf("接受客户端数据失败,错误代码:%d\n", m_ErrorNum);
break;
}
else
{
printf("客户端:%s\n", StoCbuf);
}
//***7***给客户端发送数据
printf("服务器:");
gets(StoCbuf);
int m_ServerStrLen = send(m_ClientSocket, StoCbuf, strlen(StoCbuf), 0);//目的socket,发送的数据,发送数据的长度,发送方式
if (m_ServerStrLen == SOCKET_ERROR)
{
int m_ErrorNum = WSAGetLastError();
printf("服务器发送数据失败,错误代码:%d\n", m_ErrorNum);
}
if (strcmp(StoCbuf, "886") == 0)
{
printf("结束对话\n");
break;
}
//使用完毕后,先销毁创建的套接字(该函数为网络库里面的函数),再关闭网络库
closesocket(m_ServerSocket);
WSACleanup();
system("pause");
}
客户端:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
/*1打开网络库*/
WORD m_versions = MAKEWORD(2, 2); //MAKEWORD(A,B)参数1为副版本号,参数2为主版本号
WSADATA m_data;//用于接受返回的socket信息
int answer=WSAStartup(m_versions,&m_data);
if (answer != 0)//打开失败的结果
{
switch (answer)
{
case WSASYSNOTREADY:
printf("网络通信依赖的网络子系统还没有准备好。");
break;
case WSAVERNOTSUPPORTED:
printf("系统不支持当前所需库版本。");
break;
case WSAEINVAL:
printf("无法找到当前库版本所需DLL。");
break;
case WSAEPROCLIM:
printf("当前端口数量已达限制。");
break;
case WSAEINPROGRESS:
printf("当前初始化函数被阻塞。");
break;
case WSAEFAULT:
printf("当前指针(参数2)为无效指针。");
break;
}
return 0;
}
if (m_data.wVersion != 514) //即未能打开2.2版本 2.2版本以单字节存储即为十进制的514
{//HIBYTE(m_data.wVersion)获取该成员的高字节(即高版本号) LOBYTE()获取该成员的低字节
WSACleanup(); //未能打开指定版本,则关闭该网络库
system("pause");
return 0;
}
/*2创建服务器socket*/
SOCKET m_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //类似于MFC的窗口句柄,对该端口进行操作都需使用该套接字值
//参数1地址类型(IPV4形式),2套接字类型(顺序,可靠,双向,基于连接的字节流),3协议的类型(TCP协议 与参数1对应)
if (m_ServerSocket == INVALID_SOCKET)//创建失败
{
int m_ErrorNum = WSAGetLastError();
printf("创建服务器socket失败,错误代码:%d\n", m_ErrorNum);
WSACleanup(); //未能打开指定版本,则关闭该网络库
system("pause");
return 0;
}
/*3连接到服务器*/
sockaddr_in m_ServerMsg;//装载服务器信息
m_ServerMsg.sin_family = AF_INET;//地址类型
m_ServerMsg.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//服务器IP地址 将字符串形式转换成IP地址形式存储,127.0.0.1为回环地址,在本地(不出电脑)测试使用
m_ServerMsg.sin_port = htons(12345);//服务器的端口号 将网络字节序转换为主机字节序
int m_connect = connect(m_ServerSocket, (sockaddr*)&m_ServerMsg, sizeof(m_ServerMsg));
if (m_connect != 0)
{
int m_ErrorNum = WSAGetLastError();
printf("连接服务器失败,错误代码:%d\n", m_ErrorNum);
closesocket(m_ServerSocket);//关闭创建的socket;
WSACleanup(); //未能打开指定版本,则关闭该网络库
system("pause");
return 0;
}
printf("服务器连接成功\n");
while (1)
{
/*4发送消息给服务器*/
char CtoSbuf[1500] = { 0 };//客户端发送给服务器
printf("客户端:");
gets(CtoSbuf);
int m_ServerStrLen = send(m_ServerSocket, CtoSbuf, strlen(CtoSbuf), 0);//目的socket,发送的数据,发送数据的长度,发送方式
if (m_ServerStrLen == SOCKET_ERROR)
{
int m_ErrorNum = WSAGetLastError();
printf("客户端发送数据失败,错误代码:%d\n", m_ErrorNum);
}
/*5接受服务器发送的数据*/
char StoCbuf[1500] = { 0 };//用于存储服务器发来的字符
int m_ClientStrLen = recv(m_ServerSocket, StoCbuf, sizeof(StoCbuf), 0); //1接受来自哪的sockt,存放在哪,放多少,读取方式(0为默认) 也会一直处于阻塞状态,直到客户端对其操作
if (m_ClientStrLen == 0)//即客户端已下线
{
printf("连接中断,服务器已关闭");
}
else if (m_ClientStrLen == SOCKET_ERROR)
{
int m_ErrorNum = WSAGetLastError();
printf("接受客户端数据失败,错误代码:%d\n", m_ErrorNum);
break;
}
else
{
printf("服务器:%s\n",StoCbuf);
}
if (strcmp(StoCbuf, "886") == 0)
{
printf("结束对话\n");
break;
}
}
closesocket(m_ServerSocket);//关闭创建的socket;
WSACleanup(); //未能打开指定版本,则关闭该网络库
system("pause");
}
先运行服务器,再运行客户端,运行效果如下