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,表示默认方式

练习

相关推荐
裴东青8 小时前
10-实战:RuoYi-Cloud的自动化发布
运维·ci/cd·自动化
江公望8 小时前
Ubuntu htop命令,10分钟讲清楚
linux·服务器
哎呦,帅小伙哦8 小时前
Linux 时间:从原子钟到 clock_gettime 的每一面
linux·运维·服务器
sxgzzn8 小时前
新能源场站数智化转型:基于数字孪生与AI的智慧运维管理平台解析
大数据·运维·人工智能
张小姐的猫8 小时前
【Linux】多线程 —— 线程互斥
linux·运维·服务器·c++
YMWM_9 小时前
UDP协议详解:从原理到Python实践
网络·网络协议·udp
CodeMartain9 小时前
Dify Windows 原生部署(无 Docker、纯本地)
运维·docker·容器
xxx1x1x9 小时前
极客向:DLL/运行库故障的底层逻辑与自动化修复方案
运维·自动化·dll文件·dll·dll修复·dll缺失·dll一键修复
pengyi8710159 小时前
共享 IP 与独享 IP 怎么选?被封后升级方案避坑
网络·网络协议·tcp/ip
YuanDaima20489 小时前
Linux 进阶运维与 AI 环境实战:进程管理、网络排错与 GPU 监控
linux·运维·服务器·网络·人工智能