Linux应用开发:socket

news/2024/5/17 17:28:02 标签: linux, 服务器, socket, 网络编程, tcp

目录

1、TCP

1.1 TCP建立连接的流程图

1.2 TCP函数

socket-toc" style="margin-left:80px;">1.2.1 socket

1.2.2 bind

1.2.3 listen

1.2.4 accept

1.2.5 recv

1.2.6 send

1.2.7 connnect

1.2.8 setsockopt、getsockopt

1.3 应用程序:服务器

1.4 应用程序:客户端

2、UDP

2.1 UDP建立连接的流程图

2.2 UDP函数

socket-toc" style="margin-left:80px;">2.2.1 socket

2.2.2 bind

2.2.3 recvfrom

2.2.4 sendto

2.3 应用程序:服务器

2.4 应用程序:客户端


1、TCP

1.1 TCP建立连接的流程图

1.2 TCP函数

socket">1.2.1 socket

int socket(int domain, int type, int protocol);
/*
功能:创建套接字
头文件:
       #include <sys/types.h>     
       #include <sys/socket.h>
参数:
    @domain:指定传输时候的地址族(协议族)
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   Local communication              unix(7)
       AF_INET             IPv4 Internet protocols          ip(7)
       AF_INET6            IPv6 Internet protocols          ipv6(7);

	@type:套接字类型
		SOCK_STREAM:字节流式套接字,流式套接字。--->TCP
        SOCK_DGRAM:数据报式套接字,报式套接字。---->UDP
		SOCK_RAW:原始套接字,传输协议需要自定义,在第三个参数中定义
            
	@protocol:传输协议,默认协议填0;
		IPPROTO_TCP IPPROTO_UDP	
返回值:
    成功,返回套接字的文件描述符
	失败,返回-1,更新errno             
*/

1.2.2 bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
功能:将IP地址和端口与套接字绑定;
头文件:
       #include <sys/types.h>       
       #include <sys/socket.h>
参数:
    @sockfd:socket的返回值;
	@addr:需要绑定到套接字上的地址信息;
        通用结构体,地址信息结构体。实际的结构体根据地址族的不同,指定不同类型;
		AF_INET: man 7 ip 查找;
           struct sockaddr_in {
               sa_family_t    sin_family; // address family: AF_INET 
               in_port_t      sin_port;   // 端口的网络字节序
               struct in_addr sin_addr;   // internet address 
           };
           
           struct in_addr {
               uint32_t       s_addr;      // IP的网络字节序
           };
		
		AF_INET6:man 7 ipv6;

	@addrlen:实际结构体的大小;
                sizeof(struct sockaddr_in);
返回值:
    成功,返回0;
	失败,返回-1,更新errno;
*/

1.2.3 listen

int listen(int sockfd, int backlog);
/*
功能:将套接字设置为被动监听状态;
头文件:
       #include <sys/types.h>       
       #include <sys/socket.h>
参数:
    @sockfd:socket函数返回的流式套接字;
	@backlog:允许同一时间连接的客户端个数;
			调用listen函数后,内核会维护两个队列:已完成连接队列,未完成连接队列;
           	backlog:未完成连接队列的容量;
返回值:
    成功,返回0;
	失败,返回-1,更新errno;
*/

1.2.4 accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
功能:阻塞函数,会从已完成连接的队列头中获取一个客户端信息,并生成一个新的文件描述符,用于通信;
头文件:
       #include <sys/types.h>       
       #include <sys/socket.h>
参数:
    @sockfd:被listen设置成监听状态的文件描述符;
	@addr:存储连接成功的客户端的地址信息;当不想获取的时候,填NULL;
        通用结构体,地址信息结构体。实际的结构体根据地址族的不同,指定不同类型;

	@addrlen:地址信息结构体的大小,注意是指针类型; 第二个参数填NULL,则当前参数填NULL;
返回值:
    成功,返回新的文件描述符,该文件描述符用于与客户端交互;
	失败,返回-1,更新errno;	
*/

1.2.5 recv

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
/*
功能:从套接字中读取数据;
头文件:
       #include <sys/types.h>
       #include <sys/socket.h>
参数:
    @sockfd:accept函数的返回值;
	@buf:存储接收到的数据,可以接收任意类型,但是需要注意,收发格式一致;
	@len:指定要读取多少个字节;
	@flags:0,阻塞方式接收;
返回值:
    >0, 	成功,读取到的字节数;
	=-1,	函数运行失败;更新errno;
	=0; 	对方关闭;
*/

