C++sokcet网络编程笔记

news2024/11/17 17:25:10

C++网络编程

TCP/IP协议


网络介质层:将模拟信号转化成数字信号,会形成一个MAC地址(本机地址,一般情况下不会变化)。百兆宽带用4根线,千兆宽带用8根线进行传输。

网络层:进行网络层的通讯,IP地址对IP地址。

数据进行协议栈时的封装:

WireShark抓包工具

可以通过wireshark进行网络抓包分析,具体教程可以参考网络分析工具——WireShark的使用(超详细)教程进行软件安装和使用,注意Windows可能需要安装一个小插件,软件才能实现本地化配置,具体博客内已经详细说明了情况,这里附上软件下载地址:win10pcap。

windows和linux系统之间配置共享

使用windows编辑工具直接编辑linux代码:

  • 直接用在windows中提交linux上源码
  • 使用VS直接编辑linux跨平台代码
  • 安装配置samba

samba安装

切换到管理员账号/使用sudo权限,

apt-get install sambda

然后编辑sambda的配置文件

vim /etc/samba/smb.conf

在文件末尾加载以下内容:

[code]
path=/your_dir
writeable=yes
browseable=yes
guest ok =yes

然后使用命令在服务器上创建your_dir

mkdir /your_dir

然后重启smbd,先杀掉进程,在重启:

pkill smbd
smbd

接着设置your_dir的访问权限,防止不能在文件内进行操作:

chown nobody:nogroup /your_dir

然后使用cmd,输入\\ip_adress,我的就是\\192.168.20.121回车之后就可以跳转到文件夹中,进行创建文件、修改文件,修改后服务器也会自动进行修改。

socke函数

socket又称为套接字
套接字的定义:

  1. 套接字是一个主机本地应用程序所创建的,为操作系统所控制的接口(门),其实也就是程序使用socket通知操作系统对主机硬件进行操作。
  2. 应用进程通过这个接口,使用传输层提供的服务,跨网络发送(/接收)消息到(/从)其他应用进程。
  3. Client/server模式的通信接口——套接字接口。


socket其实就是文件,Socket发送方和接收方都不是实时的,需要排队进行处理(好处是稳定可靠、坏处是可能不执行)。socket不仅仅只用于网络通信,还可以用于蓝牙、红外等。

windows/linux创建socket

//使用ifdef-endif可以在程序运行开始前识别主机环境
#ifdef WIN32
#include<Windows.h>
#else
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
//函数名替换,重定义,将close关闭sock的函数转化为closesocket
#define closesocket close
#endif
#include <iostream>

using namespace std;

int main(int argc,char* argv[])
{	
	#ifdef WIN32
	//通过进程启动Winsock DLL使用
	WSADATA ws;
	WSAStartup(MAKEWORD(2, 2), &ws);
	#endif
	for (int i = 0; i < 1000; i++) {
		//创建socket,创建失败返回-1,AF_INET表示ipv4协议,SOCK_STREAM表示接受tcp/ip协议的数据
		int sock = socket(AF_INET, SOCK_STREAM, 0);
		if (sock == -1) {
			cout << "create socket failed" << endl;
			return -1;
		}
		cout << "[" << sock << "]" << endl;
		//socket关闭
		closesocket(sock);
	}
	return 0;
}

windows用户在vs studio上运行需要添加ws2_32.lib进行编译,否则会出错。具体可以参考Visual Studio 2019 C++实现socket通信,添加ws2_32.lib库,新手代码。

TCP协议

TCP协议的性质:

  • TCP是面向连接的,需要三次握手确定连接后才开始发送数据,而UDP是直接发送数据的。
  • TCP提供了可靠性,实现了丢失重传。RTT的估算。
  • TCP通过给所发送数据的每一个段管理一个序列号进行排序。
  • TCP提供流量控制。通信窗口、拥塞窗口。
  • TCP连接是全双工的,也就是说基于TCP连接的程序/进程可以同时接收数据和发送数据,具有不同的传出信道。


TCPServer在windows/linux通用程序

#ifdef WIN32
#include<Windows.h>
#define socklen_t int
#else
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
//函数名替换,重定义,将close关闭sock的函数转化为closesocket
#define closesocket close
#endif
#include <iostream>
#include<stdlib.h>
#include<string.h>
#include<thread>

using namespace std;

class TCPThread {
public:
	void main() {
		while (true) {

			//recv和send都不能保证能够发送信息和收到信息成功
			char buf[1024] = { 0 };
			//接收客户端发送得请求,并将其内容存储在buf字符串中
			int reclen = recv(client_sock, buf, sizeof(buf) - 1, 0);
			if (reclen <= 0) {
				break;
			}
			buf[reclen] = '\0';
			if (strstr(buf, "quit") != NULL) {
				char re[] = "quit success!\n";
				//服务器向客户端发送数据
				send(client_sock, re, strlen(re) + 1, 0);
				break;
			}
			int sendlen = send(client_sock, "ok\n", 4, 0);
			cout << "receive data: " << buf << endl;
		}
		closesocket(client_sock);
		delete this;
	}
	int client_sock=0;
};

