当前位置:网站首页>套接字编程TCP篇
套接字编程TCP篇
2022-06-12 11:11:00 【Diligent_wu】
TCP通信编程
TCP通信编程的流程:
模拟服务端和客户端通信的过程:
接口的使用:
1.创建套接字:
int socket(int domain,int type,int protocol)
domain:地址域 AF_INET/AF_INET6
type:SOCK_STREAM(TCP流式套接字) SOCK_DGRAM(UDP数据报套接字)
protocol:IPPROTO_TCP IPPROTO_UDP
返回socket操作句柄
2.绑定地址信息:
int bind(int sockfd,(struct sockaddr*)addr,socklen_t len)
3.开始监听
listen(int sockfd,int backlog)
sockfd:将哪个socket设置为监听套接字,就传入哪个套接字的句柄
backlog:同一时间的并发连接数,决定同一时间最多接受多少个客户端的连接请求
如果有一个恶意主机同一时间向服务器无限制的发送连接请求,服务端是不是要一直为每一条请求创建套接字?如果是这样,最终只有一个结构就是资源耗尽,系统崩溃。
backlog就是解决这个问题的,前面流程里面说到,监听阶段,监听服务器收到连接请求创建套接字放入未完成连接队列中,绑定好五元组信息后放入已完成连接队列中。
backlog就是决定这个未完成连接队列的节点个数的。但只决定同一时间的节点个数,但不决定系统可以接受的最大客户端连接。
4.获取新建连接
从已完成连接队列中取出一个socket,返回这个socket的操作句柄,这样就可以通信了。
int accept(int sockfd,(struct sockaddr*)cli_addr,socklen* len);
sockfd : 监听套接字,表示要获取哪个tcp服务端套接字的新建连接(可能会有很多TCP服务端)
cli_addr:这个新建套接字的客户端地址信息
len:地址信息长度
返回值:新建套接字的操作句柄 --- 外部程序中的套接字句柄
5.收发数据
ssize_t recv(int sockfd,char* buf,int len,int flag);
默认阻塞,没有数据则等待,连接断开返回0,不在阻塞
ssize_t send(int sockfd,char* data,int len,int flag);
默认阻塞,缓冲区满了则等待,连接断开则出发SIGPIPE异常
6.关闭套接字
close(sockfd); #include<unistd.h>
7.发送连接请求
int connect(int sockfd,struct sockaddr* srv_addr, int len);
srv_addr:服务端的地址信息
connet这个接口中也会描述服务端的地址信息(五元组),所以收发数据只需要直接收发即可。
简易TCP通信流程
socket类的封装
封装一个TcpSocket类,实例化的每一个对象都是一个udp通信连接,通过这个对象的成员方法完成通信流程。
class TcpSocket{
public:
TcpSocket():_sockfd(-1);
bool Socket();
bool Bind(const std::string& ip,uint16_t port);
bool Listen(int backlog = MAX_LISTEN);
bool Accept(TcpSocket* new_sock,std::string* ip =NULL,uint16_t* port);
bool recv(std::string* buf);
bool send(const std::string& data);
bool Close();
bool Connect(const std::string& ip,uint16_t port);
private:
int _sockfd;
#pragma once
#include<iostream>
#include<string>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
class TcpSocket
{
private:
int sockfd;
public:
TcpSocket()
{
if(!Socket())
{
cout<<"socket create failed"<<endl;
}
}
~TcpSocket()
{
Close();
}
public:
bool Socket()
{
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == 0)
{
cerr<<"scoket error"<<endl;
return false;
}
return true;
}
//有可能错
void Addr(struct sockaddr_in* addr, const string& ip,const uint16_t& port)
{
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
addr->sin_addr.s_addr = inet_addr(ip.c_str());
}
bool Bind(const string& ip,const uint16_t& port)
{
struct sockaddr_in addr;
Addr(&addr,ip,port);
socklen_t len = sizeof(addr);
int ret = bind(sockfd,(struct sockaddr*)&addr,len);
if(ret < 0)
{
cerr<<"bind errror"<<endl;
return false;
}
return true;
}
bool Listen()
{
int ret = listen(sockfd,5);
if(ret == -1)
{
cerr<<"listen error"<<endl;
return false;
}
return true;
}
bool Connect(const string& ip,const uint16_t& port)
{
struct sockaddr_in addr;
Addr(&addr,ip,port);
socklen_t len = sizeof(addr);
int ret = connect(sockfd,(struct sockaddr*)&addr,len);
if(ret < 0)
{
cerr<<"connect error"<<endl;
return false;
}
return true;
}
bool Accept(TcpSocket* new_sock,string* ip = NULL,uint16_t* port = NULL)
{
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int newfd = accept(sockfd,(struct sockaddr*)&addr,&len);
if(newfd < 0)
{
cerr<<"accept error"<<endl;
return false;
}
new_sock->sockfd = newfd;
if(ip != NULL)
{
*ip = inet_ntoa(addr.sin_addr);
}
if(port != NULL)
{
*port = ntohs(addr.sin_port);
}
return true;
}
bool Recv(string* buf)
{
char tmp[4096] = {
0};
int ret = recv(sockfd,tmp,4095,0);;
if(ret <0)
{
cerr<<"recv error"<<endl;
return false;
}
*buf = tmp;
buf->assign(tmp);
return true;
}
bool Send(const string& data)
{
//UDP是数据报传输,整条交付
//而TCP是字节流传输,能传输多少就传输多少,对数据边界并没有进行区分
//如果对方的缓冲区只剩了50个字节的空间,send给对方100个字节,就只能成功发送50个字节,并返回传送大小50字节
size_t total = 0 ;
while(total < data.size())
{
int ret = send(sockfd,&data[0]+total,data.size()-total,0);
if(ret < 0)
{
cerr<<"send error"<<endl;
return false;
}
total+=ret;
}
return true;
}
bool Close()
{
close(sockfd);
return true;
}
};
边栏推荐
- The reason why scanf return value is ignored and its solution
- Zabbix 监控之LLD
- Flex layout
- The difference between meta universe chain games and traditional games
- Leetcode 162 Looking for peak value (June 11, 2022)
- Php中redis的keys问题
- Vite Basics
- The most detailed explanation of the top ten levels of sqli labs platform
- LVS基于应用层的健康状态检测
- Leetcdoe 2037. Make each student have the minimum number of seat movements (yes, once)
猜你喜欢

