c/s模型,可以实现最简单的客户端与服务器之间的通讯,虽然这个模型无法实际使用,但是可以学习函数的调用及使用
下面代码可以实现客户端与服务器的一问一答的通讯
服务器
/*windows socket 第二版本的网络库*/
#include <WinSock2.h>
/*添加库文件 Windows socket 库就是编译好的二进制代码,使用时不需要再次编译*/
#pragma comment(lib,"ws2_32.lib")
#include <stdio.h>
#include <string.h>
int main(void)
{
WORD wdVersion = MAKEWORD(2, 2);
WSADATA wdSocMsg;
/*
* WSAStartup()
* 启动网络库,Windows socket Asynchronous异步
* 参数1 网络库版本 参数2 系统返回的库信息
* 系统返回的库信息:1、我们要使用的版本 2、系统能提供给我们的最高版本 3、4、5、2.0版本后不再使用
*/
int nRes = WSAStartup(wdVersion,&wdSocMsg);
if (0 != nRes)
{
switch (nRes)
{
case WSASYSNOTREADY:
printf("重启电脑试试,或者检查网络库");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库");
break;
case WSAEINPROGRESS:
printf("请重新打开软件");
break;
case WSAEPROCLIM:
printf("请关闭不必要的资源,为当前程序腾出更多控件");
break;
case WSAEFAULT:
printf("第二个参数输入错误");
break;
}
return 0;
}
/*检验版本*/
if (2 != HIBYTE(wdSocMsg.wVersion) || 2 != LOBYTE(wdSocMsg.wVersion))
{
//版本不对
/*关闭网络库*/
WSACleanup();
return 0;
}
/*
* socket()
* 本质就是一个uint,这个数是唯一的
* 参数1 地址的类型 参数2 套接字的类型 参数3 协议的类型
* 参数1:AF_INET_2 ipv4 192.168.xxx.xxx/AF_BTH_23 ipv6 /AF_BTH_32 蓝牙 6B:2D:BC:A9:8C:12
* 参数2:SOCK_STREAM 1一种套接字类型,提供带有OOB数据传输机制的顺序,可靠,双向,顺序,使用的传输协议为TCP(邮件)
* 参数2:SOCK_DGRAM 2 UDP 不可靠的,无顺序,效率高, (电影)
* 参数3:IPPROTO_TCP IPPROTO_UDP IPPROTO_ICMP 0的话系统默认选
*/
SOCKET socketServer = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (INVALID_SOCKET == socketServer)
{
/*获取socket错误码*/
int errCode = WSAGetLastError();
/*失败时,清理网路库*/
WSACleanup();
return 0;
}
/*
* bind()
* 给socket绑定端口号与具体地址 地址如192.168.x.x 端口号找到对应的软件,
* 如QQ,浏览器等,都对应自己的端口号,同一个软件可能对应多个端口号,用于不同功能,每种通讯的端口号是唯一的
* 参数1 socket
* 参数2 结构体 sockaddr
* 参数3 结构体大小
*/
struct sockaddr_in si;
si.sin_family = AF_INET; //地址类型
/*端口号,本质就是一个整数,0—65535,0-1023是系统保留占用端口号,通过cmd netstat -ano查看系统已使用的端口号 ,通过netstat -ao|findstr "12345"来查看12345端口有没有被占用*/
si.sin_port = htons(12345);
/*IP地址 如127.0.0.1本地回传地址,用于网路测试,*/
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int bres = bind(socketServer,(const struct sockaddr *)&si,sizeof(si));
if (SOCKET_ERROR == bres)
{
//获取错误码
int a = WSAGetLastError();
/*关闭socket*/
closesocket(socketServer);
/*清理网路库*/
WSACleanup();
}
/*
* listen()
* 将套接字置于正在监听传入连接的状态,就是调用这个函数后,客户端就可以通过服务器的socket连接服务器,
* 就是将socket启动,服务器进入可侦听状态
* 参数1 服务器端的socket
* 参数2 挂起连接队列的最大长度,如果服务器同时能处理10个连接,对于超出10个的连接放入队列,此参数指示队列的长度
* WSAAPI前缀,调用约定,决定了函数的编译方式,决定参数的入栈顺序,决定函数的调用时间,给操作系统来看的
*/
int lisState = listen(socketServer,SOMAXCONN);//SOMAXCONN 让系统决定等待队列的长度
if (lisState == SOCKET_ERROR)
{
/*关闭socket*/
closesocket(socketServer);
/*清理网路库*/
WSACleanup();
}
/*
* accept() //有多少客户端连接,就要循环多少次,不能多,否则就会等待
* 将每个客户端信息创建成socket
* 参数1 服务器的socket
* 参数2 结构体
* 参数3 参数2的大小
*/
struct sockaddr_in clientMsg;
int len = sizeof(clientMsg);
SOCKET socketClient = accept(socketServer, (struct sockaddr *)&clientMsg, &len);
//getpeername(socketServer,(struct sockaddr *)&clientMsg, &len)可以随时获取客户端socket信息
if (INVALID_SOCKET == socketClient)
{
/*关闭socket*/
closesocket(socketServer);
/*清理网路库*/
WSACleanup();
printf("客户端链接失败");
return 0;
}
printf("客户端链接成功");
while (1)
{
/*
* recv()
* 如果客户端在线,则会等待其发送数据...
* 得到指定客户端发来的消息,本质是复制,从系统中的缓冲区(协议自动实现),存入数组中
* 参数1 客户端的socket
* 参数2 *buf 客户端消息的存储空间,数组不要超出1500字节,网络一次最大的传输单元就为1500字节
* 参数3 len 想要读取的字节数 <= buf长度
* 参数4 flag 数据的读取方式 0是正常逻辑,数据读出后,就会删除缓存区,MSG_PEEK,读出来不删除
* 参数4 MSG_OOB 带外数据 传输一段数据,再外带一个额外的特殊数据,会降低存储效率
* 参数4 MSG_WAITALL 直到缓冲区的字节数满足参数3所请求的字节数,才开始读取
* 返回值 读出的字节的大小
*
*/
char buf[1500] = { 0 };
int lens = 1024;
int res = recv(socketClient, buf, lens, 0);
if (0 == res)
{
printf("连接中断");
}
else if (SOCKET_ERROR == res)
{
//出错了
}
else
{
printf("%d %s\n", res, buf);
}
/*
* send()
* 向目标发送数据
* 将数据复制到系统的协议发送缓冲区,计算机伺机发送出去
* 参数1 目标的socket,每个客户端对应唯一的socket
* 参数2 要发送的数据
* 参数3 要字节数
* 参数4 0 正常发送 MSG_OOB 带外 MSG_DONTROUTE 指定数据不受路由限制
* 返回值 成功写入的字节数
*/
scanf("%s",buf);
send(socketClient, buf, strlen(buf), 0);
//if (SOCKET_ERROR)
}
//清理网络库
closesocket(socketServer);
closesocket(socketClient);
WSACleanup();
system("pause");
return 0;
}
客户端
/*windows socket 第二版本的网络库*/
#include <WinSock2.h>
/*添加库文件 Windows socket 库就是编译好的二进制代码,使用时不需要再次编译*/
#pragma comment(lib,"ws2_32.lib")
#include <stdio.h>
#include <string.h>
int main(void)
{
WORD wdVersion = MAKEWORD(2, 2);
WSADATA wdSocMsg;
/*
* WSAStartup()
* 启动网络库,Windows socket Asynchronous异步
* 参数1 网络库版本 参数2 系统返回的库信息
* 系统返回的库信息:1、我们要使用的版本 2、系统能提供给我们的最高版本 3、4、5、2.0版本后不再使用
*/
int nRes = WSAStartup(wdVersion, &wdSocMsg);
if (0 != nRes)
{
switch (nRes)
{
case WSASYSNOTREADY:
printf("重启电脑试试,或者检查网络库");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库");
break;
case WSAEINPROGRESS:
printf("请重新打开软件");
break;
case WSAEPROCLIM:
printf("请关闭不必要的资源,为当前程序腾出更多控件");
break;
case WSAEFAULT:
printf("第二个参数输入错误");
break;
}
return 0;
}
/*检验版本*/
if (2 != HIBYTE(wdSocMsg.wVersion) || 2 != LOBYTE(wdSocMsg.wVersion))
{
//版本不对
/*关闭网络库*/
WSACleanup();
return 0;
}
/*
* socket() 创建服务器的socket
* 本质就是一个uint,这个数是唯一的
* 参数1 地址的类型 参数2 套接字的类型 参数3 协议的类型
* 参数1:AF_INET_2 ipv4 192.168.xxx.xxx/AF_BTH_23 ipv6 /AF_BTH_32 蓝牙 6B:2D:BC:A9:8C:12
* 参数2:SOCK_STREAM 1一种套接字类型,提供带有OOB数据传输机制的顺序,可靠,双向,顺序,使用的传输协议为TCP(邮件)
* 参数2:SOCK_DGRAM 2 UDP 不可靠的,无顺序,效率高, (电影)
* 参数3:IPPROTO_TCP IPPROTO_UDP IPPROTO_ICMP 0的话系统默认选
*/
SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socketServer)
{
/*获取socket错误码*/
int errCode = WSAGetLastError();
/*失败时,清理网路库*/
WSACleanup();
return 0;
}
/*
* WSAAPIconnect() 连接到服务器并把服务器信息与服务器socket绑定到一起
* 参数1:服务器socket
* 参数2:服务器IP地址端口号结构体
* 参数3: 参数2结构体的大小
* 返回值:0成功
*/
struct sockaddr_in serverMsg;
serverMsg.sin_family = AF_INET;
serverMsg.sin_port = htons(12345);//服务器的端口号
serverMsg.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//服务器的ip
if (SOCKET_ERROR == connect(socketServer, (struct sockaddr*)&serverMsg, sizeof(serverMsg)))
{
/*获取socket错误码*/
int errCode = WSAGetLastError();
/*失败时,清理网路库*/
WSACleanup();
return 0;
}
while (1)
{
/*
* send()
* 向服务器发送消息
*
*/
char bufout[50] = { 0 };
scanf("%s", bufout);
send(socketServer, bufout, strlen(bufout), 0);
//if (SOCKET_ERROR)
/*
* recv() 从服务器收消息
*
*/
char buf[1500] = { 0 };
int len = 1499;
int res = recv(socketServer, buf, len, 0);
if (0 == res)
{
printf("连接中断");
}
else if (SOCKET_ERROR == res)
{
//出错了
}
else
{
printf("%d %s\n", res, buf);
}
}
//清理网络库
closesocket(socketServer);
WSACleanup();
system("pause");
return 0;
}