1.2.6 send

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
/*
功能:发送数据;
头文件:
       #include <sys/types.h>
       #include <sys/socket.h>
参数:
    int sockfd:accept返回的新的文件描述符;
	void *buf:存储要发送的数据;可以是任意类型数据;
	size_t len:指定要发送多少个字节;
	int flags:发送标识
        0:阻塞方式发送,当接收缓冲区满的时候,该函数阻塞;
返回值:
   	>0, 成功发送的字节数;
	=-1, 函数调用出错;
*/

1.2.7 connnect

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
功能:连接服务器
头文件:
       #include <sys/types.h>      
       #include <sys/socket.h>      
参数:
    @sockfd:socket函数创建的文件描述符;
	@addr:指定要连接的服务器,填服务器的地址信息;
			AF_INET: man 7 ip
           struct sockaddr_in {
               sa_family_t    sin_family;   // address family: AF_INET
               in_port_t      sin_port;    	//端口号的网络字节序
               struct in_addr sin_addr;  	//ip地址的网络字节序
           };

           struct in_addr {
               uint32_t       s_addr;       //网络字节序
           };
	@addrlen:第二个地址信息结构体的大小;
返回值:
    成功,返回0;
	失败,返回-1,更新errno;
*/

1.2.8 setsockopt、getsockopt

int setsockopt(int sockfd, int level, int optname,
        const void *optval, socklen_t optlen);
int getsockopt(int sockfd, int level, int optname, 
              void *optval, socklen_t *optlen);
/*
功能:
头文件:    
    #include <sys/types.h>
    #include <sys/socket.h>
参数:
    @sockfd:套接字的文件描述符
    @level: 选项定义的层次,通常是SOL_SOCKET
    @optname:需设置的选项
    @optval:指向在其中指定所请求选项值的缓冲区的指针
    @optlen:指向的缓冲区的大小(以字节为单位)
返回值:成功返回0。 
       失败返回SOCKET_ERROR值,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。
此处放上一些常用的:
    level:
		SOL_SOCKET: 通用套接字选项;
		IPPROTO_TCP  	TCP选项
        IPPROTO_UDP 	UDP选项
        IPPROTO_IP 		IP选项;
	optname:
	             SOL_SOCKET
        SO_REUSEADDR 	允许端口快速重用;
		SO_BROADCAST 	广播;
		SO_RCVTIMEO 	接收超时时间
        SO_SNDTIMEO 	发送超时时间
        SO_SNDBUF 		发送缓冲区大小
        SO_RCVBUF 		接收缓冲区大小;
*/

 选项太多,具体的参考文档:

使用此函数的文章:

        linux网络编程系列(五)--setsockopt的常用选项 - 知乎

为了防为此文章挂掉,因为我觉得写的很好,所以我抄了一份

        setsockopt的常用选项_凛冬将至__的博客-CSDN博客

setsockopt 的参考文档:

        setsockopt 函数 (winsock.h) - Win32 apps | Microsoft Learn

level 的参考文档:

        套接字选项 - Win32 apps | Microsoft Learn

optname 的参考文档:

        

1.3 应用程序:服务器

#define PORT  	6666
#define IP 		"192.168.1.12"

int main(int argc, const char *argv[])
{
	//创建流式套接字 socket
	int sfd = socket(AF_INET, SOCK_STREAM, 0);

	//允许端口快速重用
	int reuse = 1;
	setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))

	//填充地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET; 	 	//IPV4地址族
	sin.sin_port 		= htons(PORT); 	//端口号的网络字节序,1024~49151
	sin.sin_addr.s_addr = inet_addr(IP); 	//IP地址的网络字节序,终端输入ifconfig

	//绑定服务器的地址信息结构体
	bind(sfd, (struct sockaddr*)&sin, sizeof(sin))

	//将套接字设置为被动监听状态
	listen(sfd, 10);

	//获取新的文件描述符,当有客户端连接成功,解除阻塞;
	int newfd = accept(sfd, NULL, NULL);

	char buf[128] = "";
	ssize_t res = -1;
	while(1)
	{
		bzero(buf, sizeof(buf));
		//读取数据
		res = recv(newfd, buf, sizeof(buf), 0);
		if(res < 0) {
			return -1;
		} else if(0 == res) {
			break;
		}

		send(newfd, buf, sizeof(buf), 0);
	}

	//关闭文件描述符
	close(newfd);
	close(sfd);

	return 0;
}

