首页 > 编程语言 >基于Window网络编程课程设计(刘琰著)写tcp和udp双回射服务器思想及代码实现

基于Window网络编程课程设计(刘琰著)写tcp和udp双回射服务器思想及代码实现

时间:2024-10-10 23:18:23浏览次数:3  
标签:课程设计 addr sock udp tcp FD 双回 接字

再写一遍双回射,主要还是按照书上走,也方便自己回顾理解

而且这个代码完美解决了tcp阻塞问题,其实看懂这个代码也理解了为什么上篇的代码网络编程——实现tcp和udp的双回射服务器(c++)-CSDN博客

会被阻塞,读者可以自己思考下

本书还是采用的是select的方法来实现双回射的服务器。

一、基本思路流程图:

                               

                                          

一、主要实现过程:

创建三个套接字:

SOCKET tcp_sock = INVALID_SOCKET;//tcp监听套接字
SOCKET udp_sock = INVALID_SOCKET;//udp套接字
SOCKET tcp_clnt_sock = INVALID_SOCKET;//tcp连接套接字

初始化套接字:

if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
    printf("WSAStartup failed");
}
对监听和udp两个套接字进行基本操作:

tcp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

//这一步上次文章有讲到,目的是能够快速重新使用该端口

int opt = 1;
setsockopt(tcp_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));

tcp_addr.sin_family = AF_INET;
tcp_addr.sin_addr.s_addr = INADDR_ANY;
tcp_addr.sin_port = htons(3000);

udp_addr.sin_family = AF_INET;
udp_addr.sin_addr.s_addr = INADDR_ANY;
udp_addr.sin_port = htons(3000);


分别为tcp_sock和udp_sock绑定端口:


bind(tcp_sock, (struct sockaddr*)&tcp_addr, sizeof(tcp_addr));
bind(udp_sock, (struct sockaddr*)&udp_addr, sizeof(udp_addr));

监听套接字:

 listen(tcp_sock, 5);//监听

select 函数进行 I/O 多路复用时,用来设置和管理需要检测的套接字集合时的一些基本化操作:

fd_set readfds;

//这行代码声明了一个类型为 fd_set 的变量 readfds。fd_set 是一个数据结构,用于存储一组需要监视的套接字

FD_ZERO(&readfds);

//这行代码调用 FD_ZERO 宏,初始化 readfds 集合,将其清空。FD_ZERO 宏将 fd_set 结构中的所有位都设置为 0,表示开始时没有任何套接字被监视。

FD_SET(tcp_sock, &readfds);

//这行代码调用 FD_SET 宏,将 TCP 套接字 tcp_sock 添加到 readfds 集合中。FD_SET 宏会在 fd_set 结构中对应的套接字位置设置为 1,表示这个套接字被加入到监视集合中。

FD_SET(udp_sock, &readfds);

//这行代码将 UDP 套接字 udp_sock 也添加到 readfds 集合中,操作与添加 TCP 套接字类似

通过循环,检查套接字活动:

while (1) {
    fd_set fds = readfds;
    int activity = select(0, &fds, NULL, NULL, NULL);  

    if ((activity < 0) && (errno != EINTR)) {
        printf("select error");
    }

    if (FD_ISSET(tcp_sock, &fds)) {
            查看tcp监听套接字,如果有,进行accept,将将连接套接字移入readfds集合
    }

    else if (FD_ISSET(udp_sock, &fds)) {
        //如果是udp套接字,进行回射操作
    }

    else
    {
      剩下的只可能是tcp的连接套接字了,直接进行回射操作

    }
}

三、具体代码

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#define _CRT_SECURE_NO_WARNINGS

