当前位置: 首页 > news >正文

网络编程Socket linux

目录

端口号(port)

Socket(套接字)

TCP与UDP介绍

网络字节序

Socket编程预备

UDP编程

创建

ip和port的获取

绑定

读取

发送

示例代码

TCP编程

创建

listen

accept

connect

示例代码

Inet_ntoa和Inet_ntop

小知识


端口号(port)

1.是传输层协议的内容,用来标识一台主机中的进程

2.是一个2字节,16比特位的整数

3.一个端口号只能被一个进程占用,一个进程可以绑定多个端口号

4.IP地址+端口号能够表示网络上的某台主机的一个进程


端口号范围

0 - 1023: 知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议, 他们的端口号都是固定的.

1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的.

Socket(套接字)

IP+端口号(port) = socket   

TCP与UDP介绍

TCP

1.是传输层协议

2.有连接

3.可靠传输

4.面向字节流


UDP

1.是传输层协议

2.无连接

3.不可靠传输

4.面向数据流

可靠传输:如果数据传输过程中出现丢包等问题,会采取纠错,确认和控制重传等机制确保数据完整,准确送达.

不可靠传输:发送之后,不管数据在传输过程中出现的问题.

两种方式各有优缺


面向字节流:将数据看做一连串的字节,对于一组数据,可以先发一部分字节,再发另外的部分

面向数据流:将数据看做一组组包,发送时将整个包发送

网络字节序

TCP/IP协议规定,网络数据流采用大端字节序,即低地址高字节.

所以在编程时,要注意字节序的转化.

• h 表示 host,n 表示 network,l 表示 32 位长整数(ip地址),s 表示 16 位短整数(port端口号)。
• 例如 htonl 表示将 32 位的长整数从主机字节序转换为网络字节序.
• 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;

• 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

Socket编程预备


// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);



// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);



// 开始监听 socket (TCP, 服务器)
int listen(int socket, int backlog);



// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);



// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

struct sockaddr结构体

因为网络通信有不同的场景,所以socket的创造者为了省事,用一套接口适用了不同场景.

在三种场景中使用时,接口是统一的,只需要改动点宏和转化结构体就可以了

UDP编程

网络编程这里使用sockaddr_in

创建

domain:是协议簇或叫域,传不同的宏适用于不同场景,    传AF_INET :适用IPv4

type:套接字类型,   UDP协议使用SOCK_DGRAM

protocol :默认为0即可

返回值:成功返回文件描述符

ip和port的获取

通过命令行人手动传

绑定

sockfd:创建一个socket时返回的那个socket文件描述符

addr:结构体,下面介绍

addrlen:输入型参数,表示传入的结构体大小

返回值 :成功返回0,失败返回-1


sockaddr结构体内容

sin_port:端口号

sin_addr:ip地址

sin_family:协议簇

1.bzero用来将local清0,可以使用其他函数比如memset

2.htons(_port)将主机字节序转化为网络字节序

3.htonl(INADDR_ANY)用来将主机字节序转化为网络字节序,INADDR_ANY这个宏表示IP地址为该主机任意ip地址

读取

buf:输出型参数

flags: 设置为0,如果读不到数据会阻塞

src_addr:输出型参数,可以获取发送数据的一方的sockaddr

发送

参数和读取类似

示例代码

测试时使用可以127.0.0.1ip,是本地环回,常用来做本地测试,不会到数据链路层,数据送到网络层然后返回本地的应用层,port注意范围即可

<EchoServer.hpp>