int main(int argc, char* argv[])
{
#ifdef WIN32
	//通过进程启动Winsock DLL使用
	WSADATA ws;
	WSAStartup(MAKEWORD(2, 2), &ws);
#endif
	
	//创建socket,创建失败返回-1,AF_INET表示ipv4协议,SOCK_STREAM表示接受tcp/ip协议的数据
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock == -1) {
		cout << "create socket failed" << endl;
		return -1;
	}
	cout << "[" << sock << "]" << endl;

	//获得端口号
	unsigned short port = 8080;
	if (argc > 1) {
		port = atoi(argv[1]);
	}
	//绑定地址
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	//大端字节序和小端字节序的问题,
	saddr.sin_port = htons(port);
	//0设置为绑定本机地址
	saddr.sin_addr.s_addr = htonl(0);

	//绑定地址
	if (bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0) {
		cout << "bind port " << port << " failed.\n";
		return -2;
	}
	cout << "bind port " << port << " successful.\n";

	//监听客户端发送的信息
	//backlog=10表示缓冲大小
	listen(sock, 10);

	while (true) {
		//每个连接就会生成一个client
		sockaddr_in caddr;
		socklen_t len = sizeof(caddr);
		//在accept之前会进行三次握手(由操作系统完成),accept只是获取了握手后的信息
		int client_sock = accept(sock, (sockaddr*)&caddr, &len);
		if (client_sock <= 0) {
			break;
		}
		cout << "accept client " << client_sock << ".\n";
		char* ip = inet_ntoa(caddr.sin_addr);
		unsigned short cport = ntohs(caddr.sin_port);
		cout << "client ip address " << ip << ".\n";
		cout << "client port " << cport << ".\n";

		TCPThread* th = new TCPThread();
		th->client_sock = client_sock;
		//启动多线程,第一个参数是函数地址,第二个为对象本身
		thread sth(&TCPThread::main, th);
		//释放主线程中子线程的资源
		sth.detach();
	}
	//socket关闭
	closesocket(sock);

	return 0;
}

TCP封装成函数便于使用

XTCP.h

#ifndef XTCP_H //保证只初始化一次
#define XTCP_H

#ifdef WIN32
#pragma once

#ifdef XSOCKET_EXPORTS
#define XSOCKET_API __declspec(dllexport)
#else
#define XSOCKET_API __declspec(dllimport)
#endif

#else
#define XSOCKET_API
#endif // WIN32

#include<string>
class XSOCKET_API XTCP
{
public:
	int createSocket();
	bool bindListen(unsigned short port);
	void closeSocket();
	int receiveData(char* buf,int bufsize);
	int sendData(const char* buf, int sendsize);
	bool connectSocket(const char* ip, unsigned short port);
	XTCP acceptClient();
	XTCP();
	virtual ~XTCP();

	int sock = 0;
	unsigned short port = 0;
	char ip[16];
};
#endif // !XTCP_H

XTCP.cpp

#include "XTCP.h"
#ifdef WIN32
#include<Windows.h>
#define socklen_t int
#else
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
//函数名替换,重定义,将close关闭sock的函数转化为closesocket
#define closesocket close
#define strcpy_s strcpy
#endif
#include <iostream>
#include<stdlib.h>
#include<cstring>

XTCP::XTCP() {
#ifdef WIN32
	static bool is_first = true;
	if (is_first) {
		is_first = false;		
		//通过进程启动Winsock DLL使用
		WSADATA ws;
		WSAStartup(MAKEWORD(2, 2), &ws);		
	}
#endif
}


bool XTCP::connectSocket(const char* ip, unsigned short port) {
	if (sock <= 0) {
		createSocket();
	}
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	//将字符串ip地址转化为网络地址
	saddr.sin_addr.s_addr = inet_addr(ip);

	if (connect(sock, (const sockaddr*)&saddr, sizeof(saddr)) != 0) {
		// strerror(errno)将错误转化为字符串
		std::cout << "connect " << ip << " : " << port << " failed! ";
		return false;
	}
	std::cout << "connect " << ip << " : " << port << " success!\n ";
	return true;
}

int XTCP::createSocket() {
	//创建socket,创建失败返回-1,AF_INET表示ipv4协议,SOCK_STREAM表示接受tcp/ip协议的数据
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock == -1) {
		std::cout << "create socket failed" << std::endl;
	}
	return sock;
}

