基于TCP/IP的迭代回声服务器/客户端

服务器/客户端的实现顺序:

                              

需要注意的是:客户端只有等到服务器调用listen函数后才能调connect函数。同时要清楚,客户端调用connect函数前,服务器端有可能率先调用accept函数。当然,此时服务器在调用accept函数时进入阻塞状态,直到客户端调用connect函数为止。


迭代服务器/客户端:

插入循环语句反复调用accept函数,图示如下

                                 

简单的迭代服务器/客户端代码:

服务器

//服务器代码
#include<stdlib.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib") //显示调用ws2_32.lib

void ErrorHanding(char* message);
int main() {
	WSADATA wsadata;
	SOCKET hserverSocket, hclientSocket;
	SOCKADDR_IN serveraddr, clientaddr;
	int szClientAdd,str_len;
	char message[4096] = "\0";
	
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) //初始化winsock库
		ErrorHanding("WSAStartup() error");
	hserverSocket = socket(PF_INET, SOCK_STREAM, 0); // IPV4 面向连接传输
	if (hserverSocket == INVALID_SOCKET)
		ErrorHanding("socket() error");

	memset(&serveraddr, 0, sizeof(serveraddr));//清空,值全为0

	serveraddr.sin_family = AF_INET;    //IPv4
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);  //自动寻找本机地址
	serveraddr.sin_port = htons(6666);  //端口为 6666
	
	//绑定服务器地址
	if (bind(hserverSocket, (SOCKADDR*)&serveraddr, sizeof(serveraddr)) == SOCKET_ERROR)
		ErrorHanding("bind() error");
	printf("服务器启动成功.........\n");

	//使套接字成为服务器套接字
	if (listen(hserverSocket,5) == SOCKET_ERROR) {  //等待队列的最大数目为5
		ErrorHanding("listen() error");
	}
	szClientAdd = sizeof(clientaddr);
	for (int i = 0; i < 5; i++) {
		hclientSocket = accept(hserverSocket, (SOCKADDR*)&clientaddr, &szClientAdd);
		if (hclientSocket == INVALID_SOCKET)
			ErrorHanding("accept() Error");
		printf("这是客户端:   NO . %d\n", i + 1);

		closesocket(hclientSocket);  //关闭客户套接字
	}
	getchar();
	closesocket(hserverSocket);  //关闭服务器套接字
	WSACleanup();  //清理winsock库
	return 0;
}
void ErrorHanding(char* message) {
	fputs(message, stderr);
	fputc('\n', stderr);
}

客户端

#include<stdlib.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
void ErrorHanding(char* message);
int main() {
	WSADATA wsadata;
	SOCKET hclientsocket;
	SOCKADDR_IN servAddr;

	char message[30];
	int strlen;

	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
		ErrorHanding("WSAStartup() error");
	hclientsocket = socket(PF_INET, SOCK_STREAM, 0);
	if (hclientsocket == INVALID_SOCKET)
		ErrorHanding("socket() error");

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //回送地址,访问本地
	servAddr.sin_port = htons(6666); //端口号

	printf("客户端启动成功.......\n");
	//客户端套接字通过目的地址建立连接
	if (connect(hclientsocket, (SOCKADDR*)&servAddr, sizeof(servAddr)))
		ErrorHanding("conect() error");
	getchar();
	closesocket(hclientsocket);
	WSACleanup();
	return 0;
}
void ErrorHanding(char* message) {
	fputs(message, stderr);
	fputc('\n', stderr);
}

每次客户端打开,获取任意字符后结束,服务器都能接收到消息,但是只能相应5次,获取任意字符后就关闭了,当然这只是为了了解原理而写,非常简单,不能识别同一客户端,

迭代回声服务器/客户端:

程序的基本运行方式如下

1)服务器在同一时刻只与一个客户端相连,并提供回声服务。

2)服务器依次向5个客户端提供服务并退出

3)客户端接受用户输入的字符串并发送到服务器端

4)服务器端将接收的自负出汗数据传回客户端,即“回声”

5)服务器端与客户端之间的字符串回声一直执行到客户端输入Q为止


服务器:

//服务器代码
#include<stdlib.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib") //显示调用ws2_32.lib

