Linux系统编程:TCP/IP网络编程 从socket到通信全解析

目录

tcp协议的网络编程

socket函数

功能

参数

返回值

connect(连接)

功能

参数

返回值

IPv4的地址结构体

网络字节序(大端模式)

字节序的转换函数

htons(端口号)

功能

参数

返回值

inet_addr(IP地址)

功能

参数

返回值

bind(绑定)

功能

参数

返回值

listen(标记)

功能

参数

返回值

accept

功能

参数

返回值

网络编程中专门的读写函数

recv(read)

send(write)

注意

练习


两台主机的进程通信

如何找到网络中的一台主机?

  • IP地址

如何找到该主机上的一个进程?

  • 端口号

编程的接口?

  • 怎么用到系统的网络功能

tcp协议的网络编程

客户端

  • 主动的一方

服务端

  • 被动的一方

socket函数

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

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

功能

  • 创建通信的一端

参数

  • @damain:通信范围,AF_UNIX:本地通信;AF_INET:IPv4;AF_INET6:IPv6
  • @type:socket的类型

sock_stream:流式套接字 sock_dgram:用户数据报

  • protocol:协议,表示前面类型对应的具体协议,填0表示走默认协议

sock_stream:默认走的协议是TCP

sock_dgram:默认走的协议是UDP

返回值

  • 成功返回 socket对应的文件描述符
  • 失败返回 -1而且errno被设置

eg:基于tcp协议的socket

int fd = socket(AF_INET,SOCK_STREAM,0);

connect(连接)

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);

功能

  • 发起连接请求

参数

  • @socket:客户端的socket文件描述符
  • @addr:代表服务器端的地址信息
  • @addrlen:addr对用的地址结构体类型的大小

返回值

  • 成功返回 0
  • 失败返回 -1而且errno被设置

IPv4的地址结构体

网络字节序(大端模式)

字节序:大小端?

  • 只要是发到网络中的数据,统一称为网络字节序(大端模式)
  • 接收方收到后,转换为本机的字节序(主机字节序)

字节序的转换函数

  • htons--------------端口号
  • inet_addr---------IP地址
htons(端口号)

uint16_t htons(uint16_t hostshort);

功能
  • 将hostshort主机字节序数值转换为网络字节序
参数
  • @hostshort:要转换的数值
返回值
  • 成功返回 转换好的网络字节序
  • htons(5000);
inet_addr(IP地址)

in_addr_t inet_addr(const char *cp);

功能
  • 将cp传过来的IPv4格式的地址转换为网络序的数值
参数
  • @cp:需要的是一个点分十进制格式的 ip地址 eg:"192.168.1.122"
返回值
  • 成功返回 转换好的网络字节序数值
  • 失败返回 -1
cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <strings.h>

