c++ Socket实现客户端与服务器数据传输

c++ Socket实现客户端与服务器数据传输

这是自己第二次写博客,希望在博客记录自己的学习过程,欢迎大家评论!

实现: 客户端往服务器端发送一条数据,服务器端接收数据并输出;
服务器端再发送出接收到的数据给客户端!

根据自己的脑洞,想一下是否可以通过的改进实现简单的QQ聊天室!

少说废话,上代码

[文字描述都写在注释啦,也就那么点,]
服务器实现

一般情况下,先实现服务端的代码,这样逻辑可能会更清晰点,主要还是看个人的吧!
希望不要直接copy哦,至少自己动手敲一敲.

#define _WINSOCK_DEPRECATED_NO_WARNINGS 	//比较新版的vs,会警告我们不要使用一
											//下旧的函数,因为提供更新更安全的函数供我们使用,在这呢我们
											//还是用旧的吧,这个宏定义就是起屏蔽警告作用,VS下面也有提示的
#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include<string>
#include<cstring>
#include<WS2tcpip.h>
#include <WinSock2.h>						//一般情况下,这个头文件位于windows.h之前,避免发生某些错误
#include<Windows.h>
#pragma comment(lib, "ws2_32.lib") 			//显示加载 ws2_32.dll	ws2_32.dll就是最新socket版本啦
using namespace std;

int main()
{
	cout << "-----------服务器-----------" << endl;

	//	1	初始化
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 2), &wsadata);	//make word,你把鼠标移到WSAStartup看看参数列表,是不是就是一个word啊


	//	2	创建服务器的套接字
	SOCKET serviceSocket;
	serviceSocket = socket(AF_INET, SOCK_STREAM, 0);	//socket(协议族,socket数据传输方式,某个协议)	我们默认为0,其实就是一个宏
	if (SOCKET_ERROR == serviceSocket) {	
		cout << "套接字闯创建失败!" << endl;
	}
	else {
		cout << "套接字创建成功!" << endl;
	}


	//	3	绑定套接字	指定绑定的IP地址和端口号
	sockaddr_in socketAddr;								//一个绑定地址:有IP地址,有端口号,有协议族
	socketAddr.sin_family = AF_INET;
	socketAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");		//代码开头第一行我们定义的宏在这就其作用啦
	socketAddr.sin_port = htons(1234);
	int bRes = bind(serviceSocket, (SOCKADDR*)&socketAddr, sizeof(SOCKADDR));	//绑定注意的一点就是记得强制类型转换
	if (SOCKET_ERROR == bRes) {
		cout << "绑定失败!" << endl;
	}
	else {
		cout << "绑定成功!" << endl;
	}

	//	4	服务器监听	
	int lLen = listen(serviceSocket, 5);	//监听的第二个参数就是:能存放多少个客户端请求,到并发编程的时候很有用哦
	if (SOCKET_ERROR == lLen) {
		cout << "监听失败!" << endl;
	}
	else {
		cout << "监听成功!" << endl;
	}


	//	5	接受请求
	sockaddr_in revClientAddr;
	SOCKET recvClientSocket = INVALID_SOCKET;	//初始化一个接受的客户端socket
	int _revSize = sizeof(sockaddr_in);
	recvClientSocket = accept(serviceSocket, (SOCKADDR*)&revClientAddr, &_revSize);
	if (INVALID_SOCKET == recvClientSocket) {
		cout << "服务端接受请求失败!" << endl;
	}
	else {
		cout << "服务端接受请求成功!" << endl;
	}


	//	6	发送/接受 数据
	char recvBuf[1024] = {};
	int reLen = recv(recvClientSocket, recvBuf, 1024, 0);
	int sLen = send(recvClientSocket, recvBuf, reLen, 0);
	if (SOCKET_ERROR == reLen) {
		cout << "服务端发送数据失败" << endl;
	}
	else {
		cout << "服务器接受到数据:    " << recvBuf << endl << endl;
	}
		

	//	7	关闭socket
	closesocket(recvClientSocket);
	closesocket(serviceSocket);

	//	8	终止
	WSACleanup();

	cout << "服务器停止" << endl;
	cin.get();
	return 0;
}

