谨以此文章记录我的研究生生涯第一篇博客
本文主要内容是关于在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操作系统,希望记录一下自己的学习情况,加油!