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

练习

相关推荐
R²AIN SUITE几秒前
企业智能体平台私有化部署需要什么服务器?CPU、内存、显卡与项目管理集成的7个评估项
运维·服务器
坚定的共产主义生产设备永不宕机1 分钟前
服务器从裸机开始搭建ESXI虚拟机
linux·服务器·网络
刚入门的大一新生8 分钟前
Linux-线程封装
linux·运维·服务器
n1121220 分钟前
Docker 容器时区错 8 小时:3 个坑
运维·docker·容器·故障排查·服务器运维·容器时区
Android系统攻城狮26 分钟前
Linux FFmpeg深度解析之av_dump_format调用流程与实战(十一)
linux·运维·服务器·ffmpeg·1024程序员节·音视频进阶
Android系统攻城狮41 分钟前
Linux FFmpeg深度解析之avcodec_find_decoder调用流程与实战(十二)
linux·运维·服务器·ffmpeg·1024程序员节·音视频进阶
小白的码BUG之路1 小时前
K8s -- 启动一主二从节点
linux·运维·服务器
yyzc111111 小时前
合规稳定资金结算收款系统助力门店发展
大数据·运维·人工智能
AI_Lab011 小时前
服务器管理工具要在服务器上装 Agent,安全吗?
服务器·安全·github
OpenLoong 开源社区1 小时前
打破 NAT 限制,零侵入跨网络通信:基于 WebRTC 的透明 TCP 隧道跨网络通信方案
网络协议·tcp/ip·开源·webrtc·openloong开源社区