1.4 应用程序:客户端

int main(int argc, const char *argv[])
{
	if(argc < 3) {
		fprintf(stderr, "请输入IP 及 端口\n");
		return -1;
	}

	//创建流式套接字
	int sfd = socket(AF_INET, SOCK_STREAM, 0);

	//绑定客户端的IP和端口---->非必须	

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET;
	sin.sin_port 		= htons(atoi(argv[2])); 	//外部传参输入端口号,需要转换成整型
	sin.sin_addr.s_addr = inet_addr(argv[1]); 		//外部传参输入IP 

	//连接服务器
	connect(sfd, (struct sockaddr*)&sin, sizeof(sin));
	
	char buf[128] = "";
	ssize_t res = -1;

	//循环接收发送
	while(1)
	{
		bzero(buf, sizeof(buf));

		printf("请输入>>>");
		fgets(buf, sizeof(buf), stdin);
		buf[strlen(buf)-1] = 0; 	//将结尾的'\n'修改成'\0'

		send(sfd, buf, sizeof(buf), 0);

		bzero(buf, sizeof(buf));
		res = recv(sfd, buf, sizeof(buf), 0);
		if(res < 0) {
			return -1;
		} else if(0 == res) {
			break;
		}
		printf(":%s\n", buf);
	}

	//关闭文件描述符
	close(sfd);

	return 0;
}

2、UDP

2.1 UDP建立连接的流程图

2.2 UDP函数

socket">2.2.1 socket

int socket(int domain, int type, int protocol);
/*
功能:创建套接字
头文件:
       #include <sys/types.h>     
       #include <sys/socket.h>
参数:
    @domain:指定传输时候的地址族(协议族)
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   Local communication              unix(7)
       AF_INET             IPv4 Internet protocols          ip(7)
       AF_INET6            IPv6 Internet protocols          ipv6(7);

	@type:套接字类型
		SOCK_STREAM:字节流式套接字,流式套接字。--->TCP
        SOCK_DGRAM:数据报式套接字,报式套接字。---->UDP
		SOCK_RAW:原始套接字,传输协议需要自定义,在第三个参数中定义
            
	@protocol:传输协议,默认协议填0;
		IPPROTO_TCP IPPROTO_UDP	
返回值:
    成功,返回套接字的文件描述符
	失败,返回-1,更新errno             
*/

2.2.2 bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
功能:将IP地址和端口与套接字绑定;
头文件:
       #include <sys/types.h>       
       #include <sys/socket.h>
参数:
    @sockfd:socket的返回值;
	@addr:需要绑定到套接字上的地址信息;
        通用结构体,地址信息结构体。实际的结构体根据地址族的不同,指定不同类型;
		AF_INET: man 7 ip 查找;
           struct sockaddr_in {
               sa_family_t    sin_family; // address family: AF_INET 
               in_port_t      sin_port;   // 端口的网络字节序
               struct in_addr sin_addr;   // internet address 
           };
           
           struct in_addr {
               uint32_t       s_addr;      // IP的网络字节序
           };
		
		AF_INET6:man 7 ipv6;

	@addrlen:实际结构体的大小;
                sizeof(struct sockaddr_in);
返回值:
    成功,返回0;
	失败,返回-1,更新errno;
*/

2.2.3 recvfrom

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
/*
功能:接收数据包,并且可以获取到该数据包是谁发送的,存储在地址信息结构体中;
头文件:
       #include <sys/types.h>
       #include <sys/socket.h>
参数:
    @sockfd:accept函数的返回值;
	@buf:存储接收到的数据,可以接收任意类型,但是需要注意,收发格式一致;
	@len:指定要读取多少个字节;
	@flags:0,阻塞方式接收;
	@src_addr:存储数据包发送端的IP和端口;
				如果不想接收,填NULL,最后一个参数也填NULL
	@addrlen:地址信息结构体的大小,注意是指针类型;
返回值:
    >0, 	成功,读取到的字节数;
	=-1,	函数运行失败;更新errno;
	=0; 	对方关闭;(只适用于tcp)   
*/

2.2.4 sendto

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
/*
功能:发送数据给指定的IP和端口;
头文件:
       #include <sys/types.h>
       #include <sys/socket.h>   
参数:
    @sockfd:accept返回的新的文件描述符;
	@buf:存储要发送的数据;可以是任意类型数据;
	@len:指定要发送多少个字节;
	@flags:发送标识
        0:阻塞方式发送,当接收缓冲区满的时候,该函数阻塞;
	@dest_addr:指定该数据包该发送给谁;
	@addrlen:地址信息结构体的大小;
返回值:
   	>0, 成功发送的字节数;
	=-1, 函数调用出错;
*/

