socket 网络通信 ( windows + Linux )

2023-10-27

参考:
C++ socket 网络通信等

Socket 编程

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).
说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

在这里插入图片描述
交互流程:
TCP/IP通信中,主要是进行C/S交互。废话不多说,下面看看具体交互内容:

服务端:建立socket,申明自身的port和IP,并绑定到socket,使用listen监听,然后不断用accept去查看是否有连接。如果有,捕获socket,并通过recv获取消息的内容,通信完成后调用closeSocket关闭这个对应accept到的socket。如果不需要等待任何客户端连接,那么用closeSocket直接关闭自身的socket。

客户端:建立socket,通过端口号和地址确定目标服务器,使用Connect连接到服务器,send发送消息,等待处理,通信完成后调用closeSocket关闭socket。

windows平台(C++):

以下编程平台在VS2015上,新建两个不同的控制台应用程序,一个只有服务器代码,另一个只有客户端代码,同时运行,即可实现服务器和客户端的通信
服务器代码:
//server.cpp
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

void initialization();

int main() 
{
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	int len = 0;

	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];

	//定义服务端套接字,接受请求套接字
	SOCKET s_server;
	SOCKET s_accept;

	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	SOCKADDR_IN accept_addr;
	initialization();

	//填充服务端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(7777);

	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) 
	{
		cout << "套接字绑定失败!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "套接字绑定成功!" << endl;
	}

	//设置套接字为监听状态
	if (listen(s_server, SOMAXCONN) < 0) 
	{
		cout << "设置监听状态失败!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "设置监听状态成功!" << endl;
	}
	cout << "服务端正在监听连接,请稍候...." << endl;

	//接受连接请求
	len = sizeof(SOCKADDR);
	s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
	if (s_accept == SOCKET_ERROR) 
	{
		cout << "连接失败!" << endl;
		WSACleanup();
		return 0;
	}
	cout << "连接建立,准备接受数据" << endl;

	//接收数据
	while (1) 
	{
		recv_len = recv(s_accept, recv_buf, 100, 0);
		if (recv_len < 0) 
		{
			cout << "接受失败!" << endl;
			break;
		}
		else 
		{
			cout << "客户端信息:" << recv_buf << endl;
		}
		cout << "请输入回复信息:";
		cin >> send_buf;
		send_len = send(s_accept, send_buf, 100, 0);
		if (send_len < 0) 
		{
			cout << "发送失败!" << endl;
			break;
		}
	}

	//关闭套接字
	closesocket(s_server);
	closesocket(s_accept);

	//释放DLL资源
	WSACleanup();
	return 0;
}

void initialization()
{
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) 
	{
		cout << "初始化套接字库失败!" << endl;
	}
	else 
	{
		cout << "初始化套接字库成功!" << endl;
	}

	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) 
	{
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "套接字库版本正确!" << endl;
	}

}
客户端代码:
//client.cpp
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

void initialization();

int main() 
{
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;

	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];

	//定义服务端套接字,接受请求套接字
	SOCKET s_server;

	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	initialization();

	//填充服务端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	server_addr.sin_port = htons(7777);

	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) 
	{
		cout << "服务器连接失败!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "服务器连接成功!" << endl;
	}

	//发送,接收数据
	while (1)
	{
		cout << "请输入发送信息:";
		cin >> send_buf;
		send_len = send(s_server, send_buf, 100, 0);
		if (send_len < 0)
		{
			cout << "发送失败!" << endl;
			break;
		}
		recv_len = recv(s_server, recv_buf, 100, 0);
		if (recv_len < 0) 
		{
			cout << "接受失败!" << endl;
			break;
		}
		else 
		{
			cout << "服务端信息:" << recv_buf << endl;
		}

	}

	//关闭套接字
	closesocket(s_server);

	//释放DLL资源
	WSACleanup();
	return 0;
}
void initialization() 
{
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) 
	{
		cout << "初始化套接字库失败!" << endl;
	}
	else 
	{
		cout << "初始化套接字库成功!" << endl;
	}

	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2)
	{
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "套接字库版本正确!" << endl;
	}

}
现象:

在这里插入图片描述

Linux平台(C代码):

服务器代码:
/* File Name: server.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <unistd.h>

#define DEFAULT_PORT 8000
#define MAXLINE 4096

int main(int argc, char** argv)
{
    int    socket_fd, connect_fd;
    struct sockaddr_in  servaddr;
	char   sendline[4096];
    char   buff[4096];
    int    n;
	
    //初始化Socket
    if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
	{
		printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
    }
	
    //初始化
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  //IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
    servaddr.sin_port = htons(DEFAULT_PORT);       //设置的端口为DEFAULT_PORT
 
    //将本地地址绑定到所创建的套接字上
    if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
	{
		printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
    }
	
    //开始监听是否有客户端连接
    if( listen(socket_fd, 10) == -1)
	{
		printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
    }
	
    printf("waiting for client's request...\n");

    //多个客户端与服务器建立连接,发送消息
	/*	
    while(1)
	{		
		//阻塞直到有客户端连接,不然多浪费CPU资源。
        if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1)
		{
			printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
			continue;
        }
	
		//接受客户端传过来的数据
		n = recv(connect_fd, buff, MAXLINE, 0);		
		
		buff[n] = '\0';
		printf("recv msg from client: %s\n", buff);
				
    }
	*/
	
	 //单个客户端与服务器建立连接,发送消息并回复消息,多次通信
		
	if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1) //阻塞直到有客户端连接,不然多浪费CPU资源。
	{
		printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
	}
	else
	{		
		printf("client connected!\n");		
	}
	
    while(1)
	{		
		//接受客户端传过来的数据
		n = recv(connect_fd, buff, MAXLINE, 0);				
		buff[n] = '\0';
		printf("recv msg from client: %s", buff);
		
		//给客户端发送消息
		printf("send msg to client:");
		fgets(sendline, 4096, stdin);
		if( send(connect_fd, sendline, strlen(sendline), 0) < 0)
		{
			printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
			exit(0);
		}
				
    }
		
	
	close(connect_fd);
    close(socket_fd);
}
客户端代码:
/* File Name: client.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <unistd.h>

#define MAXLINE 4096
 
int main(int argc, char** argv)
{
    int   sockfd, n, rec_len;
    char  recvline[4096], sendline[4096];
    char  buf[MAXLINE];
    struct sockaddr_in  servaddr;
 
    if( argc != 2)
	{
		printf("usage: ./client <ipaddress>\n");
		exit(0);
    }
 
    //初始化socket
    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
		exit(0);
    }
 
 
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8000);
	
	//将点分文本的IP地址转换为二进制网络字节序的IP地址
    if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
	{
		printf("inet_pton error for %s\n",argv[1]);
		exit(0);
    }
 
    //连接服务端
    if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
	{
		printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
    }
	else
	{
		printf("connect server success!\n");
	}
 
	while(1)
	{
		printf("send msg to server:");
		fgets(sendline, 4096, stdin);
		if( send(sockfd, sendline, strlen(sendline), 0) < 0)
		{
			printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
			exit(0);
		}
		
		if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) 
		{
			perror("recv error");
			exit(1);
		}
		else
		{
			buf[rec_len]  = '\0';
			printf("recv msg from server:%s",buf);
		}
		
	}
    close(sockfd);
    exit(0);
}
Makefile:
ifeq ($(DEBUG),Enable)
	CPPFLAGS = -D_DEBUG -g -Wall
else
	CPPFLAGS = -s 
endif 

INCS=-I./include 
LIBS=./lib   
OUTDIR=./
TARGET_CLIENT=client
TARGET_SERVER=server

exe:	
	gcc $(CPPFLAGS) $(INCS)  client.c  -o $(OUTDIR)/$(TARGET_CLIENT)
	gcc $(CPPFLAGS) $(INCS)  server.c  -o $(OUTDIR)/$(TARGET_SERVER)
	
all: exe

clean:
	rm $(OUTDIR)/$(TARGET) 

现象:

首先启动服务器,等待客户端连接
在这里插入图片描述
然后客户端连接上来,准备发送信息
在这里插入图片描述
这时服务端已经收到了客户端连接
在这里插入图片描述
然后客户端发信息给服务端
在这里插入图片描述
服务端接收到信息
在这里插入图片描述
最终实现互相通信,当然这只是与一个客户端通信,放开服务端代码中的注释部分,可以实现多个客户端与服务器建立连接。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

socket 网络通信 ( windows + Linux ) 的相关文章

  • 尝试编译 git 但在 linux 中找不到 libcurl

    我想编译支持 http https 的 git 我有 ls usr include curl curlbuild h curl h curlrules h curlver h easy h mprintf h multi h stdchea
  • 在 4.x 内核上的 64 位内存中查找系统调用表

    我正在尝试编写一个简单的内核模块来查找 Linux 中的 sys call table 但遇到了一些麻烦 我在这里找到了 32 位 Linux 的基本指南 https memset wordpress com 2011 03 18 sysc
  • 无法使用 tar -cvpzf 解压完整目录

    把我的头敲在这上面 I used tar cvpzf file tar gz压缩一个完整的目录 我将文件移动到另一台服务器 并尝试解压缩复制存档的目录 无法使其发挥作用 bash 3 2 tar xvpzf news tar gz tar
  • mod_perl 无法看到 /tmp 中的文件

    我有一些 mod perl 代码试图访问 tmp 下的文件 但它抛出 没有这样的文件或目录 错误 我在代码中添加了一个 ls al tmp 来查看 Perl 在目录中看到的内容 它只给了我 和 drwxrwxrwt 2 root root
  • 在linux中使用setcap [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 要将 cap net raw 功能添加到 例如 bin ping 我们使用以下命令 setcap cap net raw ep bin ping ep
  • 如何从powershell获取主机名?

    我如何获得hostname https stackoverflow com q 42014215 262852 for dur来自 powershell PS home thufir powershell gt PS home thufir
  • 如何更改Linux服务器中的MySQL表名不区分大小写?

    我正在开发一个旧网站 该网站曾经托管在 Apple 服务器上 当它迁移到新的 Linux 服务器时 它停止工作 我很确定这是因为 php 脚本中使用的所有 MySQL 查询对于表名都有不同的大小写组合 我不知道为什么原始开发人员在创建表名或
  • Mongo:无法连接到服务器 127.0.0.1:27017 位于 src/mongo/shell/mongo.js:145

    当我尝试在 ubuntu 中的 shell 中运行 mongo 或打开 rockmongo 时 我看到以下错误 couldn t connect to server 127 0 0 1 27017 at src mongo shell mo
  • 带有接收器的 boost_log 示例无法编译

    我正在考虑将 boost log 用于一个项目 一开始我就遇到了以下问题 我在以下位置找到的升压日志示例 http www boost org doc libs 1 54 0 libs log example doc tutorial fi
  • 多线程进程的线程ID可以与另一个正在运行的进程的进程ID相同吗?

    我正在尝试找到一种方法来唯一标识多进程环境中的线程 我有一个服务器 它跟踪连接到它的不同进程 其中一些是多线程的 一些不是 为了识别多线程连接中的线程 我使用线程 ID 作为唯一标识符 在任何给定时间最多有 1 个多线程进程连接 我的问题是
  • Pthread互斥锁由不同线程解锁

    一个天真的问题 我之前读到过 MUTEX 只能由锁定它的线程解锁 但我写了一个程序THREAD1锁定 mutexVar 并进入睡眠状态 然后THREAD2可以直接解锁mutexVar做一些操作并返回 gt 我知道每个人都说我为什么要这样做
  • 在 Windows 下使用 linux 实用程序的最佳方法是什么? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Linux 实用程序 如 sed awk 和其他 shell 脚本功能 非常棒 但当我在 Windows 上进行开发并且无法使用其中任何一
  • 使用inotify监控文件

    我正在使用 inotify 来监视本地文件 例如使用 root temp inotify add watch fd root temp mask 删除该文件后 程序将被阻止read fd buf bufSize 功能 即使我创建一个新的 r
  • 如何告诉 CMake 将构建文件放在哪里?

    我想告诉 CMake 将文件和文件夹输出到不同的文件夹而不是当前文件夹 我在下面讨论的是 CMake 生成的文件 文件 CMakeCache txt 目录 CMakeFiles 文件 生成文件 目录 bin 文件 cmake install
  • 使用多个 NIC 广播 UDP 数据包

    我正在 Linux 中为相机控制器构建嵌入式系统 非实时 我在让网络做我想做的事情时遇到问题 该系统有 3 个 NIC 1 个 100base T 和 2 个千兆端口 我将较慢的连接到相机 这就是它支持的全部 而较快的连接是与其他机器的点对
  • 超立方体错误。非法的最小或最大规格

    尝试从这里运行示例代码http tess4j sourceforge net codesample html http tess4j sourceforge net codesample html我收到一条错误消息 Error Illega
  • copy_from_user() 错误:目标大小太小

    我正在为内核模块编写 ioctl 处理程序 我想从用户空间复制数据 当我编译禁用优化的代码时 O0 gflags 编译器返回以下错误 include linux thread info h 136 17 error call to bad
  • Powershell从Linux客户端连接到Windows远程

    我正在尝试从我的 Linux 工作站远程连接到 Windows 计算机 我在 Arch Linux 工作站上安装了 powershell 目前正在尝试连接到主机 在主机上 Enable PSRemoting 然后允许所有主机Set Item
  • Linux 从设备本身运行的脚本卸载设备

    我在路径中安装了一个 iso 映像 mnt iso 在这个 iso 中我有一个安装脚本 install sh 我从 iso 运行安装脚本 最后脚本询问用户是否要卸载 iso 本身 如果用户按 y 脚本将执行以下代码 cd umount mn
  • 导入错误:没有名为“tensorrt”的模块

    我使用 Debian 安装在我的虚拟机上安装了 TensorRT 如果我运行 dpkg l grep TensorRT 我会得到预期的结果 ii graphsurgeon tf 5 0 2 1 cuda10 0 amd64 GraphSur

随机推荐

  • 划分数组

    快排的parition划分 class Solution param nums The integer array you should partition param k An integer return The index after
  • ABAP 关于BAPI的EXTENSIONIN 一点解释

    其实EXTENSIONIN 相当于BAPI的一种增强 可以这样理解 要传一下BAPI本身没有的数据 比如 自己增加的 或者是关联过来的数据 BAPI没有给提供这样的字段 那么就要自己去找了 首先要找到增强的结构 比如 BAPI REQUIS
  • 升级Flutter 3.13.x 之后出现watcher-1.0.2报错

    Failed to build intl utils generate pub cache hosted pub flutter io cn watcher 1 0 2 lib src constructable file system e
  • mesa编译

    0 准备工作 sudo apt get install git autoconf libtool dpkg dev quilt debhelper 1 libdrm编译 下载 git clone http anongit freedeskt
  • 彻聊DNS

    先得聊聊什么是域名 域名是什么 我会连域名都不知道 别着急 先看看嘛 我们以www fanyi baidu com为例 域名结构划分为根域名 顶级域名 二级域名 三级域名等 做过开发的都知道 在创建项目时 一般是com xxx xxx 这就
  • 【毕业设计】大数据用户画像数据分析系统 - python

    文章目录 1 前言 2 用户画像分析概述 2 1 用户画像构建的相关技术 2 2 标签体系 2 3 标签优先级 3 实站 百货商场用户画像描述与价值分析 3 1 数据格式 3 2 数据预处理 3 3 会员年龄构成 3 4 订单占比 消费画像
  • 设计模式之单例模式(通俗易懂,超详细)

    1 什么是单例模式 单例模式 属于创建类型的一种常用的软件设计模式 通过单例模式的方法创建的类在当前进程中只有一个实例 根据需要 也有可能一个线程中属于单例 如 仅线程上下文内使用同一个实例 百度百科 简单来说单例模式就是指在内存中只会创建
  • 简单几个配置 Go 实现敏感数据脱敏,可以自定义数据脱敏规则(附完整实现源码)

    简单几个配置 Go 实现敏感数据脱敏 可以自定义数据脱敏规则 附完整实现源码 介绍 为了保障企业的数据安全和隐私安全 godlp 提供了一系列针对敏感数据的识别和处置方案 其中包含敏感数据识别算法 数据脱敏处理方式 业务自定义的配置选项和海
  • c当中宏理解

    宏 Macro 是预处理命令的一种 它允许用一个标识符来表示一个字符串 include
  • STM32F429配置MircoPython的SDRAM参数

    本例使用的开发板是正点原子STM32F429 一 修改mpconfigboard h文件 具体配置如下 SDRAM define MICROPY HW SDRAM SIZE 32 1024 1024 32M Bytes define MIC
  • 拉格朗日对偶

    https www cnblogs com ooon p 5723725 html
  • Ngrok 服务搭建

    一 前言 ngrok 是一个反向代理 通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道 ngrok 可捕获和分析所有通道上的流量 便于后期分析和重放 ngrok一条命令可以解决外网访问内网问题 本地WEB外网访问 本地开发
  • Nginx的安装(Ubuntu)以及常用简介

    目录 Nginx安装 安装前提 源码下载 Nginx安装方式 简单说明nginx目录下的内容 Nginx编译和安装 启动 Nginx安装 安装前提 gcc pcre库 函数库 支持解析正则表达式 apt get install libpcr
  • 快速学习Stm32舵机控制板控制一个舵机运动

    PWM是什么 PWM 英文名Pulse Width Modulation 是脉冲宽度调制缩写 它是通过对一系列脉冲的宽度进行调制 等效出所需要的波形 包含形状以及幅值 对模拟信号电平进行数字编码 也就是说通过调节占空比的变化来调节信号 能量
  • 域名到站点的负载均衡技术一览

    一 问题域 nginx lvs keepalived f5 DNS轮询 往往讨论的是接入层的这样几个问题 1 可用性 任何一台机器挂了 服务受不受影响 2 扩展性 能否通过增加机器 扩充系统的性能 3 反向代理 负载均衡 请求是否均匀分摊到
  • QComboBox样式表 下拉框 QSS 样式表

    注意 QComboBox pComboBox new QComboBox this pComboBox gt setView new QListView 添加这句 设置下拉列表项高才能生效 未下拉时 QComboBox的样式 QComboB
  • Kubernetes下载kube-flannel失败解决方案 大数据

    Kubernetes下载kube flannel失败解决方案 大数据 Kubernetes是一个开源的容器编排平台 而kube flannel是Kubernetes集群中广泛使用的网络插件 用于提供容器之间的网络互通 然而 有时候在下载ku
  • Socket通讯工具类【SocketTools】(20140402修订版)

  • error pulling image configuration: Get https://production.cloudflare.docker.com/registry-v2/docker/

    error pulling image configuration Get https production cloudflare docker com registry v2 docker 原因 使用docker拉取镜像失败 解决方法 在
  • socket 网络通信 ( windows + Linux )

    参考 C socket 网络通信等 Socket 编程 socket起源于Unix 而Unix Linux基本哲学之一就是 一切皆文件 都可以用 打开open gt 读写write read gt 关闭close 模式来操作 Socket就