#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#pragma comment (lib,"Ws2_32.lib")
#pragma comment (lib,"Mswsock.lib")
#pragma comment (lib,"AdvApi32.lib")
#define BUF_SIZE 1024
int main() {
	WSADATA wsaData;
	SOCKET tcp_sock = INVALID_SOCKET;//tcp监听套接字
	SOCKET udp_sock = INVALID_SOCKET;//udp套接字
	SOCKET tcp_clnt_sock = INVALID_SOCKET;//tcp连接套接字
	struct sockaddr_in tcp_addr; 
	struct sockaddr_in udp_addr; 
	int addr_len = sizeof(struct sockaddr_in);
	// Initialize Winsock
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		printf("WSAStartup failed");
	}

	tcp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	
	int opt = 1;
	setsockopt(tcp_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
	setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
	
	tcp_addr.sin_family = AF_INET;
	tcp_addr.sin_addr.s_addr = INADDR_ANY;
	tcp_addr.sin_port = htons(3000);

	udp_addr.sin_family = AF_INET;
	udp_addr.sin_addr.s_addr = INADDR_ANY;
	udp_addr.sin_port = htons(3000);
	//分别为tcp_sock和udp_sock绑定端口
	bind(tcp_sock, (struct sockaddr*)&tcp_addr, sizeof(tcp_addr));
	bind(udp_sock, (struct sockaddr*)&udp_addr, sizeof(udp_addr));
	
	listen(tcp_sock, 5);//监听
	// select 函数进行 I/O 多路复用时,用来设置和管理需要检测的套接字集合
	fd_set readfds;//这行代码声明了一个类型为 fd_set 的变量 readfds。fd_set 是一个数据结构,用于存储一组需要监视的套接字
	FD_ZERO(&readfds);//这行代码调用 FD_ZERO 宏,初始化 readfds 集合,将其清空。FD_ZERO 宏将 fd_set 结构中的所有位都设置为 0,表示开始时没有任何套接字被监视。
	FD_SET(tcp_sock, &readfds);//这行代码调用 FD_SET 宏,将 TCP 套接字 tcp_sock 添加到 readfds 集合中。FD_SET 宏会在 fd_set 结构中对应的套接字位置设置为 1,表示这个套接字被加入到监视集合中。
	FD_SET(udp_sock, &readfds);//这行代码将 UDP 套接字 udp_sock 也添加到 readfds 集合中,操作与添加 TCP 套接字类似

	while (1) {
		fd_set fds = readfds;
		int activity = select(0, &fds, NULL, NULL, NULL);
		if ((activity < 0) && (errno != EINTR)) {
			printf("select error");
		}
		
		if (FD_ISSET(tcp_sock, &fds)) {
			 tcp_clnt_sock = accept(tcp_sock, (struct sockaddr*)&tcp_addr, &addr_len);
			if (tcp_clnt_sock == -1)
				printf("accept error");
			FD_SET(tcp_clnt_sock, &readfds);
		}

		else if (FD_ISSET(udp_sock, &fds)) {
			char buffer[BUF_SIZE];
			memset(buffer, 0, BUF_SIZE);
			int n = recvfrom(udp_sock, buffer, BUF_SIZE, 0, (struct sockaddr*)&udp_addr, &addr_len);
			if (n > 0)
			{
				printf("服务器接收到UDP数据:%s\n", buffer);
				//回射数据
				n = sendto(udp_sock, buffer, n, 0, (struct sockaddr*)&udp_addr, addr_len);
				if (n == SOCKET_ERROR)
				{
					printf("send 函数调用错误,错误号:%ld\n", WSAGetLastError());
					closesocket(udp_sock);
					FD_CLR(udp_sock, &fds);
				}
				else
				{
					printf("服务器回射UDP数据:%s\n", buffer);
					printf("\n");
				}
			}
			else if (n == 0)
			{
				//连接关闭
				printf("当前连接关闭。。。\n");
				closesocket(udp_sock);
				FD_CLR(udp_sock, &fds);
			}
			else
			{

				printf("recvfrom 函数调用错误,错误号:%d\n", WSAGetLastError());
				closesocket(udp_sock);
				FD_CLR(udp_sock, &fds);
			}
		}

		else
		{
			char recvbuf[BUF_SIZE];
			memset(recvbuf, 0, BUF_SIZE);
			int iResult = 0;
			//接收数据
			iResult = recv(tcp_clnt_sock, recvbuf, BUF_SIZE, 0);
			if (iResult > 0)
			{
				printf("服务器接收到TCP数据:%s\n", recvbuf);
				//回射数据
				iResult = send(tcp_clnt_sock, recvbuf, iResult, 0);
				if (iResult == SOCKET_ERROR)
				{
					printf("send 函数调用错误,错误号:%ld\n", WSAGetLastError());
					closesocket(tcp_clnt_sock);
					FD_CLR(tcp_clnt_sock, &fds);
				}
				else
				{
					printf("服务器回射TCP数据:%s\n", recvbuf);
					printf("\n");
				}
			}
			else if (iResult == 0)
			{
				//连接关闭
				printf("当前连接关闭。。。\n");
				closesocket(tcp_clnt_sock);
				FD_CLR(tcp_clnt_sock, &fds);
			}
			else
			{

				printf("recv 函数调用错误,错误号:%d\n", WSAGetLastError());
				closesocket(tcp_clnt_sock);
				FD_CLR(tcp_clnt_sock, &fds);
			}

		}


	}
	return 0;
}

