Linux下Socket编程

1. Socket简介

  • Socket是什么?
    • Socket是一种进程间通信的机制,通过它应用程序可以通过网络进行数据传输。Socket提供了一种跨平台的接口,使得同样的代码可以在不同的操作系统上运行。
  • Socket类型
    • 流式套接字(SOCK_STREAM): 基于TCP协议,提供可靠的双向字节流通信。
    • 数据报套接字(SOCK_DGRAM): 基于UDP协议,提供不可靠的、无连接的通信。
    • 原始套接字(SOCK_RAW): 允许直接访问底层协议,主要用于协议开发或网络分析工具。

2. Socket编程流程

  1. 创建Socket

    int socket(int domain, int type, int protocol);

    • domain : 协议族,常用的有 AF_INET(IPv4) 和 AF_INET6(IPv6)。
    • type : 套接字类型,如 SOCK_STREAMSOCK_DGRAM
    • protocol : 通常设为 0,表示由系统自动选择合适的协议。
  2. 绑定Socket

    int bind(int sockfd, const struct sockaddr *addr,

    socklen_t addrlen);

    • 将Socket与指定的IP地址和端口绑定。
  3. 监听连接

    int listen(int sockfd, int backlog);

    • 服务器端Socket进入监听模式,backlog指定队列中允许的最大未处理连接数。
  4. 接受连接

    int accept(int sockfd, struct sockaddr *addr,

    socklen_t *addrlen);

    • 接受客户端的连接请求,创建一个新的Socket用于与客户端通信。
  5. 连接到服务器

    int connect(int sockfd, const struct sockaddr *addr,

    socklen_t addrlen);

    • 客户端Socket使用此函数连接到服务器。
  6. 发送和接收数据

    • 发送数据: ssize_t send(int sockfd, const void *buf, size_t len,

      int flags);

    • 接收数据: ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  7. 关闭Socket

    int close(int sockfd);

3. 常用API详解

  • socket()

    • 功能: 创建一个新的Socket。
    • 参数:
      • domain: 地址族,常用的如 AF_INET 表示IPv4地址。
      • type: Socket类型,SOCK_STREAMSOCK_DGRAM
      • protocol: 协议编号,通常为0,由系统选择默认协议。
    • 返回值: 返回新的文件描述符,失败返回 -1
  • bind()

    • 功能: 将Socket绑定到特定的IP地址和端口。
    • 参数:
      • sockfd: 由 socket() 创建的文件描述符。
      • addr: 结构体指针,包含要绑定的地址信息。
      • addrlen: addr 的长度。
  • listen()

    • 功能: 在Socket上监听连接请求。
    • 参数:
      • sockfd: 由 socket() 创建的文件描述符。
      • backlog: 未处理连接的最大数量。
  • accept()

    • 功能: 接受连接请求,创建用于通信的新Socket。
    • 参数:
      • sockfd: 监听套接字。
      • addr: 客户端的地址信息结构体。
      • addrlen: addr 的长度。
  • connect()

    • 功能: 客户端使用此函数连接到服务器。
    • 参数:
      • sockfd: 由 socket() 创建的文件描述符。
      • addr: 服务器的地址信息。
      • addrlen: addr 的长度。
    • 返回值: 成功返回 0,失败返回 -1
  • send() 和 recv()

    • send() 用于向对方发送数据,recv() 用于接收数据。
    • 参数:
      • sockfd: 通信的套接字。
      • buf: 数据缓冲区。
      • len: 缓冲区的大小。
      • flags: 标志位,一般为0。

4. 错误处理

在Socket编程中,经常会遇到错误。通常的做法是检查函数的返回值,若为 -1 则出错,并通过 errno 查看具体的错误原因。以下是一些常见的错误:

  • EADDRINUSE: 地址已被使用。
  • EADDRNOTAVAIL: 无效的地址。
  • ECONNREFUSED: 连接被拒绝。
  • ETIMEDOUT: 连接超时。

5. 高级特性

  • 非阻塞I/O : 使用 fcntl() 设置Socket为非阻塞模式。
  • 多路复用 : 使用 select()poll() 等函数同时监听多个Socket。

6. 示例代码

服务器端
cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int clientFd;
int serverFd;
void hand(int s){
	if(2 == s){
		//7 断开连接
		close(clientFd);
		close(serverFd);
		printf("bye bye 了勾八!\n");
		exit(1);
	}
}

