C++socket网络编程(跨平台)实战HTTP服务器(二)

使用Socket创建TCP服务器

1首先了解一下TCP

1TCP是面向连接的,必须是三次握手之后

2TCP提供可靠连接,实现丢失重传,RTT的估算物理 网卡 网线都会影响 这个丢包

3TCP通过给所发数据的每一个段管理一个序列号进行排序. 没一个包都有一个序号,由底层按照序列号发送给你

4TCP提供流量控制和拥塞控制:通过窗口拥塞窗口可以限制流量的,1000个客户TCP可以限定他能够是以多块的速度来接收你的数据TCP的连接是全双工的. 互相不干扰发送和接收是同时进行的

首先我们配置一下Linux环境非常简单哦

地址是:http://12158490.blog.51cto.com/12148490/1947803

Linux需要安装g++ gcc makefile

1开始写代码在windows上编码,但是我们的工程是在linux目录下的

1初始化

/初始化windows网络库,这里就会载入动态库文件
所以我们要载入lib文件,可以在vs载入也可以代码
#pragma comment(lib,"ws2_32.lib")
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws);

2 初始化完成就是创建一个socket了,socket反回的

是一个int型,是与客户建立建立的socket,第一个参数是使用的

协议是tcp/ip,应为socket还可以用来蓝牙的通信协议

第二个参数是使用TCP还是UDP这里SOCK_STREAM是TCP

第三个参数是原始套接字的时候用到的为0就行

int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
printf("create err\n");
return -1;
}
printf("[%d]", sock);

3绑定

sockaddr_in是一个结构体,他记录端口和ip地址以及协议族.

1需要指定端口。

2绑定本地的ip地址,如果绑定127.0.0.1就只能本地访问

这个网络,如果绑定外网ip那只能从外部访问,内部不能访问

一台机器可以有两个网卡,如果要任意都可以访问那就直接htonl(0),将ip和杜端口绑定到哪个socket,

bind()如果不等于0会失败,而且很容易失败,最好判断一下。

        enum{PORT=8017}
 
	sockaddr_in sa;
	sa.sin_family = AF_INET;
	sa.sin_port = htons(PORT);
	sa.sin_addr.s_addr = htonl(0);

	if (bind(sock, (sockaddr*)&sa, sizeof(sa)) != 0)
	{
		printf("bind port err\n");
		return -2;
	}
	printf("bind port ok!!!\n");

4侦听

绑定只是做了一个标识,还没有等待客户的连接.

这个函数一调用开始接收用户的连接了。第二个参数

是该套接字使用的队列长度,指定在请求队列总允许的最大请求

数,意思就是如果你接收10个连接还没有用Accept进行接待就会把新的连接扔掉。

listen(socket,10);

4获得连接用户信息

这个函数是获得获得用户的连接信息,他反回一个int是用来真正

和用户进行收发数据的,第一个参数之前创建的sock是和用户建立连接的,第二个参数是输出的参数,就是你穿进去之后,在里面进行赋值,

然后输出出来, 在访问结构体的成员就可以获得ip和端口了。

如果不需要ip地址和端口,只需要直接传人0就行。

这个时候就可以创建线程和用户进行通信了,

	sockaddr_in sadd;
	socklen_t len = sizeof(sadd);

	int client = accept(sock, (sockaddr*)&sadd, &len);

	if (client > 0)
	{
		printf("client connect ok\n");
		printf("sockeid: %d\n", client);

		//输出端口号和信息
		char *ip = inet_ntoa(sadd.sin_addr);
		//网络字节序转本地字节序哦
		unsigned short port = ntohs(sadd.sin_port);
		printf("IP: %s\n", ip);
		printf("port: %d\n", port);
		closesocket(client);
	}

一般情况下每建立一个连接,会把他传到一个新的线程当中,简单的方案是把他来的时候就创建一个新的线程复杂的方案是创建一个线程池,每来一个连接,把他扔给闲置的线程.

我们在linux进行编译首先打卡这个工程路劲

编写一个makefile

简单说下makefile

上面的4个类似预定义

CC是编译器

SRC是源文件

OBJ是编译的文件

EXEC编译连接可执行文件

$(OBJ)代表简单点就是使用这个预定义一样类似

start: $(OBJ)

$(CC) -o $(EXEC) $(OBJ)

上面依赖OBJ他会先执行这个

$(OBJ):

$(CC) -o $(OBJ) -c $(SRC)//这里只编译

//删除只编译的文件

clean:

