网络编程

本文介绍了Linux环境下网络编程的基础知识,包括常见的网络协议如TCP、UDP、SMTP等,并通过实例展示了如何使用Socket接口进行网络编程,涵盖了并发服务器的设计、客户端与服务器间的文件传输等方面。
摘要由CSDN通过智能技术生成

对于网络理论介绍一般采用OSI模型,但是Linux中网络栈的介绍一般分为四层的Internet模型。

应用层

TFTP(Trivial File Transfer Protocol,简单文件传输协议)是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,基于UDP实现。提供不复杂、开销不大的文件传输服务。

FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。

SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件
的规则,由它来控制信件的中转方式。SMTP协议属于TCP/IP协议簇,它帮助每台计算机在发送或中转信件
时找到下一个目的地。通过SMTP协议所指定的服务器,就可以把E-mail寄到收信人的服务器上了,整个过程
只要几分钟。SMTP服务器则是遵循SMTP协议的发送邮件服务器,用来发送或中转发出的电子邮件。SMTP
是一种TCP协议支持的提供可靠且有效电子邮件传输的应用层协议。

DNS(Domain Name System )域名系统。也可以叫做域名解析协议。在我们在浏览器访问网页的时候,通常度是用我们所熟悉的一连串有意义的英文字符标识,比如www.baidu.com、www.sohu.com等。 但是我们学了前面的知识,计算机并不是通过这些字符串去找到对应的计算机,而是通过32位的二进制,也就是我们的IP地址来找。所以就有了DNS协议。他的作用就是将域名解析成对应的IP地址。因为让我们人去记那些IP地址,很难记得住,所以就想办法让IP地址转变为了现在的域名,在进行访问的时候,只需要将域名解析为对应的IP地址就行了,这个域名也很有讲究,其中分为好多层域名,是独一无二的。这里不细讲这个,只要我们知道,域名通过DNS能找到对应的IP地址就行了。

传输层

TCP

传输控制协议(TCP):该协议对建立网络上用户进程之间的对话负责,它确保进程之间的可靠通信,所提供的功能如下:         1.监听输入对话建立请求                 2.请求另一网络站点对话                 3.可靠的发送和接收数据                 4.适度的关闭对话

TCP协议段格式:

 具体详解见:https://blog.csdn.net/sandmm112/article/details/80441223

TCP的三次握手及四次挥手详解见:

https://blog.csdn.net/Neo233/article/details/72866230

用户数据报文协议(UDP):UDP 提供不可靠的非连接型传输层服务,它允许在源和目的地之间传送数据,而不必在传送数据之前建立对话。它主要用于那些非连接型的应用程序,如:视频点播。

Linux中的网络编程通过Socket(套接字)接口实现,Socket是一种文件描述符。

套接字socket有三种类型:    

流式套接字(SOCK_STREAM):流式的套接字可以提供可靠的、面向连    接的通讯流。它使用了TCP协议。TCP保证了数据传输的正确性和顺序性。

数据报套接字(SOCK_DGRAM):数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错,它使用数据报协议UDP。

原始套接字:原始套接字允许对低层协议如IP或ICMP直接访     问,主要用于新的网络协议的测试等。

进行Socket编程的常用函数有:

socket 创建一个socket bind 用于绑定IP地址和端口号到socket 。

connect 该函数用于绑定之后的client端与服务器建立连接。

listen设置能处理的最大连接要求,Listen()并未开始接收连线,只是设置socket为listen模式。

accept 用来接受socket连接。

send 发送数据 。

recv 接收数据。

 网络层(网际层)

主要包括Internet 协议(IP)、网际控制报文协议(ICMP)和地址解析协议(ARP)。

Internet 协议(IP)该协议被设计成互联分组交换通信网,以形成一个网际通信环境。它负责在源主机和目的地主机之间传输来自其较高层软件的称为数据报文的数据块,它在源和目的地之间提供非连接型传递服务。

网际控制报文协议(ICMP)它实际上不是IP层部分,但直接同IP层一起工作,报告网络上的某些出错情况。允许网际路由器传输差错信息或测试报文。

地址解析协议(ARP)ARP实际上不是网络层部分,它处于IP和数据链路层之间,它是在32位IP地址和48位物理地址之间执行翻译的协议。

#include <stdio.h>                  //TCP并发服务器接受多客户端消息并转发给客户端
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define PORT 4444
#define SIZE 1000

struct info
{
	int tofd;
	char buf[32];
};
typedef struct info Info;

void *recvhand(void *arg)
{
	Info Rec;
	pthread_detach(pthread_self());
	int ret;
	int fd = *(int *)arg;
	while(1)
	{
		ret = recv(fd,&Rec, sizeof(Rec), 0);
		if(-1 == ret)
		{
			perror("recv");
			exit(1);
		}
		if(!strcmp("bye",Rec.buf))
		{
			close(fd);
			break;
		}
		printf("from %d is %s\n", fd, Rec.buf);
		
		printf("%d\n",Rec.tofd);
		ret = send(Rec.tofd,&Rec,sizeof(Rec),0);
		if(-1 == ret)
		{
			perror("send");
			exit(1);
		}
		memset(&Rec, 0, sizeof(Rec));
	}
}