//tcp客户服务端
int main(int argc, const char *argv[])
{
	//1.socket
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket fail");
		return -1;
	}
	printf("fd:%d\n",fd);
	//2.bind //可选
	//3.connect
	struct sockaddr_in addr;//结构体
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("192.168.1.139");
	addr.sin_port = htons(50000);
	if(connect(fd,(const struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("connect fail");
		return -1;
	}
	printf("connect success \n");
	return 0;
}

服务器和客户端进行交互

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
//tcp客户服务端
int main(int argc, const char *argv[])
{
	//1.socket
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket fail");
		return -1;
	}
	printf("fd:%d\n",fd);
	//2.bind //可选
	//3.connect
	struct sockaddr_in addr;//结构体
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("192.168.1.139");
	addr.sin_port = htons(50000);
	if(connect(fd,(const struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("connect fail");
		return -1;
	}
	printf("connect success \n");
#if 0
	char buf[1024] = "hello";
	write(fd,buf,sizeof(buf)); 
#endif
#if 1
	while(1)
	{
		char buf[1024];
		fgets(buf,sizeof(buf),stdin);
		write(fd,buf,strlen(buf)+1);
		if(strncmp(buf,"quit",4)==0)
		{
			break;
		}
		read(fd,buf,sizeof(buf));
		printf("buf:%s\n",buf);
	}
#endif
	return 0;
}

bind(绑定)

  • 服务器端绑定不能省略

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);

功能

  • 绑定一个地址(ip+port)到一个socket文件描述符上

参数

  • @sockfd:socket函数获得的文件描述符
  • @sockaddr:地址信息结构体
  • @addrlen:表示addr参数对应类型的地址信息结构体的大小

返回值

  • 成功返回 0
  • 失败返回 -1而且errno被设置

listen(标记)

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

int listen(int sockfd, int backlog);

功能

  • 将sockfd设置为监听套接字

参数

  • @sockfd:socket创建的fd
  • @backlog:队列的大小,最大128

返回值

  • 成功返回 0
  • 失败返回 -1而且errno被设置

accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

功能

  • 获取连接请求,建立连接套接字

参数

  • @socket:监听套接字
  • @addr:用来获取对端的地址信息
  • addrlen:值结果参数(注意:必须自己初始化一个值sizeof(addr))

返回值

  • 成功返回 新连接的套接字文件描述符
  • 失败返回 -1而且errno被设置

accept是一个阻塞操作,如果请求连接队列为空,此时accept会阻塞等待

client.c

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
//tcp客户服务端
int main(int argc, const char *argv[])
{
	//1.socket
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket fail");
		return -1;
	}
	printf("fd:%d\n",fd);
	//2.bind //可选
	//3.connect
	struct sockaddr_in addr;//结构体
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	addr.sin_port = htons(50000);
	if(connect(fd,(const struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("connect fail");
		return -1;
	}
	printf("connect success \n");
#if 0
	char buf[1024] = "hello";
	write(fd,buf,sizeof(buf)); 
#endif
#if 1
	while(1)
	{
		char buf[1024];
		fgets(buf,sizeof(buf),stdin);
		write(fd,buf,strlen(buf)+1);
		if(strncmp(buf,"quit",4)==0)
		{
			break;
		}
	//	read(fd,buf,sizeof(buf));
	//	printf("buf:%s\n",buf);
	}
#endif
	return 0;
}

server.c

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
//tcp客户服务端
int main(int argc, const char *argv[])
{
	//1.socket
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket fail");
		return -1;
	}
	printf("fd:%d\n",fd);

	//2.bind(绑定)
	struct sockaddr_in addr;//结构体
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	addr.sin_port = htons(50000);
	if(bind(fd,(const struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("bind fail");
		return -1;
	}
	printf("bind success \n");

	//3,listen
	if(listen(fd,5)<0)
	{
		perror("listen fail");
		return -1;
	}
#if 0
	//4.accpet
	int connfd = accept(fd,NULL,NULL);
	if(connfd < 0)
	{
		perror("accept fail");
		return -1;
	}

#endif

	struct sockaddr_in cliaddr;
	bzero(&cliaddr,sizeof(cliaddr));
	socklen_t len = sizeof(cliaddr);
	int connfd = accept(fd,(struct sockaddr*)&cliaddr,&len);
	if(connfd < 0)
	{
		perror("accept fail");
			return -1;
	}
	printf("connfd:%d\n",connfd);
	printf("-------------------\n");
	printf("client ip:%s\n",inet_ntoa(cliaddr.sin_addr));
	printf("client port:%d\n",ntohs(cliaddr.sin_port));
#if 0
	//connect
	char buf[1024] = "hello";
	write(fd,buf,sizeof(buf)); 
#endif
#if 1
	while(1)
	{
		char buf[1024];
		//fgets(buf,sizeof(buf),stdin);
		//write(fd,buf,strlen(buf)+1);
		//if(strncmp(buf,"quit",4)==0)
		//{
		//	break;
		//}
		read(connfd,buf,sizeof(buf));
		printf("buf:%s\n",buf);
	}
#endif
	return 0;
}

网络编程中专门的读写函数

recv(read)

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

send(write)

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

注意

网络中的收发,在tcp中我们主要用的是recv(读)和send(写)

  • recv中的flags一般写0,表示默认方式(阻塞)
  • send中的flags一般写0,表示默认方式

练习

相关推荐
AOwhisky7 分钟前
Kubernetes 学习笔记:集群管理、命名空间与 Pod 基础
linux·运维·笔记·学习·云原生·kubernetes
小龙在慢慢变强..1 小时前
目录结构(FHS 标准)
linux·运维·服务器
刘延林.1 小时前
win11系统下通过 WSL2 安装Ubuntu 24.04 使用RTX 5080 GPU
linux·运维·ubuntu
星恒讯工业路由器1 小时前
星恒讯工业生产自动化解决方案
运维·物联网·自动化·智能路由器·信息与通信
Diros1g1 小时前
如何通过普通网线给另一个设备供网
网络·网络协议
a8a3021 小时前
Laravel9.x新特性全解析
运维·spring boot·nginx
beyond阿亮1 小时前
IEC104 Client Simulator - IEC104 主站/客户端模拟器 仿真器免费使用教程
运维·服务器·网络
(Charon)2 小时前
【C++/Qt】Qt 封装 TCP 客户端底层 Network 类:连接、收发、自动测试与错误处理
服务器·网络·qt·tcp/ip
Agent产品评测局2 小时前
生产排期与MES/ERP系统打通,实操方法详解:2026企业级智能体与超自动化集成实战指南
运维·人工智能·ai·chatgpt·自动化
CodeOfCC2 小时前
Linux 嵌入式arm64安装openclaw
linux·运维·服务器