客户端代码实现

在这里再提醒一点,运行程序的时候,我们是先运行服务端程序,再运行客户端程序.
所以有些同学可能会说"我们的代码都一样啊!怎么到我这就不成功了呢?",这是一个
低级的错误,我们要避免.

还有就是我还要强调,敲代码,像我一样的初学者,咋不能直接copy,慢慢敲,不急!
像我写博客也一样,慢慢写,不慌.

乍一看,客户端跟服务器端代码差不多啊!

也确实是,而且比服务器端还简单,在这里我就不再一个个写注释啦,其实跟上面的差不多,
少废话,开始吧!

#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include<string>
#include<cstring>
#include<WS2tcpip.h>
#include <WinSock2.h>
#include<Windows.h>
#pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.dll
using namespace std;

int main()
{
	cout << "-----------客户端-----------" << endl;

	//	1	初始化
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 2), &wsadata);


	//	2	创建套接字
	SOCKET clientSocket = {};
	clientSocket = socket(PF_INET, SOCK_STREAM, 0);
	if (SOCKET_ERROR == clientSocket) {
		cout << "套接字闯创建失败!" << endl;
	}
	else {
		cout << "套接字创建成功!" << endl;
	}


	//	3	绑定套接字	指定绑定的IP地址和端口号
	sockaddr_in socketAddr;
	socketAddr.sin_family = PF_INET;
	socketAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	socketAddr.sin_port = htons(1234);
	int cRes = connect(clientSocket, (SOCKADDR*)&socketAddr, sizeof(SOCKADDR));
	if (SOCKET_ERROR == cRes) {
		cout << "客户端:\t\t与服务器连接失败....." << endl;	
	}
	else {
		cout << "客户端:\t\t与服务器连接成功....." << endl;		
	}


	//	4	发送请求
	char sendBuf[1024] = "from Client:   hello service.";
	send(clientSocket, sendBuf, strlen(sendBuf), 0);


	//	5	发送/接受 数据
	char recvBuf[1024] = {};
	recv(clientSocket, recvBuf, 1024, 0);
	cout << "客户端接收数据	:	" << recvBuf << endl << endl;
	

	//	6	关闭socket
	closesocket(clientSocket);


	//	7	终止
	WSACleanup();

	cout << "客户端退出" << endl;
	cin.get();
	return 0;
}


这个就是运行效果啦

在这里插入图片描述

根据简单实现数据的发送和接受,我们一步一步改善代码

1. 首先使得客户端不断向服务器发送数据

发送数据: (也可以叫做请求,一般嘛,客户端都是向服务器端请求获得某资源).

更改代码,与上面一样,其余部分代码不做改变
服务器:
// 循环接收数据
	while (true)
	{

		//	6	发送/接受 数据
		char recvBuf[4024] = {};
		int reLen = recv(recvClientSocket, recvBuf, 4024, 0);
		int sLen = send(recvClientSocket, recvBuf, reLen, 0);
		if (SOCKET_ERROR != reLen) {

			cout << "服务器接受到数据:    " << recvBuf << endl << endl;
			reLen = SOCKET_ERROR;
		}
	}
客户端:
while (true)
	{
		//	4	发送请求
		string s;
		cout << "输入发送数据:   " << endl;
		getline(cin, s);					//可输入空格,默认以换行符结束输入,
		send(clientSocket, (char*)s.c_str(), s.length(), 0);
		//	5	发送/接受 数据
		char recvBuf[4024] = {};
		recv(clientSocket, recvBuf, 4024, 0);
		cout << "客户端接收数据	:	" << recvBuf << endl << endl;
	}

提示:代码更改仅仅是增加一个while循环,思路很清晰. 客户端我们从控制台输入发送数据,变得更主动

2.进一步改善,再服务器端处理简单的业务逻辑

作为学习,简单来说呢,就是获得客户端发送来的数据做一些处理(判断).

下面我们提供一个简单的例子(在服务器上做修改):

服务器:

相对于上一阶段,就是在while循环里面添加条件判断语句.
简单吧!

while (true)
	{

		//	6	发送/接受 数据
		char recvBuf[1024] = {};
		int reLen = recv(recvClientSocket, recvBuf, 1024, 0);//阻塞函数,等待接受数据

		if (SOCKET_ERROR == reLen) {
			cout << "服务端发送数据失败" << endl;
		}
		else {
			cout << "请求命令:    " << recvBuf;
			if (0 == strcmp("cls", recvBuf)) {
				//服务端执行命令	
				system(recvBuf);		
			}
			// 中文请求仅仅是为了测试可行性,一般都是用英文
			else if(0 == strcmp("获取版本信息",recvBuf)) {
				//返回数据
				string verData = "Version: 1.0.1\nAuthor: Primer\nReleaseData: 2019-04-21";
				int sLen = send(recvClientSocket, (char*)verData.c_str(), verData.length(), 0);
			}
			else if(0 == strcmp("exit", recvBuf)){
				cout << endl << "退出服务器" << endl;
				break;
			}
			else {
				cout << "\t不正确..." << endl;
			}
			cout << endl;
		}
	}

客户端:

类似的修改,仅仅加了一个判断语句,不做过多的解释!

while (true)
	{
		//	4	发送请求
		string s;
		cout << "输入发送数据:\t";
		getline(cin, s);									//可输入空格,默认以换行符结束输入,
		send(clientSocket, (char*)s.c_str(), (int)s.length(), 0);
	
		//因为recv接受函数是阻塞函数,所以我们加以判断
		//请求正确我才接收数据,否则不影响我继续请求
		if(0 == strcmp("获取版本信息", s.c_str())) {
			char recvBuf[4024] = {};
			int reLen = recv(clientSocket, recvBuf, 4024, 0);//阻塞函数,等待接受数据
			cout << endl << recvBuf << endl << endl;
		}

	}
效果图:

在这里插入图片描述

若要实现更多的逻辑处理,自己大开脑洞.

3.传输结构体到服务器

数据有着各种各样的类型,文字,图片,音频,视频等都是数据,有些数据复杂有些简单,
刚开始部分我们传输的是一个字符型或者整型的数据到服务器上,那么这此我们传输
稍微复杂一些的数据—结构体.

编不下去了.

上代码!
第一:

在客户端和服务器端定义一个结构一致的结构体.

什么叫结构一致?
你在结构体里面定义的基本类型顺序一定要相同,不然在读取数据的时候,
由于字节对齐不正确,可能读取出来的就是乱码
. 自己体会.

我的结构体是真这样子的.


//
// 定义一个学生结构体信息
typedef struct node {

	int		id;					//学号
	char	name[50];			//姓名
	char	sex[10];			//性别
	int		age;				//年龄
	char	className[100];		//班级

}STUDENT;



服务器端:

一样的,仅仅改动while循环部分

	STUDENT student = {};//服务器接受数据,数据格式需要和客户端数据格式一致
	while (true)
	{

		//	6	发送/接受 数据
		int reLen = recv(recvClientSocket, (char*)&student, 4024, 0);
		if (SOCKET_ERROR != reLen){

			cout << "服务端输出接受数据:  " << endl << endl;
			cout <<"学号:\t"<< student.id << endl;
			cout <<"姓名:\t"<< student.name << endl;
			cout <<"性别:\t"<< student.sex << endl;
			cout <<"年龄:\t"<< student.age << endl;
			cout <<"班级:\t"<< student.className << endl;
			memset(&student, 0, sizeof(student));
		}
	}

客户端:

更简单

	while (true)
	{
		//	4	发送数据
		STUDENT student;
		cout << "输入学生信息:[学号+姓名+性别+年龄+专业班级]\t";
		cin >> student.id >> student.name >> student.sex >> student.age >> student.className;
		//在发送结构体的受强制转换 提供地址形式发送即可	
		send(clientSocket, (char*)&student, sizeof(student), 0);
	}

效果图:

在这里插入图片描述

  • 97
    点赞
  • 370
    收藏
    觉得还不错? 一键收藏
  • 29
    评论
