再写一遍双回射,主要还是按照书上走,也方便自己回顾理解
而且这个代码完美解决了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