linux系统下的TCP网络编程

谨以此文章记录我的研究生生涯第一篇博客
本文主要内容是关于在Ubuntu环境下,编写服务器-客户端的程序。
1、环境准备:我的Ubuntu镜像是18.04的,linux版本内核是5.4.0-126-generic。有几种方法可以看自己的Ubuntu版本和内核版本。在终端输入下列命令:

查看Ubuntu的版本
	uname -a
	uname -v
	cat /etc/issue
	lsb_release -a

在里插入图片描述

查看操作系统的版本
cat /proc/version	

在这里插入图片描述 2、下载vim编辑器,用于编写c程序,在终端上输入sudo apt-get install vim,sudo的作用是暂时获得管理员权限,执行完命令之后,权限就没有了。

3、在home目录下常见文件目录tcp_server,然后再到该目录下创建两个.c文件,可以直接在图形化界面上创建文件,建议在终端上创建,输入命令如下:

mkdir -p tcp_server  tcp_client
cd tcp_server
touch tcp_server.c
vim tcp_server.c
server.c代码
#include<unistd.h>
#include<netdb.h>
#include<sys/socket.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<signal.h>
#include<time.h>

int sockfd;//全局变量

//信号处理函数,按住ctrl+c终止掉服务器的进程
void sig_handler(int signo){	
	if(signo==SIGINT){	
		printf("server close\n");
		//关闭socket,最开始创建的socket
		close(sockfd);
		exit(1);
	}
}

//输出客户端的信息,如果是本机地址,服务器和客户端共用的一个地址127.0.0.1
void  out_addr(struct sockaddr_in *clientaddr){	//将端口从网络字节序转换为主机字节序
	
	int port=ntohs(clientaddr->sin_port);
	char ip[16];
	memset(ip, 0 , sizeof(ip));
	//将ip地址从网络字节序转换为点分十进制
	inet_ntop(AF_INET,
			&clientaddr->sin_addr.s_addr,ip,sizeof(ip));
	printf("client:%s(%d) connected\n",ip,port);//打印客户端的ip和端口号
}
//与客户端进行I/O 读写操作
void do_services(int fd){
	long t = time(0);
	char *s=ctime(&t);
	size_t size=strlen(s)*sizeof(char);
	//将服务端获得的系统时间写回到客户端	
	if(write(fd,s,size)!=size){
		perror("write error");
	}
}
int main(char argc,char *argv[]){

	if(argc < 2 ){//参数个数出错处理	
		printf("usage :%s#port\n",argv[1]);
		exit(1);	
	}
	if(signal(SIGINT,sig_handler)==SIG_ERR){//	
		perror("signal sigint error");
		exit(1);
	}
	// 步骤1:创建socket套接字,socket创建在内核中,是一个结构体
  //AF_INET:IPV4 SOCK_STREAMM:tcp协议	
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	
	//步骤2:调用bind函数将socket和地址(ip、port)进行绑定
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	
	//往地址写入ip、port、internet地址族类型	
	serveraddr.sin_family=AF_INET;	
	
	//命令行接受的端口号把主机字节序转化为网络字节序
	serveraddr.sin_port=htons(atoi(argv[1]));
	
	//将服务器ip地址设置为本地上网卡任何类型的ip地址	
	serveraddr.sin_addr.s_addr= htonl(INADDR_ANY);	
	
	//绑定失败处理
	if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
	{
		perror("bind error");
		exit(1);
	}

	//步骤3:调用listen函数启动监听(指定port监听)通知系统去接受来自客户端的连接请求
	if(listen(sockfd, 10) < 0){
		perror("listen error");
		exit(1);	
	}
	
	//步骤4:调用accept函数从队列中获得一个客户端的请求,并返回新的socket描述符	
	struct sockaddr_in clientaddr;
	socklen_t clientaddr_len = sizeof(clientaddr);
	while(1){	
		int fd=accept(sockfd,(struct sockaddr*)&clientaddr,
				&clientaddr_len);
		if(fd < 0){
			perror("accept error");
			continue;		
		}
		
		//调用IO函数和客户端连接进行双向的通信
		out_addr(&clientaddr);
		do_services(fd);
		//关闭与客户端连接的套接字
		close(fd);
	}
}

在vim里面编写完成之后按一下esc退出编辑模式,按住shift+:输入wq,回车就完成保存退出了。
然后转到命令行输入如下:

cd tcp_client.c
vim tcp_client.c
client.c代码如下
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<memory.h>
#include<sys/socket.h>
#include<netdb.h>

int main(int argc,char* argv[]){

	if(argc < 3){
		printf("usage :%s# ip port\n",argv[0]);
		exit(1);		
	}

	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0){	
		perror("sockfd error");
		exit(1);
	}
  //创建专用地址结构体用于存放客户端的ip和端口号以及协议族信息
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));

	//往serveraddr中填入客户端要连接服务器的ip、port和通讯满足的地址族协议
	serveraddr.sin_family=AF_INET;

	//主机字节序转换为网络字节序
	serveraddr.sin_port=htons(atoi(argv[2]));

	//将从命令行获取到的点分十进制ip转换为网络字节序存入s_addr
	inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);

	//客户端调用connect函数连接到服务器端
	if(connect(sockfd,(struct sockaddr*)&serveraddr, sizeof(serveraddr))< 0){
		perror("connect error");
		exit(1);	
	}

	char buffer[1024];
	memset(buffer,0,sizeof(buffer));
	size_t size;
	//读取服务器返回的数据
	if(size= read(sockfd,buffer,sizeof(buffer))<0){
		perror("read error");
	
	}
	//写回到屏幕下显示
	if(write(STDOUT_FILENO,buffer,size)!=size){
		perror("write error");
	}
	close(sockfd);
	return 0;
}

然后回到终端编译两个c程序,编译器选择gcc,在终端输入如下命令:

gcc -o  tcp_server.out  tcp_server.c
gcc -o  tcp_client.out  tcp_client.c

然后运行tcp_server.out,打开服务器,需要输入一个端口等待客户端连接
,接着运行客户端,输入服务器ip和端口号,就连接成功了!效果如下图:
在这里插入图片描述注意这里是两个命令行窗口!
这里我使用的127.0.0.1回环地址,服务器和客户端共用一个地址,于是我在思考能不能,再另外创建一台虚拟机,然后把两台虚拟机网络配置一下,用桥接模式配置在同一网段下,然后用客户端去连接服务器,然后我又搭建了一个Ubuntu环境不过镜像选择的是16.04的,内核版本是4.5.0-142-generic,然后把程序复制到该虚拟机下运行,结果服务器端拒绝了我的访问

在这里插入图片描述但是通过另一台虚拟机带的浏览器输入ip和端口号可以连接上服务器
在这里插入图片描述132是另一台虚拟机的主机号,服务器的主机号是131,两台机器是可以相互ping通的
在这里插入图片描述

请大佬帮忙看一下为什么会拒绝访问(防火墙问题已排除),写的第一篇文章居然遇到bug,不过刚开始学习linux操作系统,希望记录一下自己的学习情况,加油!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听风就是

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值