2.3 应用程序:服务器

#define PORT 	8888
#define IP 		"192.168.1.12"

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	
	//填充服务器本身的地址信息
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET;
	sin.sin_port 		= htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);

	//绑定服务的IP和端口
    bind(sfd, (struct sockaddr*)&sin, sizeof(sin));

	char buf[128] = "";
	ssize_t res = 0;

	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	while(1)
	{
		bzero(buf, sizeof(buf));
		//接收
		res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);

		printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
		
		//发送
		strcat(buf, "*_*");
		sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, sizeof(cin));	
	}
	//关闭
	close(sfd);
	return 0;
}

2.4 应用程序:客户端

#define PORT 	8888
#define IP 		"192.168.1.12"

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	//绑定IP和端口---->非必须绑定

	//填充服务器的地址信息
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET;
	sin.sin_port 		= htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);

	char buf[128] = "";
	ssize_t res = 0;

	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	while(1)
	{
		bzero(buf, sizeof(buf));
		//发送
		fgets(buf, sizeof(buf), stdin);
		buf[strlen(buf)-1] = 0;

		sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin));

		bzero(buf, sizeof(buf));
		//接收
		res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);

		printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
	}
	//关闭
	close(sfd);
	return 0;
}

http://www.niftyadmin.cn/n/318188.html

相关文章

如何使用现代C++特性构建游戏引擎

如何使用现代C特性构建游戏引擎 一、引言1 游戏引擎的定义及作用2 现代C特性的应用3 本文目的和意义 二、现代C特性概述1 C11、C14、C17的新特性2 常用现代C特性介绍2.1 智能指针2.2 Lambda表达式2.3 模板特化与偏特化2.4 可变参数模板2.5 constexpr函数2.6 类成员函数的默认实…

JVM 直接内存(Direct Memory)

直接内存概述 不是虚拟机运行时数据区的一部分&#xff0c;也不是<<Java 虚拟机规范>> 中定义的内存区域直接内存是Java 堆外的、直接向系统申请的内存区间来源于 NIO&#xff0c;通过存在堆中的 DirectByteBuffer 操作 Native 内存访问直接内存的速度会优于 Java…

数据结构与算法之动态规划: Leetcode 70. 爬楼梯 (Typescript版)

爬楼梯 https://leetcode.cn/problems/climbing-stairs/ 描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以…

MyBatis日志

日志 Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一&#xff1a; SLF4JApache Commons LoggingLog4j 2Log4jJDK logging 配置日志 配置日志功能非常简单&#xff1a;添加一个或多个配置文件&#xff08;如 log4j.properties…

基于列系数比较一组迭代次数的顺序

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入有3个节点&#xff0c;训练集AB各由6张二值化的图片组成&#xff0c;让A中每行有1个1&#xff0c;B中全是0&#xff0c;排列组合A &#xff0c;统计迭代次数的顺序。共有729组不同的排列组合&#xff0c;但按照对称性只有26…

【杂记】显式锁

1.什么是java显式锁&#xff1f; JDK 5版本引入了java.util.concurrent并发包&#xff0c;简称为JUC包&#xff0c;里面提供了各种高并发工具类&#xff0c;通过此JUC工具包可以在Java代码中实现功能非常强大的多线程并发操作。所以&#xff0c;Java显式锁也叫JUC显式锁。 2.…

OpenCL编程指南-1.2OpenCL基本概念

OpenCL概念基础 面向异构平台的应用都必须完成以下步骤&#xff1a; 1&#xff09;发现构成异构系统的组件。 2&#xff09;探查这些组件的特征&#xff0c;使软件能够适应不同硬件单元的特定特性。 3&#xff09;创建将在平台上运行的指令块&#xff08;内核)。 4&#xff09…

java编程语言的特点,作为程序员如何面对跳槽的问题?

最近有很多小伙伴问我一些问题&#xff0c;比如说是否要跳槽、工作需要做些什么、如何规划职业生涯等。我并不是什么人生导师&#xff0c;只是一名普通的程序员&#xff0c;因此回答这些问题有时会感到无从下手&#xff0c;因为我担心自己的回答会是错误的。但是&#xff0c;也…