bool XTCP::bindListen(unsigned short port) {
	if (sock <= 0) {
		createSocket();
	}
	//绑定地址
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	//大端字节序和小端字节序的问题,
	saddr.sin_port = htons(port);
	//0设置为绑定本机地址
	saddr.sin_addr.s_addr = htonl(0);

	//绑定地址
	if (bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0) {
		std::cout << "bind port " << port << " failed.\n";
		return false;
	}
	std::cout << "bind port " << port << " successful.\n";

	//监听客户端发送的信息
	//backlog=10表示缓冲大小
	listen(sock, 10);
	return true;
}

XTCP XTCP::acceptClient() {
	XTCP tcp;
	//每个连接就会生成一个client
	sockaddr_in caddr;
	socklen_t len = sizeof(caddr);
	//在accept之前会进行三次握手(由操作系统完成),accept只是获取了握手后的信息
	int client_sock = accept(sock, (sockaddr*)&caddr, &len);
	if (client_sock <= 0) {
		return tcp;
	}
	tcp.sock = client_sock;
	std::cout << "accept client " << client_sock << ".\n";
	char* ip = inet_ntoa(caddr.sin_addr);
	strcpy_s(tcp.ip, ip);
	tcp.port = ntohs(caddr.sin_port);
	std::cout << "client ip address " << tcp.ip << ".\n";
	std::cout << "client port " << tcp.port << ".\n";
	return tcp;
}


int XTCP::receiveData(char* buf, int bufsize) {
	return recv(sock, buf, bufsize, 0);
}

int XTCP::sendData(const char* buf, int sendsize) {
	//需要全部发送完全才能结束
	int sendedSize = 0;
	while (sendedSize!=sendsize) {
		int len = send(sock, buf + sendedSize, sendsize - sendedSize, 0);
		if (len <= 0) {
			break;
		}
		sendedSize += len;
	}
	return sendedSize;
}

void XTCP::closeSocket() {
	if (sock <= 0) {
		return;
	}
	closesocket(sock);
}

XTCP::~XTCP() {

}

通过上述函数就可以实现在Windows封装成lib文件,在Linux封装成so文件进行更方便的使用。更多细节参考C/C++封装:Windows/Linux下封装.lib/.so文件。

makefile最基本使用

tcpserver:testSocket2.cpp XTCP.h XTCP.cpp
        g++ testSocket2.cpp XTCP.cpp -o tcpserver -std=c++11 -lpthread

目标文件:源文件和头文件(空格隔开)
		g++ 源文件(不需要头文件) -o 目标文件名 附加命令
		-lpthread:表示多线程使用
		-std:表示c++版本

直接使用make命令就可以执行该程序了

编译成.so文件:

libxsocket.so:XTCP.cpp XTCP.h
        g++ $+ -o $@ -fpic -shared -std=c++11
        
$+:文件列表——依赖项
$@:目标文件——目标项
-fpic:代码与位置无关
-shared:把代码编译成动态链接库

三次握手

第一次握手:客户端给服务器发送一个J信号。
第二次握手:服务器接收客户端发送的请求,并得到客户端发送的J信号(但这个信号不一定正确),所以服务器会返回J+1信号和一个K信号。
第三次握手:客户端接收服务器发送返回的J+1信号,如果正确表示客户端和服务器端通信正常,但是此时服务器端还不知道通信是否正常,所以客户端还需要将服务器发送的K进行处理,发送K+1信号给服务器端,告知服务器通信正常。

服务器和客户端进行TCP/IP通信的流程


服务器的close会关闭两个通信口,一个是接收数据通信的sendrecv的通道,一个是socket客户端和服务器端连接的通道。

设置阻塞方式

在Windows和Linux中,socket阻塞和及时通信方式具有很大的差异,windows通过使用ioctsocket()函数进行设置,而linux则是通过fcntl()函数进行处理。具体如下代码所示。

bool XTCP::setBlock(bool isBlock) {
	//默认为阻塞模式 isBlock=true
	if (sock <= 0) {
		return false;
	}
	#ifdef WIN32
		unsigned long ul = 0;
		if (!isBlock) {
			ul = 1;
		}
		//ul=1就会立刻返回结果,反之,则不会立即返回,只有windows才有这个函数
		ioctlsocket(sock, FIONBIO, &ul);
	#else
		#include<fctnl.h> //头文件
		//fcntl操作文件描述符的函数,
		int flag=fcntl(sock, F_GETFL, 0);
		if (flag < 0) {
			return false;
		}
		if (isBlock) {
			flag = flag & ~O_NONBLOCK;
		}
		else {
			flag = flag | O_NONBLOCK;
		}
		if (fcntl(sock, F_SETFL, flag) != 0) {
			return false;
		}
	#endif // WIN32

	return true;
}