void ErrorHanding(char* message);
int main() {
	WSADATA wsadata;
	SOCKET hserverSocket, hclientSocket;
	SOCKADDR_IN serveraddr, clientaddr;
	int szClientAdd,str_len;
	char message[4096] = "\0";
	
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) //初始化winsock库
		ErrorHanding("WSAStartup() error");
	hserverSocket = socket(PF_INET, SOCK_STREAM, 0); // IPV4 面向连接传输
	if (hserverSocket == INVALID_SOCKET)
		ErrorHanding("socket() error");

	memset(&serveraddr, 0, sizeof(serveraddr));//清空,值全为0

	serveraddr.sin_family = AF_INET;    //IPv4
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);  //自动寻找本机地址
	serveraddr.sin_port = htons(6666);  //端口为 6666
	
	//绑定服务器地址
	if (bind(hserverSocket, (SOCKADDR*)&serveraddr, sizeof(serveraddr)) == SOCKET_ERROR)
		ErrorHanding("bind() error");
	printf("服务器启动成功.........\n");

	//使套接字成为服务器套接字
	if (listen(hserverSocket,5) == SOCKET_ERROR) {  //等待队列的最大数目为5
		ErrorHanding("listen() error");
	}
	szClientAdd = sizeof(clientaddr);
	for (int i = 0; i < 5; i++) {
		hclientSocket = accept(hserverSocket, (SOCKADDR*)&clientaddr, &szClientAdd);
		if (hclientSocket == INVALID_SOCKET)
			ErrorHanding("accept() Error");
		printf("这是客户端:   NO . %d\n", i + 1);
		while ((str_len = recv(hclientSocket, message, sizeof(message), 0)) != 0) {
			printf("%s", message);
			if (send(hclientSocket, message, str_len, 0) == SOCKET_ERROR) { 
				ErrorHanding("send() error");
				break;
			}
			memset(message, '\0', sizeof(message));
		}
		closesocket(hclientSocket);  //关闭客户套接字
	}
	getchar();
	closesocket(hserverSocket);  //关闭服务器套接字
	WSACleanup();  //清理winsock库
	return 0;
}
void ErrorHanding(char* message) {
	fputs(message, stderr);
	fputc('\n', stderr);
}

客户端:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
void ErrorHanding(char* message);
#define bufsize 100
int main() {
	WSADATA wsadata;
	SOCKET hclientsocket;
	SOCKADDR_IN servAddr;

	char message[bufsize];
	int str_len=0;
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
		ErrorHanding("WSAStartup() error");
	hclientsocket = socket(PF_INET, SOCK_STREAM, 0);
	if (hclientsocket == INVALID_SOCKET)
		ErrorHanding("socket() error");

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //回送地址,访问本地
	servAddr.sin_port = htons(6666); //端口号

	printf("客户端启动成功.......\n");
	//客户端套接字通过目的地址建立连接
	if (connect(hclientsocket, (SOCKADDR*)&servAddr, sizeof(servAddr)))
		ErrorHanding("conect() error");
	while (1) {
		printf("输入Q退出:");
		fgets(message, bufsize, stdin);
		if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) break;
		send(hclientsocket, message, strlen(message), 0);
		memset(message, '\0', sizeof(message));
		str_len = recv(hclientsocket, message, bufsize - 1, 0);
		if (str_len == SOCKET_ERROR) ErrorHanding("recv() error\n");
		printf("服务器消息:%s", message);
	}
	closesocket(hclientsocket);
	WSACleanup();
	return 0;
}
void ErrorHanding(char* message) {
	fputs(message, stderr);
	fputc('\n', stderr);
}

上面客户端代码存在一点问题,TCP传输的数据不存在数据边界,如果传输数据大,经过多个路由器的转发后,recv函数执行第一次收到的字符串长度会小于实际发送的长度。

改进的客户端代码:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
void ErrorHanding(char* message);
#define bufsize 2048
int main() {
	WSADATA wsadata;
	SOCKET hclientsocket;
	SOCKADDR_IN servAddr;

	char message[bufsize]="\0";
	int str_len=0,rec_cnt,rec_len;
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
		ErrorHanding("WSAStartup() error");
	hclientsocket = socket(PF_INET, SOCK_STREAM, 0);
	if (hclientsocket == INVALID_SOCKET)
		ErrorHanding("socket() error");

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //回送地址,访问本地
	servAddr.sin_port = htons(6666); //端口号

	printf("客户端启动成功.......\n");
	//客户端套接字通过目的地址建立连接
	if (connect(hclientsocket, (SOCKADDR*)&servAddr, sizeof(servAddr)))
		ErrorHanding("conect() error");
	while (1) {
		printf("输入Q退出:");
		fgets(message, bufsize, stdin);
		if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) break;
		str_len = send(hclientsocket, message, strlen(message), 0);
		memset(message, '\0', sizeof(message));
		//保证数据传输的完整性
		rec_len = 0;
		while (rec_len < str_len) {
			rec_cnt = recv(hclientsocket, &message[rec_len], bufsize - 1, 0);
			if (rec_cnt == SOCKET_ERROR) ErrorHanding("recv() error\n");
			rec_len += rec_cnt;
		}
		/*str_len = recv(hclientsocket, message, bufsize - 1, 0);
		if (str_len == SOCKET_ERROR) ErrorHanding("recv() error\n");*/
		printf("服务器消息:%s", message);
	}
	closesocket(hclientsocket);
	WSACleanup();
	return 0;
}
void ErrorHanding(char* message) {
	fputs(message, stderr);
	fputc('\n', stderr);
}



  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值