int main(){
	signal(2,hand);
	//1 创建socket
	serverFd = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == serverFd) printf("创建socket失败:%m!\n"),exit(-1);
	printf("创建socket成功!\n");
	//2 协议地址簇
	struct sockaddr_in addr={0};
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");//ip地址 注意字符串转网络字节序
	addr.sin_port = htons(8888);//端口号 用1W左右的  
	//3 绑定
	int r = bind(serverFd,(struct sockaddr*)&addr,sizeof addr); 
	if(-1 == r) printf("绑定失败:%m!\n"),close(serverFd),exit(-1);
	printf("绑定成功!\n");
	//4 监听
	r = listen(serverFd,100);//最大容量为100
	if(-1 == r) printf("监听失败:%m!\n"),close(serverFd),exit(-1);
	printf("监听成功!\n");
	//5 接受客户端连接
	struct sockaddr_in cAddr = {0};//用来接收客户端的协议地址簇
	int len = sizeof cAddr;
	clientFd = accept(serverFd,(struct sockaddr*)&cAddr,&len);
	if(-1 == clientFd) printf("服务器崩溃:%m\n"),close(serverFd),exit(-1);
	printf("接受客户端连接成功:%d %s %u\n",clientFd,inet_ntoa(cAddr.sin_addr),cAddr.sin_port);
	//6 通信
	char buff[1024];
	int n=0;
	char temp[1024];
	while(1){
		r = recv(clientFd,buff,1023,0);
		if(r > 0) {
			buff[r] = '\0';//添加字符串结束符号 '\0'
			printf("%d:%s\n",r,buff);
			sprintf(temp,"%d-%s",n++,buff);
			send(clientFd,temp,strlen(temp),0);
		}
	}
}
客户端
cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int fd;
void hand(int s){
	if(2 == s){
		//5 断开连接
		close(fd);
		printf("bye bye !\n");
		exit(1);
	}
}

int main(){
	signal(2,hand);
	//1 创建socket
	fd = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == fd) printf("创建socket失败:%m!\n"),exit(-1);
	printf("创建socket成功!\n");
	//2 协议地址簇
	struct sockaddr_in addr={0};
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");//ip地址 注意字符串转网络字节序
	addr.sin_port = htons(8888);//端口号 用1W左右的  大小端转换
	//3 连接服务器
	int r = connect(fd,(struct sockaddr*)&addr,sizeof addr);
	if(-1 == r) printf("连接服务器失败:%m!\n"),exit(-1);
	printf("连接服务器成功!\n");

	//4 通信
	char buff[1024];
	char temp[1024];
	while(1){
		printf("请输入要发送给服务器的数据:");
		scanf("%s",buff);
		r = send(fd,buff,strlen(buff),0);
		printf("发送%d字节数据到服务器!\n",r);
		r = recv(fd,temp,1023,0);
		if(r>0){
			temp[r] = '\0';
			printf("服务器回复:%s\n",temp);
		}
	}
}

7. 结论

Socket编程是网络通信的基础,通过了解各种Socket API的使用,可以实现复杂的网络应用程序。在实际编程中,掌握错误处理和高级特性如非阻塞I/O和多路复用是非常重要的。

相关推荐
xian0gang10 分钟前
rk3588 区分两个相同的usb相机
linux
这儿有一堆花20 分钟前
安全访问家中 Linux 服务器的远程方案 —— 专为单用户场景设计
linux·服务器·安全
RussellFans1 小时前
Linux 文本三剑客(grep, awk, sed)
linux·运维·服务器
猴哥聊项目管理1 小时前
什么是DevOps智能平台的核心功能?
运维·项目管理·制造·devops·软件·项目管理软件·软件分享
Chuncheng's blog1 小时前
CentOS 7如何编译安装升级gcc至7.5版本?
linux·运维·c++·centos
听风吹等浪起1 小时前
CentOS在vmware局域网内搭建DHCP服务器【踩坑记录】
linux·服务器·centos
明月看潮生1 小时前
青少年编程与数学 01-011 系统软件简介 04 Linux操作系统
linux·青少年编程·操作系统·系统软件·编程与数学
aitav02 小时前
⚡️ Linux Docker 基本命令参数详解
linux·运维·docker
量子-Alex2 小时前
【反无人机检测】C2FDrone:基于视觉Transformer网络的无人机间由粗到细检测
网络·transformer·无人机
Nazi62 小时前
docker数据管理
运维·docker·容器