目录
一:C/S架构框架
基于流套接字的编程流程
二:Linux网络编程基础函数
C/S架构 【客户端Client 服务器Server】
查看socket使用说明
查看bind使用说明
查看listen使用说明
查看accept使用说明
查看connect使用说明
查看fork使用说明 (创建进程 进程间通信 服务器与客户端进程间通信)
测试计算机网络是否可用 计算机网卡是否损坏
ping 127.0.0.1 本机
三:C/S架构实现
服务器与客户端之间通信
(fork创建进程,accept阻塞,每有一个客户端上线才会frok创建一个进程,不需要传参,fork会拷贝代码)
(创建线程,acceptfd传参,每有一个客户端上线会创建一个线程,在后面的聊天系统案例中可以学习到)
对于聊天案例,多个客户端之间要实现互相聊天,就需要数据共享
对于fork创建进程,进程间的数据共享需要使用IPC技术,比较麻烦
在后面聊天系统开发,会使用到线程(进程包含线程,一个进程中的线程会共享该进程的资源,也就是可以数据共享),因此此篇文章的学习仅仅提供一个C/S架构网络概念,想要深入学习C/S架构开发,可以阅读博主后面的文章!
服务器实现
#include<iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
using namespace std;
int main()
{
pid_t pid = 0;
char buf[50] = { 0 };
struct sockaddr_in addr;
int len = 0;
int acceptfd = 0;
//初始化网络 识别当前计算机是否可以联网
//第一个参数:采用IPV4 IP地址 第二个参数:网络分配TCP
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd == -1)
{
perror("socket error");
}
else
{
cout << "socketfd = " << socketfd << endl;
//确定用IPV4地址
addr.sin_family = AF_INET;
//服务器开放自己的IP地址给客户端连接使用 INADDR_ANY生成默认的可以联网的IP地址
addr.sin_addr.s_addr = INADDR_ANY;
//绑定服务器端口号0-65535 10000以下系统默认使用
addr.sin_port = htons(10086);
len = sizeof(addr);
//bind 绑定ip地址 绑定端口号
if (bind(socketfd, (struct sockaddr*)&addr, len) == -1)
{
perror("bind error");
}
if (listen(socketfd, 10) == -1)
{
perror("listen error");
}
cout << "网络搭建成功" << endl;
while (1)
{
cout << "服务器等待客户端连接........." << endl;
//服务器等待客户端连接 阻塞式函数 acceptfd在服务器代表已经连接成功的客户端
acceptfd = accept(socketfd, NULL, NULL);
cout << "有客户端成功连接 acceptfd = " << acceptfd << endl;
pid = fork();
if (pid == 0)
{
while (1)
{
int res = read(acceptfd, buf, sizeof(buf));
cout << "服务器收到 res = " << res << "buf = "<<buf<< endl;
}
}
}
}
return 0;
}
客户端实现
#include<iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
using namespace std;
int main()
{
char buf[50] = { 0 };
struct sockaddr_in addr;
int len = 0;
//初始化网络 识别当前计算机是否可以联网
//第一个参数:采用IPV4 IP地址 第二个参数:网络分配TCP
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
cout << "客户端 socketfd = " << socketfd << endl;
if (socketfd == -1)
{
perror("socket error");
}
else
{
//确定用IPV4地址
addr.sin_family = AF_INET;
//客户端主动寻找服务器IP地址 127.0.0.1本机回环地址 192.168.75.128
addr.sin_addr.s_addr = inet_addr("192.168.75.128");
//绑定服务器端口号0-65535 10000以下系统默认使用
addr.sin_port = htons(10086);
len = sizeof(addr);
//主动去连接服务器 IP和端口
if (connect(socketfd, (struct sockaddr*)&addr, len) == -1)
{
perror("connect error");
}
else
{
cout << "客户端连接服务器成功" << endl;
}
while (1)
{
cin >> buf;
int res = write(socketfd, buf, sizeof(buf));
cout << "客户端发送 res = " << res << endl;
bzero(buf, sizeof(buf));
}
}
return 0;
}
结果测试:
一个服务器 ,可以接收多个客户端发送过来的消息,实现了网络通信(进程间通信)
测试中可能出现问题:
如:客户端先下线【由于fork子进程拷贝父进程之前所有代码,acceptfd共用一块地址,当父进程先结束,子进程还在执行,此时造成孤儿进程,对于acceptfd的地址,在父进程结束的时候也没有了,因此子进程中acceptdf在read的时候就会出现错误,又因为使用while(1)因此,才有如下图一直读不到数据的情况】
解决:先关闭服务器 后关闭客户端
[但是在实际中其实服务器不能先关闭的,只能客户端下线]
对于客户端下线又分为两种情况:
客户端主动下线 & 客户端异常情况下线(如客户PC突然断点或蓝屏)
因此目前所搭建的C/S架构只是大致思路,需要做进一步改进的
测试中可能出现问题:
如关闭服务器后 再次运行服务器
出现问题:bind error【因为在关闭服务器之后,端口号不会立即释放(端口号被占用),需要等个三五分钟,等待到端口号释放后就可再次使用端口号,建立C/S连接】
bind error 解决:需要等个三五分钟,再次运行服务器
对于以上问题的出现,是因为刚刚学习服务器客户端网络通信,目前的C/S架构还是稚嫩,在今后的学习中会将问题深入改进
此篇文章主要学习的是C/S架构设计流程
服务器 端口复用
客户端下线处理
对于以上两个问题,可以参考下面这篇文章解决