标签:课程设计,addr,sock,udp,tcp,FD,双回,接字
From: https://blog.csdn.net/boss3624/article/details/142832550

相关文章

  • socket tcp断线重连
    一、网上常用方法1、当Socket.Conneted==false时,调用如下函数进行判断点击(此处)折叠或打开///<summary>///当socket.connected为false时,进一步确定下当前连接状态///</summary>///<returns></returns>private bool IsSocketConnected(){    #region remar......
  • 大学生课程设计报告--基于JavaGUI的贪吃蛇
    前言​贪吃蛇游戏是一个基础且经典的视频游戏,它适合作为学习编程的人进行一些更深入的学习,可以更加了解关于循环,函数的使用,以及面向对象是如何应用到实际项目中的;​不仅如此,贪吃蛇游戏的规则在思考后可以拆分,有利于学生将更多精力去设计游戏的核心逻辑,而不是更多边缘设......
  • NetCore 使用 SimpleTCP 实现双工通信
    十年河东,十年河西,莫欺少你穷学无止境,精益求精1、新建netcore控制台应用程序并引入包 2、服务端usingSimpleTCP;usingSystem;usingSystem.Net;usingSystem.Text;namespaceTcpServe{classProgram{staticvoidMain(string[]args)......
  • apisix Admin API配置TCP路由转发
    apisixAdminAPI配置TCP路由转发 一、开启和配置stream代理1、修改/usr/local/apisix/conf/config.yaml文件新增以下配置apisix:proxy_mode:http&streamstream_proxy:tcp:-9100-"192.168.1.110:9101"udp:-9200......
  • CC-Link IEFB转Modbus TCP总线协议转换网关
    一,设备主要功能捷米特JM-CCLKIE-TCP网关实现连接到CC-LinkIEFieldBasic总线中做为从站使用,连接到ModbusTCP总线中做为主站或从站使用。应用广泛:捷米特JM-CCLKIE-TCP广泛应用于支持ModbusTCP接口的上位机、变频器、仪表、马保等等。例如在印染设备中,通过该网关可以将印染......
  • 计算机网络 tcp和udp
    目录一、TCP建立连接-TCP三次握手1)什么是半连接队列和全连接队列?2)为什么要三次握手?3)三次握手过程中可以携带数据吗?断开连接-TCP四次挥手1)为什么要四次挥手?2)为什么不能把服务端发送的ACK和FIN合并起来,变成三次挥手?3)如果第二次挥手时服务端的ACK没有送......
  • <免费开题>基于Python二维码生成算法研究和实现|全套源码+文章lw+毕业设计+课程设计+数
    <免费开题>基于Python二维码生成算法研究和实现|全套源码+文章lw+毕业设计+课程设计+数据库+ppt摘要随着网络应用技术的普及和发展,计算机以及移动应用系统正在飞速的发展,通过互联网平台和移动端的应用技术帮助实现了智能化及数字化的管理模式,借助系统平台实现了高效便捷的管......
  • <免费开题>登录网站验证码的生成与识别系统(django)|全套源码+文章lw+毕业设计+课程设计
    <免费开题>登录网站验证码的生成与识别系统(django)|全套源码+文章lw+毕业设计+课程设计+数据库+ppt摘要近年来随着互联网应用技术的飞速发展,为了确保网站系统平台的安全性,各类网站相继推出了验证码应用技术,通过验证码的应用来帮助缓解暴力破解账户密码、垃圾邮件攻击以及在......
  • 数据结构课程设计大项目————迷宫问题(邻接矩阵,prim生成算法,DFS寻路,BFS寻路,路径回溯
    一.前言迷宫问题是数据结构中最值得实践的大项目之一,本文主要讲解思路,提供的代码大部分都有注释(没有的就是太多了懒得写了QAQ)。为了更好的表现效果,该程序使用了easyx可视化,easyx简单易学(大概一天到两天就可以学会),上手简单。该程序由c语言实现,本人水平有限程序可优化空间很大。......
  • 洛谷P10336 [UESTCPC 2024] 2-聚类算法
    涉及知识点:博弈、贪心题意Alice和Bob在玩选点游戏,所有的点在一个\(k\)维空间中,他们轮流选走一个点放入自己的集合中,Alice先手。定义集合\(S\)的权值\(val(S)\)为集合中点两两之间的\(k\)维曼哈顿距离之和。Alice的得分为\(val(S_A)-val(S_B)\),Bob的得分为\(val(......