rm -f $(OBJ)

//执行 可执行文件

run:

./$(EXEC)

启动make start

这时候会出现很多错误,

原因:

linux,用不到windows的库文件,也不需要初始化

,还有一些api不一致。

解决方案:

#ifdef WIN32  如果是 WIN32情况下调用哪些

#else    就是不再windows环境下用到的

#else

man socket 查看需要的头文件

用起来很方便

在红色部分,填写linux需要的东西,这个东西会

在编译的时候就会执行,他发现不是windows就是会执行#else

里的代码.

其他的也是一样的。 clossocket 缓冲 close就行

然后就可以编译成功了。

make start编译生成一下

然后make run执行一下

启动另外一个linux测试一下

telnet 192.168.1.125 8013 测试连接一些

然后左边我们看到把

连接的用户的 IP和端口都打印了。

基本流程都有了,后面的话,只需要照着编译就行。

我们开始recv接收客户端发送数据

第一个参数是用户和你连接之后,返回的

那个socket哦,第二个参数是存储数据的缓冲,

第三个参数是缓冲大小-1是为了留意味放/0结束符

第四个0就行了,跟系统有关的东西

返回值就是收到数据的大小

实际返回值有时候会大于1024 但是发送方会

有一些数据的切割,所以很容易造成错误

char buf[1024];
memset(buf, 0, sizeof(buf));
int len = recv(client, buf, sizeof(buf) - 1, 0);
print("recv %s\n",buf);

在Linux编译执行一下

在发送端连接后发送数据

服务器收到数据通信成功

循环接收用户的信息,在死循环里接收,如果收到的数据<=就退出

如果用户发送的里面有 "quit"就会退出

for (;;)
{
memset(buf, 0, sizeof(buf));
int recv_len = recv(client, buf, sizeof(buf) - 1, 0);
if (recv_len <= 0)break;
if(strstr(buf, "quit")!= NULL) break;
printf("recv %s\n", buf);
}

在Linux编译执行一下

在发送端连接后发送数据

多线程并发处理 直接用c++11的线程库。不需要考虑跨平台

的问题.

创建线程的地方就是,accept然后会返回的一个socket

给这个线程用

#include <thread>
using namespace std;
class TcpThread
{
public:
    //线程入口函数 创建一个
    void Main()
    {
        char buf[1024];
      for (;;)
	    {
		memset(buf, 0, sizeof(buf));
		int recv_len = recv(client, buf, sizeof(buf) - 1, 0);

		if (recv_len <= 0)break;
		if (strstr(buf, "quit") != NULL)
		{
			char re[] = "quit success\n";
			int sendlen = send(client, re, strlen(re)+1, 0);

		}

		int sendlen = send(client, "ok\n",4, 0);
		printf("recv %s\n", buf);

	    }

	    closesocket(client);
        
    }
    
    //用户的socket
    int client = 0;
    

};

如何创建线程呢,获得用户信息accept应该放到死循环里

应为线程需要他的返回值.

创建一个线程  需要考虑什么时候清理

几种方案 1对象复用 2自己清理

for (;;)
{
//这里会阻塞 只有等待有新的连接 才会执行下面的代码
int client = accept(sock, (sockaddr*)&sadd, &len);
if (client > 0)
{
printf("client connect ok\n");
printf("sockeid: %d\n", client);
//输出端口号和信息
char *ip = inet_ntoa(sadd.sin_addr);
//网络字节序转本地字节序哦
unsigned short port = ntohs(sadd.sin_port);
printf("IP: %s\n", ip);
printf("port: %d\n", port);
}
else
{
break;
}
//创建一个线程  需要考虑什么时候清理
//几种方案 1对象复用 2自己清理
TcpThread *th = new TcpThread();
th->client = client; //获得socket
//启动线程 第一个参数是入口函数的地址(函数指针)
//第二个参数调用的对象
std::thread sth(&TcpThread::Main,th);
//上面的调用完后 对象 会被销毁掉
//销毁不受影响 但是本线程还有一些资源没有被释放掉
// 我们可以直接让他直接释放调用,主线程不要控制子线程的处理,比如挂起啊 或者关闭
//这种操作是很危险的,因为 主线程不知道子线程运行到什么阶段 
//正常情况我们不去处理 detach()  释放主线程拥有的子线程的资源
sth.detach();
}

然后移植到linux  makefile需添加 std c++11 才可以编译成功

还需要添加 -lpthread

上面的有点问题

$(CC) $(OBJ) -o $(EXEC) -std=c++11 -lthread