select实现connect超时退出

当客户端请求ip、port出现错误时,connect()原始来阻塞模式,会导致程序一直阻塞在这里,无法执行后续代码。而select()函数就可以打破这个僵局,实现超时跳过的操作,并且具有多路复用、同时监听多个文件的功能,具体代码可以如下所示:

bool XTCP::connectSocket(const char* ip, unsigned short port, int timeoutms) {
	if (sock <= 0) {
		createSocket();
	}
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	//将字符串ip地址转化为网络地址
	saddr.sin_addr.s_addr = inet_addr(ip);

	setBlock(false);
	fd_set set;

	if (connect(sock, (const sockaddr*)&saddr, sizeof(saddr)) != 0) {
		//置空
		FD_ZERO(&set);
		FD_SET(sock, &set);
		// 两个参数的结构体,分别是秒和微秒
		timeval tm;
		tm.tv_sec = 0;
		tm.tv_usec = timeoutms * 1000;
		if (select(sock + 1, 0, &set, 0, &tm) <= 0) {
			printf("connect time or error!\n");
			// strerror(errno)将错误转化为字符串
			std::cout << "connect " << ip << " : " << port << " failed! ";
			return false;
		}
	}
	setBlock(true);
	std::cout << "connect " << ip << " : " << port << " success!\n ";
	return true;
}

更多select()函数解析可以查看网络编程之select。

apache2使用

apache用于网络测数,linux安装apt-get install apache2,安装完成后就可以使用命令进行操作了。例如:

ab -n 1000 -c 5 https://www.baidu.com/

表示连接数为1000,连接的线程数为5,访问的网址为:https://www.baidu.com/,也可以测自己本地的局域网服务器。输出结果如下图所示:

root@xiehou--ubuntu:/cpp_study/socket/src/tcpClient# ab -n 100 -c 5 https://www.baidu.com/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.baidu.com (be patient).....done


Server Software:        BWS/1.1
Server Hostname:        www.baidu.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES128-GCM-SHA256,2048,128
Server Temp Key:        ECDH P-256 256 bits
TLS Server Name:        www.baidu.com

Document Path:          /
Document Length:        227 bytes

