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

练习

相关推荐
云老大-阿里云国际站代理商41 分钟前
华为云国际站服务代理商:NAT网关怎么配?多台ECS共用公网IP要注意什么
服务器·华为云·云计算
zx_741484811 小时前
【Linux入门】Shell 函数、正则表达式与文本处理:cut 与 awk
linux·运维·正则表达式
Zenova EdgeOS1 小时前
Linux dmesg 工业边缘实战:内核日志过滤、持久化与故障定位
linux·运维·边缘计算·工业边缘·dmesg·内核日志
wdfk_prog2 小时前
ROS教程08:从 TransportTCP::connect() 追到 TCPROS Connection Header、序列化与 Socket 数据传输
运维·缓存·docker·容器·ros
做运维的阿瑞3 小时前
Python 标准库汇总:分类速览与常用模块清单
linux·运维·python
浅念-3 小时前
Redis基础详解:单线程模型、String与Hash数据结构
服务器·数据库·redis·sql·mysql·nosql数据库·nosql
日常筹谋记3 小时前
自动化仓储安全防护工况评估:明治传感器AS-33C技术适配性分析
大数据·运维·创业创新·业界资讯
Shadow(⊙o⊙)4 小时前
Linux进阶知识1.0
linux·运维·服务器
闲云自留地5 小时前
告别命令恐惧症,openEuler 基础操作 + 文本 + 网络管理实战
linux·运维·云计算
剑指offer.5 小时前
ARM裸机开发-点亮LED灯
linux·服务器·嵌入式硬件