时间: 2024-10-25 17:35:24

C++socket网络编程(跨平台)实战HTTP服务器(二)的相关文章

C++socket网络编程(跨平台)实战HTTP服务器(四)

TCP客户端 直接用上次封装的dll动态库,linux则用so文件.首先生成编译一下. #include "XTCP.h" int main(int argc,char*argv[]) {     XTCP client;     getchar();     return 0; } TCP三次握手协议详解 服务器创建,绑定,并且listen开始监听. 客户端也要创建socket,通过这个socket调用connect.他是一个阻塞的 函数,他是要主动打开,他会先发一个SYN J(协议

Socket网络编程进阶与实战

第1章 课程导学(Java语言教学)[说明:课程案例部分以Java语言实现]本章节首先会对课程进行导学讲解,包括为什么应该学习本课程,课程目标与收获,课程内容安排,适合人群和学习建议等,接着会讲解代码规范与开发注意事项,目的全在于希望极大的方便同学进行本课程的学习.... 第2章 Socket网络编程快速入门本章首先整体介绍什么是Socket网络编程:让大家对Socket有个大概的概念与方向.之后通过几个小Case引出课程的主角,轻松愉快的让你体验到Socket编程的快感.本章节主要收获:1.

[8421论坛]2019 Socket网络编程入门到进阶与实战完整视频教程

第一章 课程介绍及java语言简介第二章 快速入门Socket网络编程第三章 快速入门Socket UDP第四章 快速入门Socket TCP第五章 UDP辅助TCP实现点对点传输案例第六章 简易聊天室案例第七章 服务器传输优化-NIO第八章 数据传输稳定性优化第九章 局域网文件快传技术实战第十章 聊天室升级版实战第十一章 语音数据即时通信实战第十二章 整体代码结构梳理与升华资料 下载地址? 原文地址:https://www.cnblogs.com/yellowvase/p/10720644.h

Socket网络编程--简单Web服务器(1)

这一次的Socket系列准备讲Web服务器.就是编写一个简单的Web服务器,具体怎么做呢?我也不是很清楚流程,所以我找来了一个开源的小的Web服务器--tinyhttpd.这个服务器才500多行的代码,使用C语言.这一小节就不讲别的内容了.就对这个程序进行一些注释和讲解了. 主函数: 1 int main(void) 2 { 3 int server_sock = -1; 4 u_short port = 0; 5 int client_sock = -1; 6 struct sockaddr_

Socket网络编程--简单Web服务器(6)

本来是想实现ssl连接的,但是弄了好久都不成功,就索性不做了,等以后有能力再做了.所以这一小节就是本次的最后一节了.就简单的说几个注意点. 1.加个配置文件 使用单例模式,使用一个类,该类保存一些信息,例如一个配置类的一个属性为PAGE404的字符串,该字符串保存一个文件地址,然后我们的Page_404函数就可以用access判断PAGE404这个字符串对应的文件是否存在,如果存在那么如果是404页面那么就cat这个文件,而不是默认的404函数里面的页面.还有个端口什么的都是通过一个类似宏定义一

windows下的socket网络编程(入门级)

windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws32_lib库. 大致过程如下 1.初始

windows下的socket网络编程

windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws

socket 网络编程快速入门(一)教你编写基于UDP/TCP的服务(客户端)通信

因为UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,因为这些函数的结构往往比较复杂,参数大部分都是结构体,令人难以记忆和理解. 但是一旦我们知道这些函数包括其参数的具体含义,socket网络编程也就变得不是那么复杂.这里不赘述 具体函数的详细含义,网络上有很多的文章,同时笔者建议大家参考 MSDN,对返回值,参数等会有更好的理解. 以下均为单线程的简单实例,多线程的请关注下一篇文章. (

Socket网络编程--网络爬虫(1)

我们这个系列准备讲一下--网络爬虫.网络爬虫是搜索引擎系统中十分重要的组成部分,它负责从互联网中搜集网页,采集信息,这些网页信息用于建立索引从而为搜索引擎提供支持,它决定着整个引擎系统的内容是否丰富,信息是否即时,因此其性能的优劣直接影响着搜索引擎的效果.网络爬虫的基本工作原理: (1)从一个初始URL集合中挑选一个URL,下载该URL对应的页面: (2)解析该页面,从该页面中抽取出其包含的URL集合,接下来将抽取的URL集合再添加到初始URL集合中: (3)重复前两个过程,直到爬虫达到某种停止