Concurrency Level:      5
Time taken for tests:   1.305 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      139893 bytes
HTML transferred:       22700 bytes
Requests per second:    76.65 [#/sec] (mean)
Time per request:       65.231 [ms] (mean)
Time per request:       13.046 [ms] (mean, across all concurrent requests)
Transfer rate:          104.72 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       36   47  15.2     44     125
Processing:    11   15   2.6     14      33
Waiting:       11   14   2.6     14      32
Total:         49   62  15.6     58     139

Percentage of the requests served within a certain time (ms)
  50%     58
  66%     60
  75%     62
  80%     64
  90%     73
  95%    109
  98%    135
  99%    139
 100%    139 (longest request)

epoll多路复用IO高并发(Linux中)

epoll应对的现状:

  1. 大量并发连接中只有少量活跃。
  2. LT(level Triggered),水平触发, 一直触发。
  3. ET(Edge Triggered),边沿触发,只触发一次 , 只有文件描述符从不可读变为可读的时候才会被触发, EPOLLET 这个宏用于设置ET模式。

更多epoll学习资料可以参考tcp使用epoll进行实现并发。

HTTP协议

  • HTTP超文本传输协议,所有的WWW文件都必须遵守这个标准。
  • HTTP/1.0短连接(接受连接之后就会关闭),HTTP/1.1它支持持续连接,端口默认是80端口。

HTTP URL(URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息)格式如http://host[":" port][abs_path]

http表示要通过HTTP协议来定位网络资源,host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用缺省端口80;abs_path指定请求资源的URI;如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个工作浏览器自动完成。

Method Request-URI HTTP-Version CRLF,CRLF表示换行。
HTTP请求的方法具有很多种:

  • GET。请求获取Request-URI所标识的资源。
  • POST,在Request-URI所标识的资源后附加新的数据。
  • HEAD。请求获取由Request-URI所表示的资源的响应消息报头。
  • PUT。请求服务器存储一个资源,并用Request-URI作为标识。
  • DELETE。请求服务器删除Request-URI所标识的资源。
  • TRACE。请求服务器会送收到的请求消息,主要用于测试或诊断
  • CONNECT。保留将来使用。
  • OPTIONS。请求查询服务器的性能,或者查询与资源相关的选项和需求。

HTTP响应信息包含:
主要有三个部分组成,分别是:状态行、消息报头、响应正文。
状态行:HTTP-Version Status-Code Reason-Phrase CRLF。HTTP-Version表示服务器HTTP协议版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。

使用socket简单封装一个http服务器

XTCP.h

#ifndef XTCP_H //保证只初始化一次
#define XTCP_H

#ifdef WIN32
#pragma once

#ifdef XSOCKET_EXPORTS
#define XSOCKET_API __declspec(dllexport)
#else
#define XSOCKET_API __declspec(dllimport)
#endif

#else

#define XSOCKET_API

#endif // WIN32

#include<string>
class XSOCKET_API XTCP
{
public:
	int createSocket();
	bool bindListen(unsigned short port);
	void closeSocket();
	int receiveData(char* buf,int bufsize);
	int sendData(const char* buf, int sendsize);
	bool connectSocket(const char* ip, unsigned short port);
	bool connectSocket(const char* ip, unsigned short port,int timeoutms);
	bool setBlock(bool isBlock);
	XTCP acceptClient();
	XTCP();
	virtual ~XTCP();

	int sock = 0;
	unsigned short port = 0;
	char ip[16];
};

#endif // !XTCP_H

XTCP.cpp

#include "XTCP.h"
#ifdef WIN32
#include<Windows.h>
#define socklen_t int
#else
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<fcntl.h>
//函数名替换,重定义,将close关闭sock的函数转化为closesocket
#define closesocket close
#define strcpy_s strcpy
#endif
#include <iostream>
#include<stdlib.h>
#include<cstring>

XTCP::XTCP() {
#ifdef WIN32
	static bool is_first = true;
	if (is_first) {
		is_first = false;		
		//通过进程启动Winsock DLL使用
		WSADATA ws;
		WSAStartup(MAKEWORD(2, 2), &ws);		
	}
#endif
}


bool XTCP::connectSocket(const char* ip, unsigned short port, int timeoutms) {
	if (sock <= 0) {
		createSocket();
	}
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	//将字符串ip地址转化为网络地址
	saddr.sin_addr.s_addr = inet_addr(ip);

	setBlock(false);
	fd_set set;

	if (connect(sock, (const sockaddr*)&saddr, sizeof(saddr)) != 0) {
		//置空
		FD_ZERO(&set);
		FD_SET(sock, &set);
		// 两个参数的结构体,分别是秒和微秒
		timeval tm;
		tm.tv_sec = 0;
		tm.tv_usec = timeoutms * 1000;
		if (select(sock + 1, 0, &set, 0, &tm) <= 0) {
			printf("connect time or error!\n");
			// strerror(errno)将错误转化为字符串
			std::cout << "connect " << ip << " : " << port << " failed! ";
			return false;
		}
	}
	setBlock(true);
	std::cout << "connect " << ip << " : " << port << " success!\n ";
	return true;
}

bool XTCP::connectSocket(const char* ip, unsigned short port) {
	if (sock <= 0) {
		createSocket();
	}
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	//将字符串ip地址转化为网络地址
	saddr.sin_addr.s_addr = inet_addr(ip);

	if (connect(sock, (const sockaddr*)&saddr, sizeof(saddr)) != 0) {
		// strerror(errno)将错误转化为字符串
		std::cout << "connect " << ip << " : " << port << " failed! ";
		return false;
	}
	std::cout << "connect " << ip << " : " << port << " success!\n ";
	return true;
}

int XTCP::createSocket() {
	//创建socket,创建失败返回-1,AF_INET表示ipv4协议,SOCK_STREAM表示接受tcp/ip协议的数据
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock == -1) {
		std::cout << "create socket failed" << std::endl;
	}
	return sock;
}

bool XTCP::bindListen(unsigned short port) {
	if (sock <= 0) {
		createSocket();
	}
	//绑定地址
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	//大端字节序和小端字节序的问题,
	saddr.sin_port = htons(port);
	//0设置为绑定本机地址
	saddr.sin_addr.s_addr = htonl(0);

	//绑定地址
	if (bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0) {
		std::cout << "bind port " << port << " failed.\n";
		return false;
	}
	std::cout << "bind port " << port << " successful.\n";

	//监听客户端发送的信息
	//backlog=10表示缓冲大小
	listen(sock, 10);
	return true;
}

XTCP XTCP::acceptClient() {
	XTCP tcp;
	//每个连接就会生成一个client
	sockaddr_in caddr;
	socklen_t len = sizeof(caddr);
	//在accept之前会进行三次握手(由操作系统完成),accept只是获取了握手后的信息
	int client_sock = accept(sock, (sockaddr*)&caddr, &len);
	if (client_sock <= 0) {
		return tcp;
	}
	tcp.sock = client_sock;
	std::cout << "accept client " << client_sock << ".\n";
	char* ip = inet_ntoa(caddr.sin_addr);
	strcpy_s(tcp.ip, ip);
	tcp.port = ntohs(caddr.sin_port);
	std::cout << "client ip address " << tcp.ip << ".\n";
	std::cout << "client port " << tcp.port << ".\n";
	return tcp;
}