Malicious code analysis practice - lab03-02 DLL analysis

InfoQ geek media's 15th anniversary solicitation |position:fixed virtual button cannot take effect after being triggered. Problem analysis and Solution Exploration

Reading mysql45 lecture - self summary (part)

分布式存储探索

selenium使用代理IP

Pseudo static setting of access database in win2008 R2 iis7.5

logrotate日志轮转方式create和copytruncate原理

Don't swallow rice with vinegar! Teach you 2 moves to make the fish bones "run out" safely

Tp6+memcached configuration

How the ArrayList collection implements ascending and descending order
随机推荐
PHP get (remote) large file method record
Find the location of a function in PHP
2022-06-11:注意本文件中,graph不是邻接矩阵的含义,而是一个二部图。 在长度为N的邻接矩阵matrix中,所有的点有N个,matrix[i][j]
Common methods of string class
疫情居家办公体验 | 社区征文
(37) How bee uses different data source instances at the same time
信号继电器RXSF1-RK271018DC110V
M-Arch(番外12)GD32L233评测-CAU加解密(捉弄下小编)
Redis keys in PHP
【CF1392D】D. Omkar and Bed Wars(环形与后效性dp)
Leetcdoe 2037. Make each student have the minimum number of seat movements (yes, once)
DS18B20 digital thermometer (I) electrical characteristics, power supply and wiring mode
AcWing 1986. Mirror (simulation, ring diagram)
重量级代理缓存服务器Squid
PHP uses RSA segment encryption and decryption method
(三十七)Bee如何同时使用不同数据源实例
On 3dsc theory and application of 3D shape context feature
Why check the @nonnull annotation at run time- Why @Nonnull annotation checked at runtime?
Bug easily ignored by recursion
Mysql5.6.24 installation free deployment method