### 回答1: C语言中实现基于TCP协议的服务器客户端通信,需要使用Socket编程。下面是一些基本步骤: 1. 建立服务器 首先需要建立服务器端的socket。通过调用socket()函数创建服务器socket,指定socket的协议族(通常是AF_INET,即IPv4),socket类型(通常是SOCK_STREAM,即TCP流式套接字),和端口号。之后调用bind()函数把服务器socket绑定到指定的地址和端口。接下来就可以调用listen()函数开始监听客户端连接请求。 2. 连接客户端 客户端需要调用socket()函数创建socket,同样指定协议族、socket类型和端口号(这里可以随机指定一个未占用的端口号)。之后调用connect()函数连接服务器的地址和端口号即可。 3. 通信 一旦客户端服务器端建立连接,就可以通过读写socket进行通信。服务器端需要调用accept()函数接受客户端连接请求,返回一个新的socket描述符用于和客户端进行通信。之后可以使用send()函数向客户端发送数据,使用recv()函数从客户端接收数据。客户端同样可以使用send()和recv()函数进行通信。 4. 结束连接 通信结束后,服务器客户端需要分别调用close()函数关闭链接。 以上是基于TCP协议的服务器客户端通信的基本步骤,具体实现过程需要详细的代码实现。在实际开发中,还需要注意处理错误和异常情况,以保证程序的稳定性和安全性。 ### 回答2: 基于TCP协议的服务器客户端通信是一种常见的网络通讯方式。服务器在网络上侦听特定端口,接收客户端的连接请求。当连接建立后,服务器客户端之间可以进行数据传输实现基于TCP协议的服务器客户端通信,需要遵循以下步骤: 1. 创建服务器端的套接字(socket)并绑定IP地址和端口号。 2. 监听客户端的连接请求,等待客户端连接。 3. 接受客户端的连接请求,创建一个与客户端通信的套接字。 4. 使用套接字进行数据传输,包括从客户端接收请求和向客户端发送响应。 5. 当通信完成后,关闭连接并释放资源。 对于客户端,需要以下步骤: 1. 创建客户端的套接字。 2. 连接服务器套接字。 3. 发送请求数据给服务器。 4. 接收服务器响应数据。 5. 关闭连接并释放资源。 在实现过程中,还需要注意以下方面: 1. 使用正确的IP地址和端口号进行通信。 2. 服务器端需要使用多线程或多进程进行并发处理,以支持多个客户端同时连接。 3. 通信过程中需要加入一定的数据校验和错误处理机制,以提高通讯的可靠性。 总之,基于TCP协议的服务器客户端通信是一种灵活、可靠的网络通讯方式,可以广泛应用于各种网络场景中,例如打印、文件传输、远程控制等。 ### 回答3: C 11是一种编程语言,可以用来实现基于TCP的服务器客户端通信。TCP是传输控制协议的缩写,它提供了一种可靠的数据传输方式,被广泛用于互联网上的通信。 要实现基于TCP的服务器客户端通信,需要用C 11语言编写两个程序:一个服务器程序和一个客户端程序。服务器程序在运行时监听一个指定的端口,等待客户端程序的连接请求。当客户端请求连接时,服务器程序接受连接请求,并创建一个新的进程或线程用于处理这个连接。 在服务器程序和客户端程序之间进行数据传输时,需要使用TCP协议提供的套接字接口。服务器程序和客户端程序都可以通过套接字接口创建一个套接字,用于进行数据传输服务器程序可以使用accept函数来接受连接请求,而客户端程序可以使用connect函数来连接服务器。 一旦连接建立,服务器程序和客户端程序之间就可以通过套接字进行数据传输了。服务器程序可以使用send函数将数据发送给客户端程序,而客户端程序可以使用recv函数接收服务器发送的数据。数据传输结束后,服务器程序和客户端程序都可以使用close函数关闭套接字。 总之,用C 11语言编写基于TCP的服务器客户端通信程序需要了解TCP协议、套接字接口和相应的函数,熟练掌握C 11编程语言,并具有相应的开发经验和编程能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值