int XTCP::receiveData(char* buf, int bufsize) {
	return recv(sock, buf, bufsize, 0);
}

bool XTCP::setBlock(bool isBlock) {
	//默认为阻塞模式
	if (sock <= 0) {
		return false;
	}
	#ifdef WIN32
		unsigned long ul = 0;
		if (!isBlock) {
			ul = 1;
		}
		//ul=1就会立刻返回结果,反之,则不会立即返回,只有windows才有这个函数
		ioctlsocket(sock, FIONBIO, &ul);
	#else
		//fcntl操作文件描述符的函数,
		int flag=fcntl(sock, F_GETFL, 0);
		if (flag < 0) {
			return false;
		}
		if (isBlock) {
			flag = flag & ~O_NONBLOCK;
		}
		else {
			flag = flag | O_NONBLOCK;
		}
		if (fcntl(sock, F_SETFL, flag) != 0) {
			return false;
		}
	#endif // WIN32

	return true;
}

int XTCP::sendData(const char* buf, int sendsize) {
	//需要全部发送完全才能结束
	int sendedSize = 0;
	while (sendedSize!=sendsize) {
		//printf("--sock:%d--\n", sock);
		int len = send(sock, buf + sendedSize, sendsize - sendedSize, 0);
		if (len <= 0) {
			break;
		}
		sendedSize += len;
	}
	return sendedSize;
}

void XTCP::closeSocket() {
	if (sock <= 0) {
		return;
	}
	closesocket(sock);
}

XTCP::~XTCP() {

}

httpServer.cpp

#include"XTCP.h"
#include<iostream>
#include<thread>
#include<cstring>
#include<string>
using namespace std;

class HTTPThread {
public:
	void main() {
		char buf[10000] = { 0 };
		//接收http客户端请求
		int recvLen = client.receiveData(buf, sizeof(buf) - 1);
		if (recvLen <= 0) {
			client.closeSocket();
			delete this;
			return;
		}
		printf("===========recv=============== \n%s========================\n", buf);
		
		//回应http请求
		string rmsg = "";
		rmsg = "HTTP/1.1 200 OK\r\n";
		rmsg += "Server: XHttp\r\n";
		rmsg += "Content-Type: text/html\r\n";
		rmsg += "Content-Length: ";
		rmsg += "10\r\n";
		rmsg += "\r\n";
		rmsg += "0123456789";
		int flag=client.sendData(rmsg.c_str(), rmsg.size());
		if (flag > 0) {
			cout << "=============send=====================" << endl;
			cout << rmsg << endl;
			cout << "======================================" << endl;
		}

		client.closeSocket();
		//while (true) {
		//	//recv和send都不能保证能够发送信息和收到信息成功
		//	char buf[1024] = { 0 };
		//	//接收客户端发送得请求,并将其内容存储在buf字符串中
		//	int reclen = client.receiveData(buf, sizeof(buf) - 1);
		//	if (reclen <= 0) {
		//		break;
		//	}
		//	buf[reclen] = '\0';
		//	if (strstr(buf, "quit") != NULL) {
		//		char re[] = "quit success!\n";
		//		//服务器向客户端发送数据
		//		client.sendData(re, strlen(re) + 1);
		//		break;
		//	}
		//	int sendlen = client.sendData("ok\n", 4);
		//	cout << "receive data: " << buf << endl;
		//}
		//client.closeSocket();
		delete this;
	}
	XTCP client;
};
int main(int argc, char* argv[])
{
	//获得端口号
	unsigned short port = 80;
	if (argc > 1) {
		port = atoi(argv[1]);
	}
	XTCP server;
	server.createSocket();
	server.bindListen(port);

	while (true) {
		//每个连接就会生成一个client
		XTCP client = server.acceptClient();
		HTTPThread* th = new HTTPThread();
		th->client = client;
		//启动多线程,第一个参数是函数地址,第二个为对象本身
		thread sth(&HTTPThread::main, th);
		//释放主线程中子线程的资源
		sth.detach();

	}
	//socket关闭
	server.closeSocket();

	return 0;
}

UDP协议

UDP:用户数据报协议,具有特性

  • UDP提供无连接服务。
  • UDP缺乏可靠性支持,应用程序必须实现:确认、超时、重传、流控等。
  • UDP面向记录服务。

UDP的数据报格式:

Windows配置C++的UDP协议
在VS中 项目属性 -> 连接器 -> 输入 -> 附加依赖项添加ws2_32.lib

没有配置则会显示socket相关的函数没有找到。

UDP通信Windows版本

udpserver.cpp

#include<Windows.h>
#include <iostream>