#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <cstring>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "logger.hpp"class UdpServer
{int _sockfd = -1;uint16_t _port;bool _isrunning;public:UdpServer(int16_t port):_port(port),_isrunning(false){}void Init(){//创建socket_sockfd = socket(AF_INET,SOCK_DGRAM,0);if(_sockfd<0){LOG(Loglevel::FATAL) << "create socket error";exit(1);}LOG(Loglevel::INFO) << "create socket success";struct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);//local.sin_addr.s_addr = inet_addr(_ip.c_str());local.sin_addr.s_addr = htonl(INADDR_ANY);int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n<0){LOG(Loglevel::FATAL) << "bind socket error";exit(2);}LOG(Loglevel::INFO) << "bind socket success";}void Start(){_isrunning = true;while(_isrunning){char buffer[1024];buffer[0] = 0;//清空缓冲区struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,&len);if(n>0){buffer[n] = 0;LOG(Loglevel::DEBUG) << "client say : " <<buffer;std::string echo_string = "server echo : ";echo_string += buffer;sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,(struct sockaddr*)&peer,len);}}_isrunning = false;}void Stop(){   _isrunning = false;}~UdpServer(){}};

<EchoServer.cpp>

#include <iostream>
#include "udpServer.hpp"
#include <string>
#include <memory>void Usage()
{std::cerr << "Usage is : ./udpc serverport" << std::endl;
}int main(int argc,char* argv[])
{if(argc!=2){Usage();exit(3);}uint16_t serverport = std::stoi(argv[1]);UdpServer udps(serverport);udps.Init();udps.Start();return 0;
}

<EchoClient.cpp>   客户端不需要bind绑定,系统会在首次发送时自动绑定,会随机绑定端口号

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include "logger.hpp"void Usage()
{std::cerr << "Usage is : ./udpc serverip serverport" << std::endl;
}int main(int argc,char* argv[])
{if(argc!=3){Usage();exit(4);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){LOG(Loglevel::FATAL) << "create socket error";exit(5);}LOG(Loglevel::INFO) << "create socket success";struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_addr.s_addr = inet_addr(serverip.c_str()); server.sin_family = AF_INET;server.sin_port = htons(serverport);while(true){std::cout<<"Please input@ ";std::string line;std::getline(std::cin,line);sendto(sockfd,line.c_str(),line.size(),0,(struct sockaddr*)&server,sizeof(server));struct sockaddr_in temp;socklen_t len = sizeof(temp);char buffer[1024];buffer[0] = 0;ssize_t n = recvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&len);if(n > 0){buffer[n] = 0;LOG(Loglevel::INFO) << buffer;}}return 0;
}

<logger.hpp>

代码在之前的线程(四) 线程池中

<mutex.hpp>

#pragma once
#include <iostream>
#include <unistd.h>
#include <pthread.h>class Mutex
{pthread_mutex_t _mutex;public:Mutex(const Mutex&) = delete;const Mutex& operator = (const Mutex&) = delete;Mutex(){int n = pthread_mutex_init(&_mutex,nullptr);(void)n;}void Lock(){int n = pthread_mutex_lock(&_mutex);(void)n;}void Unlock(){int n = pthread_mutex_unlock(&_mutex);(void)n;}pthread_mutex_t *Get(){return &_mutex;}~Mutex(){int n = pthread_mutex_destroy(&_mutex);(void)n;}
};class LockGuard
{Mutex &_mutex;
public:    LockGuard(Mutex& mutex):_mutex(mutex){_mutex.Lock();}pthread_mutex_t *Get(){return _mutex.Get();}~LockGuard(){_mutex.Unlock();}
};

TCP编程

因为要连接,所以多了几步,其他基本一致

创建

socket的type参数为SOCK_STREAM

listen

backlog:底层全连接队列长度-1, 不要传太大的值

成功返回0,失败返回-1

accept

未连接成功时阻塞等待,连接成功时,返回新连接的文件描述符,失败返回-1

connect

成功返回0,失败返回-1

示例代码

<EchoServer.hpp>

#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <functional>#include <thread>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "logger.hpp"
#include "Inetaddr.hpp"
#include "Quit_code.hpp"#define DEFAULT_BACKLOG 5using task_t = std::function<void()>;class EchoServer
{int _listensockfd = -1;uint16_t _port;bool _isrunning;void HandleIO(int sockfd,InetAddr client){char buffer[1024];while(true){buffer[0] = 0;ssize_t n = read(sockfd,buffer,sizeof(buffer));if(n>0){buffer[n] = 0;std::string echo_string = "server echo: ";echo_string += buffer;LOG(Loglevel::INFO) << buffer;ssize_t m = write(sockfd,echo_string.c_str(),echo_string.size());}else if(n==0){LOG(Loglevel::INFO) << "client "<<client.Ip()<<" quit";break;}else{LOG(Loglevel::WARNING) << "client read"<<client.Ip() << "error";break;}}   close(sockfd);}
public:EchoServer(int16_t port):_port(port),_isrunning(false){}void Init(){//创建socket_listensockfd = socket(AF_INET,SOCK_STREAM,0);if(_listensockfd<0){LOG(Loglevel::FATAL) << "create socket error";exit(SOCKET_CREATE_ERROR);}LOG(Loglevel::INFO) << "create socket success";InetAddr local(_port);int n = bind(_listensockfd,local.Addr(),local.Len());if(n<0){LOG(Loglevel::FATAL) << "bind socket error";exit(SOCKET_BIND_ERROR);}LOG(Loglevel::INFO) << "bind socket success";if(listen(_listensockfd,DEFAULT_BACKLOG) != 0){LOG(Loglevel::WARNING) << "listen socket error";exit(SOCKET_LISTEN_ERROR);}LOG(Loglevel::INFO) << "listen socket success";}class ThreadData{public:int _sockfd;InetAddr _client;EchoServer* _self;ThreadData(int sockfd,EchoServer* self,InetAddr& client):_sockfd(sockfd),_self(self),_client(client){}~ThreadData(){}};static void* Routine(void* args)//因为普通成员函数参数隐含this指针,所以不设置为静态参数不匹配{ThreadData* td = static_cast<ThreadData*>(args);pthread_detach(pthread_self());td->_self->HandleIO(td->_sockfd,td->_client);delete td;return (void*)0;}void Start(){_isrunning = true;while(_isrunning){struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensockfd,(struct sockaddr*)&peer,&len);if(sockfd < 0){LOG(Loglevel::WARNING) << "accept socket error";continue;}LOG(Loglevel::INFO) << "accept socket "<< sockfd << " success" ;InetAddr clientaddr(peer);//多线程ThreadData* td = new ThreadData(sockfd,this,clientaddr);           pthread_t tid;pthread_create(&tid,nullptr,Routine,(void *)td);}_isrunning = false;}void Stop(){   _isrunning = false;}~EchoServer(){}};

<EchoServer.cpp>

#include <iostream>
#include "EchoServer.hpp"
#include <string>
#include <memory>void Usage()
{std::cerr << "Usage is : ./udpc serverport" << std::endl;
}int main(int argc,char* argv[])
{if(argc!=2){Usage();exit(3);}uint16_t serverport = std::stoi(argv[1]);EchoServer echos(serverport);echos.Init();echos.Start();return 0;
}

<EchoClient.cpp>

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <unistd.h>
#include "logger.hpp"
#include "Inetaddr.hpp"
#include "Quit_code.hpp"void Usage()
{std::cerr << "Usage is : ./udpc serverip serverport" << std::endl;
}int main(int argc,char* argv[])
{if(argc!=3){Usage();exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){LOG(Loglevel::FATAL) << "create socket error";exit(SOCKET_CREATE_ERROR);}LOG(Loglevel::INFO) << "create socket success";InetAddr server(serverport,serverip);if(connect(sockfd,server.Addr(),server.Len()) < 0){LOG(Loglevel::WARNING) << "client connect error";exit(SOCKET_CONNECT_ERROR);}    while(true){std::cout<<"Please input@ ";std::string line;std::getline(std::cin,line);ssize_t wn = write(sockfd,line.c_str(),line.size());if(wn >= 0){char buffer[1024];buffer[0] = 0;ssize_t rn = read(sockfd,buffer,sizeof(buffer));if(rn > 0){buffer[rn] = 0;std::cout << buffer <<std::endl;}else if(rn == 0){LOG(Loglevel::INFO) << "server quit";break;}else {;}}}return 0;
}

<InetAddr.hpp>

#pragma once
#include <iostream>
#include "logger.hpp"
#include <string>
#include <cstring>#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{uint16_t _port;std::string _ip;struct sockaddr_in _addr; void Net_to_H(){_ip = inet_ntoa(_addr.sin_addr);_port = ntohs(_addr.sin_port);}void H_to_Net(){memset(&_addr,0,sizeof(_addr));_addr.sin_addr.s_addr = inet_addr(_ip.c_str()); _addr.sin_family = AF_INET;_addr.sin_port = htons(_port);}
public:InetAddr(const struct sockaddr_in client):_addr(client){Net_to_H();}InetAddr(uint16_t port,const std::string ip = "0.0.0.0"):_ip(ip),_port(port){H_to_Net();}uint16_t Port(){return _port;}std::string Ip(){return _ip;}struct sockaddr* Addr(){return (struct sockaddr*)&_addr;}socklen_t Len(){return sizeof(_addr);}bool operator==(const InetAddr& addr){return _ip == addr._ip && _port == addr._port;}~InetAddr(){}
};

<Quit_code.hpp>

#pragma once
#include <iostream>enum quitcode
{OK,SOCKET_CREATE_ERROR,SOCKET_BIND_ERROR,SOCKET_LISTEN_ERROR,SOCKET_CONNECT_ERROR};

Inet_ntoa和Inet_ntop

inet_ntoa不是线程安全的,

所以可以使用inet_ntop和inet_pton来替代inet_ntoa和inet_aton

小知识

1.netstat -uanp 查看系统中所有udp的服务的信息,netstat -tlnp查看tcp服务的信息

0.0.0.0 表示支持任意ip,* 表示支持任意端口


2.线程内可以创建子进程

http://www.lryc.cn/news/594823.html

相关文章:

  • 【Prometheus+Grafana篇】监控通过Keepalived实现的MySQL HA高可用架构
  • DeepSeek vs ChatGPT:谁更胜一筹?
  • Python 模块未找到?这样解决“ModuleNotFoundError”
  • 02-UE5蓝图初始的三个节点作用
  • RuoYi配置多数据源失效
  • Laravel 系统版本查看及artisan管理员密码找回方法针对各个版本通用方法及原理-优雅草卓伊凡
  • 2025最新版虚幻引擎5(UE5)入门教程:前言——你的随身教程和学习笔记
  • 如何简洁高效的实现存在则更新,不存在则插入
  • HTML前端颜色渐变动画完整指南
  • TPS61194PWPRQ1适用于汽车照明低 EMI、高性能 4 通道 LED 驱动器TPS61194
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 主页布局实现
  • ppp实验
  • 如何在FastAPI中整合GraphQL的复杂度与限流?
  • QT跨平台应用程序开发框架(11)—— Qt系统相关
  • 了解 ReAct 框架:语言模型中推理与行动的协同
  • 论文Review Lidar 3DGS Splat-LOAM: Gaussian Splatting LiDAR Odometry and Mapping
  • 无人机浆叶安装顺序
  • 客流分析核心算法 trajectory_event_analyzer数据结构
  • 7.11.B树
  • 遇到偶现Bug(难以复现)怎么处理?
  • 数据结构:反转字符串(Reversing a String)
  • 无人机避障雷达模式运行方式
  • PHP面向对象高级特性:魔术方法、对象迭代器与设计模式应用
  • dolphinscheduler中sqoop无法执行
  • 三款适合户外探险、应急救援的智能三防手机,各有各的优势
  • SQLite以及Room框架的学习:用SQLite给新闻app加上更完善的登录注册功能
  • 深入浅出:从最小核心到完整架构,全面解析5G用户面协议栈
  • Mac上安装Claude Code的步骤
  • RANsemi 推出适用于 Split 7.2 Open RAN 无线电单元的即插即用基带板
  • Q10900H6迷你电脑:集成双10G+四2.5G网口,支持多系统网络部署