int main()
{
	int sockfd, ret, fd[SIZE], i = 0;
	pthread_t tid;

	sockfd = socket(PF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("socket");
		exit(1);
	}

	int opt = 1;
	setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置socket属性地址可以被随意绑定
	struct sockaddr_in servers;
	memset(&servers, 0, sizeof(servers));
	servers.sin_family = PF_INET;
	servers.sin_port = PORT;
	servers.sin_addr.s_addr = inet_addr("192.168.1.123");
	ret = bind(sockfd, (struct sockaddr *)&servers, sizeof(servers));
	if(-1 == ret)
	{
		perror("bind");
		exit(1);
	}

	ret = listen(sockfd, 5);
	if(ret < 0)
	{
		perror("listen");
		exit(1);
	}

	int len = sizeof(servers);
	while(1)
	{
		printf("waitting connecting...\n");
		fd[i] = accept(sockfd, (struct sockaddr *)&servers, &len);
		if(-1 == fd[i])
		{
			perror("accept");
			exit(1);
		}

		ret = pthread_create(&tid, NULL, recvhand, &fd[i]);
		if(-1 == ret)
		{
			perror("pthread_create");
			exit(1);
		}
		printf("connecting success fd: %d port: %d\n", fd[i], servers.sin_port);
		i++;
	}


	close(sockfd);

	return 0;
}
#include <stdio.h>                    //客户端发送和接收消息
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#define PORT 4444

int sockfd;
pthread_t tid1,tid2;

struct info
{
	int tofd;
	char buf[32];
};
typedef struct info Info;

void *Sendserver(void *arg)
{
	int ret;
	Info Send;
	while(1)
	{
		memset(&Send,0,sizeof(Send));
		scanf("%d%s",&Send.tofd,Send.buf);
		ret = send(sockfd,&Send,sizeof(Send),0);//发送消息给服务器
		if(-1 == ret)
		{
			perror("send");
			exit(1);
		}
	}
}

void *Recserver(void *arg)
{
	int old;
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&old);
	int ret;
	Info Rec;
	while(1)
	{
		memset(&Rec,0,sizeof(Rec));
		ret = recv(sockfd,&Rec,sizeof(Rec),0);
		if(ret == -1)
		{
			perror("recv");
			exit(1);
		}
		printf("Rec : %s\n",Rec.buf);
	}
}

void Sendbye()
{
	Info Send;
	strcpy(Send.buf,"bye");
	send(sockfd,&Send,sizeof(Send),0);//发送消息给服务器
	close(sockfd);
	pthread_cancel(tid2);
	exit(1);
}

int main()
{
	int ret;
	signal(2,Sendbye);
	struct sockaddr_in server_addr;

	sockfd = socket(PF_INET,SOCK_STREAM,0);
	if(-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}

	memset(&server_addr,0,sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = PORT;
	server_addr.sin_addr.s_addr = inet_addr("192.168.1.123");
	ret = connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
	if(-1 == ret)
	{
		perror("connect");
		exit(1);
	}

	ret = pthread_create(&tid1,NULL,Sendserver,&sockfd);
	if(ret != 0)
	{
		perror("pthread_create");
		exit(1);
	}
	
	ret = pthread_create(&tid2,NULL,Recserver,&sockfd);
	if(ret != 0)
	{
		perror("pthread_create");
		exit(1);
	}

	void *status;
	pthread_join(tid1,&status);
	pthread_join(tid2,&status);
	return 0;
}
#include <stdio.h>                //客户端发送文件
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PORT   4444

struct file
{
	char name[32];
	char buf[32];
};
typedef struct file File;

int main(int argc, char *argv[])
{
	int sockfd, ret, ret_read;
	struct sockaddr_in server_addr;

	sockfd = socket(PF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = htons(PORT);
	server_addr.sin_addr.s_addr = inet_addr("192.168.1.123");

	File file;
	strcpy(file.name,argv[1]);
	int length = sizeof(server_addr);

	int fd;
	fd = open(argv[1],O_RDONLY|O_EXCL);
	if(fd == -1)
	{
		perror("open");
		exit(1);
	}

	ret = sendto(sockfd, file.name, sizeof(file.name), 0, (struct sockaddr *)&server_addr, length);
	if (ret < 0)
	{
		perror("sendto");
	}

	while (1)
	{
		ret_read = read(fd,(void *)file.buf,sizeof(file.buf));
		ret = sendto(sockfd, file.buf, ret_read, 0, (struct sockaddr *)&server_addr, length);
		if (ret < 0)
		{
			perror("sendto");
		}
		if(ret_read < sizeof(file.buf))
		{
			close(fd);
			break;
		}
		memset(file.buf, 0, sizeof(file.buf));
	}

	return 0;
}
#include <stdio.h>                    服务器接受文件
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define PORT   4444

struct file
{
	char name[32];
	char buf[32];
};
typedef struct file File;

int main()
{
	int sockfd, ret, ret_write;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;

	sockfd = socket(PF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = htons(PORT);
	server_addr.sin_addr.s_addr = inet_addr("192.168.1.123");

	ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (ret < 0)
	{
		perror("bind");
		exit(1);
	}

	File file;
	int length = sizeof(client_addr);
	ret = recvfrom(sockfd, file.name, sizeof(file.name), 0, (struct sockaddr *)&client_addr, 
							(socklen_t *)&length);
	if (ret < 0)
	{
		perror("recvfrom");
	}

	int fd = open(file.name, O_WRONLY | O_CREAT | O_EXCL, 666);
	if (fd == -1)
	{
		perror("open");
		exit(1);
	}

	while (1)
	{
		ret = recvfrom(sockfd, file.buf, sizeof(file.buf), 0, (struct sockaddr *)&client_addr, 
							(socklen_t *)&length);
		if (ret < 0)
		{
			perror("recvfrom");
		}

		ret_write = write(fd, file.buf, ret);
		if (-1 == ret_write)
		{
			perror("write");
			exit(1);
		}

		if (ret < sizeof(file.buf))
		{
			close(fd);
			break;
		}

		memset(file.buf, 0, sizeof(file.buf));
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值