using namespace std;
int main(int argc,char* argv[])
{   
    unsigned short port = 8080;
    if (argc > 1)
    {
        port = atoi(argv[1]);
    }

    WSADATA ws;
    WSAStartup(MAKEWORD(2, 2), &ws);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock <= 0)
    {
        cout << "create socket failed!" << endl;
        return -1;
    }
    sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    saddr.sin_addr.s_addr = htonl(0);
    if (bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0)
    {
        cout << "bind port " << port << " failed!" << endl;
        return -2;
    }

    cout << "bind port " << port << "success!" << endl;
    listen(sock, 10);
    sockaddr_in client;
    int len = sizeof(client);
    char buf[10240] = { 0 };
    int re = recvfrom(sock, buf, sizeof(buf), 0, (sockaddr*)&client, &len);
    if (re <= 0)
    {
        cout << "recefrom failed!" << endl;
        return -3;
    }
    buf[re] = '\0';
    cout << buf << endl;

    return 0;
}

udpclient.cpp

#include<Windows.h>
#include <iostream>

using namespace std;
int main(int argc, char* argv[])
{
    unsigned short port = 8080;
    if (argc > 1)
    {
        port = atoi(argv[1]);
    }

    WSADATA ws;
    WSAStartup(MAKEWORD(2, 2), &ws);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock <= 0)
    {
        cout << "create socket failed!" << endl;
        return -1;
    }
    sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");       //htonl(0);

    int len=sendto(sock, "12345", 6, 0,(sockaddr*)&saddr, sizeof(saddr));
    cout << "sendto size is " << len << endl;
    return 0;
}

UDP客户端和服务器端通信成功!!!

UDP广播

广播所用的广播地址为255.255.255.255

本地广播信息是不会被路由器转发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/826954.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构 | 递归

目录 一、何谓递归 1.1 计算一列数之和 1.2 递归三原则 1.3 将整数转换成任意进制的字符串 二、栈帧&#xff1a;实现递归 三、递归可视化 四、谢尔平斯基三角形 五、复杂的递归问题 六、动态规划 一、何谓递归 递归是解决问题的一种办法&#xff0c;它将问题不断地分…

本地mvn仓库清理无用jar包

背景 开发java时间久了&#xff0c;本地的m2仓库就会产生很多过期的jar包&#xff0c;不清理的话比较占空间。 原理 是通过比较同一目录下&#xff0c;对应jar包的版本号的大小&#xff0c;保留最大版本号那个&#xff0c;删除其他的。 脚本 执行脚本见文章顶部 执行方式 …

P3374 【模板】树状数组 1 浅谈树状数组 (内附封面)

【模板】树状数组 1 题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行下面两种操作&#xff1a; 将某一个数加上 x x x 求出某区间每一个数的和 输入格式 第一行包含两个正整数 n , m n,m n,m&#xff0c;分别表示该数列数字的个数和操作的总个数。 第二…

部署安装私服-Gitlab

一、国内的gitlab是极狐 www.gitlab.cn 国服 www.github.com 国际服 二、国服的gitlab蛮适合中国国情的 1.提交申请可以获得30天的订阅版服务&#xff0c;有需要的话可以先提交一下。订阅后功能多一些。 Gitlab中文官网下载_GitLab免费下载安装_极狐GitLab免…

从零开始学python(十四)百万高性能框架scrapy框架

前言 回顾之前讲述了python语法编程 必修入门基础和网络编程&#xff0c;多线程/多进程/协程等方面的内容&#xff0c;后续讲到了数据库编程篇MySQL&#xff0c;Redis&#xff0c;MongoDB篇&#xff0c;和机器学习&#xff0c;全栈开发&#xff0c;数据分析&#xff0c;爬虫数…

uniapp 视频截图

uniapp 视频截图 本文只针对微信小程序&#xff0c;其他平台并没有测试过&#xff0c;不确定可行性。 微信提供了两个组件可以用来播放视频&#xff1a; live-player: 只要用于实时音视频的播放&#xff08;出于政策和合规的考虑&#xff0c;微信暂时没有放开所有小程序对&l…

地统计学空间插值方法及应用

地统计学 地统计学&#xff0c;是指以具有空间分布特点的区域化变量理论为基础&#xff0c;研究自然现象的空间变异与空间结构的一门学科。它是针对像矿产、资源、生物群落、地貌等有着特定的地域分布特征而发展的统计学。由于最先在地学领域应用&#xff0c;故称地统计学 地…

自学网络安全(黑客)学习心得路线规划

趁着今天下班&#xff0c;我花了几个小时整理了下&#xff0c;非常不易&#xff0c;希望大家可以点赞收藏支持一波&#xff0c;谢谢。 我的经历&#xff1a; 我 19 年毕业&#xff0c;大学专业是物联网工程&#xff0c;我相信很多人在象牙塔里都很迷茫&#xff0c;到了大三大…

html | 无js二级菜单

1. 效果图 2. 代码 <meta charset"utf-8"><style> .hiddentitle{display:none;}nav ul{list-style-type: none;background-color: #001f3f;overflow:hidden; /* 父标签加这个&#xff0c;防止有浮动子元素时&#xff0c;该标签失去高度*/margin: 0;padd…

4年测试工程师,常用功能测试点总结,“我“不再走弯路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 输入框测试 1、字…

模电专题-MOS管的放大电路分析

在实际应用中&#xff0c;我们经常会使用到功率MOS&#xff0c;这时通常不会将它当成一个开关使用&#xff0c;而是当成一个放大器来使用&#xff0c;那这就需要让其工作在放大状态。 参考下图中的mos管的特性曲线&#xff0c;右图中的输出特性曲线中有一根红色的分界线&#x…

2000-2021年上市公司常用控制变量数据(A股)含stata处理代码

2000-2021年上市公司企业A股常用控制变量 1、时间&#xff1a;2000-2021年&#xff08;注&#xff1a;股权性质从2004年开始&#xff0c;第一大股东持股比例从2003年开始&#xff09; 2、来源&#xff1a;整理自csmar 和wind 3、数据范围&#xff1a;A股公司 不包含已退市的…

使用Python动画粒子的薛定谔波函数(ψ)(完整代码)

使用Python动画粒子的薛定谔波函数&#xff08;ψ&#xff09;&#xff08;完整代码&#xff09; 使用曲柄-尼科尔森方法求解盒子中的粒子 Kowshik chilamkurthy 以后 发表于 书技术 4 分钟阅读 2月 2021&#xff0c; <> 1.4K 5 左图&#xff1a;来源&#xff0c;右图…

Leetcode-每日一题【剑指 Offer 56 - II. 数组中数字出现的次数 II】

题目 在一个数组 nums 中除一个数字只出现一次之外&#xff0c;其他数字都出现了三次。请找出那个只出现一次的数字。 示例 1&#xff1a; 输入&#xff1a;nums [3,4,3,3]输出&#xff1a;4 示例 2&#xff1a; 输入&#xff1a;nums [9,1,7,9,7,9,7]输出&#xff1a;1 限制…

网工必须掌握的5种组网技术,你会了吗?

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、VLAN技术 1、VLAN是什么&#xff1f; 2、VLAN的作用 ①提高网络安全性 ②提高了网络的灵活性性 ③增强了网络的健壮性 二、D…

SPDK的块设备抽象层,从一个简单的示例程序讲起

最早的SPDK仅仅是一个NVMe驱动,但现在的SPDK已经不是原来的SPDK了,其功能涵盖了整个存储栈。为了能够实现丰富的功能,SPDK实现了一个块设备抽象层,其功能与Linux内核的块设备层类似,这个块设备抽象层称为BDEV。 块设备抽象层BDEV在整个SPDK栈中的位置如图所示,它位于中间…

解决一个Yarn异常:Alerts for Timeline service 2.0 Reader

【背景】 环境是用Ambari搭建的大数据环境&#xff0c;版本是2.7.3&#xff0c;Hdp是3.1.0&#xff1b;我们用这一套组件搭建了好几个环境&#xff0c;都有这个异常告警&#xff0c;但hive、spark都运行正常&#xff0c;可以正常使用&#xff0c;所以也一直没有去费时间解决这…

Linux lvs负载均衡

LVS 介绍&#xff1a; Linux Virtual Server&#xff08;LVS&#xff09;是一个基于Linux内核的开源软件项目&#xff0c;用于构建高性能、高可用性的服务器群集。LVS通过将客户端请求分发到一组后端服务器上的不同节点来实现负载均衡&#xff0c;从而提高系统的可扩展性和可…

读磁盘概述

磁盘结构 磁道C 磁头H 扇区S 一个磁盘有很多个盘面&#xff0c;上面是其中一个盘面&#xff0c;每个盘面对应一个磁头。 磁盘的最小单元是扇区&#xff0c;通过CHS可以定位到一个确定的扇区&#xff0c;每个扇区一般是512个字节。 CHS寻道方式 设置好寄存器的值&#xff0c;然…

ElasticSearch可视化管理工具之ElasticHD

推荐的五种客户端 1.Elasticsearch-Head &#xff0c; Elasticsearch-Head 插件在5.x版本之后已不再维护&#xff0c;界面比较老旧。 2.cerebro 据传该插件不支持ES中5.x以上版本。 3.kinaba 功能强大&#xff0c;但操作复杂&#xff0c;以后可以考虑。 4.Dejavu 也是一个 Elas…