20201317 LYX 第12周学习总结
第12周
知识总结
- 论述TCP/IP协议及其应用,具体包括TCP/IP栈、IP地址、主机名、DNS、IP数据包和路由器;
- 介绍TCP/IP网络中的UDP和TCP协议、端口号和数据流;
- 阐述服务器—客户机计算模型和套接字编程接口;
- 介绍Web和CGI编程,解释HTTP编程模型、Web页面和Web浏览器;
- 展示如何配置Linux HTTPD服务器来支持用户Web页面、PHP和CGI编程;
- 阐释客户机和服务器端动态Web页面。
一、TCP 协议的作用
互联网由一整套协议构成。TCP 只是其中的一层,有着自己的分工。
最底层的以太网协议(Ethernet)规定了电子信号如何组成数据包(packet),解决了子网内部的点对点通信。
但是,以太网协议不能解决多个局域网如何互通,这由 IP 协议解决。
Socket套接字
套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
前面介绍过,本地的进程间通信(IPC)有很多种方式,常见的总结以下几点:
1、管道(包括无名管道和命名管道);
2、消息队列;
3、信号量;
4、共享存储。
5、……( Socket和Streams支持不同主机上的两个进程IPC)。
java实践(原创 SM2加密 一次一密)
ChatClient
package TCP3;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.util.encoders.Hex;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Scanner;
import static TCP1.SM2Utils.*;
/**
* 注意用到的输入输出流DataInputStream和DataOutputStream,成对出现,最好用字节流
* 20201317 Yongxin Lu
* 2022/11/3
*/
// 客户端类
public class ChatClient {//创建公共类
private String host = "localhost";// 默认连接到本机
private int port = 8189;// 默认连接到端口8189
public ChatClient() {
}
// 连接到指定的主机和端口
public ChatClient(String host, int port) {//构造方法
this.host = host;//将构造方法的参数host传递给类变量host
this.port = port;//将构造方法的参数port传递给类变量port
}
public void chat() {//chat方法
//socket链接
try {
// 连接到服务器
Socket socket = new Socket(host, port);//创建Socket类对象
try {
DataInputStream in = new DataInputStream(socket
.getInputStream());// 读取服务器端传过来信息的DataInputStream
DataOutputStream out = new DataOutputStream(socket
.getOutputStream());// 向服务器端发送信息的DataOutputStream
Scanner scanner = new Scanner(System.in);// 装饰标准输入流,用于从控制台输入
while (true) {
//SM2的连接准备---实现密钥更迭
String publicKeyHex = null;
String privateKeyHex = null;
KeyPair keyPair = createECKeyPair();
PublicKey publicKey = keyPair.getPublic();
if (publicKey instanceof BCECPublicKey) {
//获取65字节非压缩缩的十六进制公钥串(0x04)
publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
//System.out.println("---->SM2公钥:" + publicKeyHex);
}
PrivateKey privateKey = keyPair.getPrivate();
if (privateKey instanceof BCECPrivateKey) {
//获取32字节十六进制私钥串
privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
//System.out.println("---->SM2私钥:" + privateKeyHex);
}
//生成pubkeyc公钥,并且传向服务器
if(publicKeyHex!=null)out.writeUTF(publicKeyHex);
System.out.println("send pubkeyc:" + publicKeyHex);//输出键盘输出内容提示 ,也就是客户端向服务器端发送的消息
//从服务器接收pubkeys公钥
String pubkeys=in.readUTF();
System.out.println("pubkeys:"+pubkeys);
//发送数据
String send = scanner.nextLine();//读取控制台输入的内容
// 把从控制台得到的信息传送给服务器
String send2="Client:"+send;
String encryptData = encrypt(pubkeys, send2);
out.writeUTF(encryptData);//将客户端的信息传递给服务器
//读取服务器信息,利用私钥解密
String accpet = in.readUTF();// 读取来自服务器的信息
System.out.println(accpet);
String data = decrypt(privateKeyHex, accpet);
System.out.println(data);//输出来自服务器的信息
}
} finally {
socket.close();//关闭Socket监听
}
} catch (IOException e) {//捕获异常
e.printStackTrace();
}
}
public static void main(String[] args) {//主程序方法
new ChatClient().chat();//调用chat方法
}
}
ChatServer
package TCP3;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.util.encoders.Hex;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Scanner;
import static TCP1.SM2Utils.*;
/**
* 模拟qq聊天功能: 实现客户端与服务器(一对一)的聊天功能,客户端首先发起聊天,输入的内容在服务器端和客户端显示,
* 然后服务器端也可以输入信息,同样信息也在客户端和服务器端显示
*/
// 服务器类
public class ChatServer {//ChatServer类
private int port = 8189;// 默认服务器端口
public ChatServer() {
}
// 创建指定端口的服务器
public ChatServer(int port) {//构造方法
this.port = port;//将方法参数赋值给类参数
}
// 提供服务
public void service() {//创建service方法
try {// 建立服务器连接
ServerSocket server = new ServerSocket(port);//创建 ServerSocket类
Socket socket = server.accept();// 等待客户连接
try {
DataInputStream in = new DataInputStream(socket
.getInputStream());// 读取客户端传过来信息的DataInputStream
DataOutputStream out = new DataOutputStream(socket
.getOutputStream());// 向客户端发送信息的DataOutputStream
Scanner scanner = new Scanner(System.in);//从键盘接受数据
while (true) {
//SM2的连接准备-->实现密钥更迭
String publicKeyHex = null;
String privateKeyHex = null;
KeyPair keyPair = createECKeyPair();
PublicKey publicKey = keyPair.getPublic();
if (publicKey instanceof BCECPublicKey) {
//获取65字节非压缩缩的十六进制公钥串(0x04)
publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
//System.out.println("---->SM2公钥:" + publicKeyHex);
}
PrivateKey privateKey = keyPair.getPrivate();
if (privateKey instanceof BCECPrivateKey) {
//获取32字节十六进制私钥串
privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
//System.out.println("---->SM2私钥:" + privateKeyHex);
}
//收公钥c
String pubkeyc = in.readUTF();// 读取来自客户端的信息
System.out.println("pubkeyc:"+pubkeyc);//输出来自客户端的信息
//发公钥s
if(publicKeyHex!=null)out.writeUTF(publicKeyHex);
System.out.println("Send pubkeys:"+publicKeyHex);
//读取服务器信息,利用私钥解密
String accpet = in.readUTF();// 读取来自服务器的信息
System.out.println(accpet);
String data = decrypt(privateKeyHex, accpet);
System.out.println(data);//输出来自服务器的信息
//接收命令行的信息
String send = scanner.nextLine();//nextLine方式接受字符串
System.out.println("Server:" + send);//输出提示信息
//利用公钥加密
//System.out.println(send+" len: "+send.length());
String send2="Server:"+send;
String encryptData = encrypt(pubkeyc, send2);
System.out.println("encryptData:"+encryptData);
//发送加密后的信息
out.writeUTF(encryptData);//把服务器端的输入发给客户端
//接收Client信息
//String accpet = in.readUTF();// 读取来自客户端的信息
//System.out.println("Client:"+accpet);
}
} finally {// 建立连接失败的话不会执行socket.close();
socket.close();//关闭连接
server.close();//关闭
}
} catch (IOException e) {//捕获异常
e.printStackTrace();
}
}
public static void main(String[] args) {//主程序方法
new ChatServer().service();//调用 service方法
}
}
C
Client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SERVER_PORT 9999
#define BUFF_LEN 512
#define SERVER_IP "127.0.0.1"
void udp_msg_sender(int fd, struct sockaddr* dst)
{
socklen_t len;
struct sockaddr_in src;
while(1)
{
char buf[BUFF_LEN] = "TEST UDP MSG!\n";
len = sizeof(*dst);
printf("client:%s\n",buf); //打印自己发送的信息
sendto(fd, buf, BUFF_LEN, 0, dst, len);
memset(buf, 0, BUFF_LEN);
recvfrom(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&src, &len); //接收来自server的信息
printf("server:%s\n",buf);
sleep(1); //一秒发送一次消息
}
}
/*
client:
socket-->sendto-->revcfrom-->close
*/
int main(int argc, char* argv[])
{
int client_fd;
struct sockaddr_in ser_addr;
client_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(client_fd < 0)
{
printf("create socket fail!\n");
return -1;
}
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
//ser_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); //注意网络序转换
ser_addr.sin_port = htons(SERVER_PORT); //注意网络序转换
udp_msg_sender(client_fd, (struct sockaddr*)&ser_addr);
close(client_fd);
return 0;
}
Server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define SERVER_PORT 9999
#define BUFF_LEN 1024
void handle_udp_msg(int fd)
{
char buf[BUFF_LEN]; //接收缓冲区,1024字节
socklen_t len;
int count;
struct sockaddr_in clent_addr; //clent_addr用于记录发送方的地址信息
while(1)
{
memset(buf, 0, BUFF_LEN);
len = sizeof(clent_addr);
count = recvfrom(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, &len); //recvfrom是拥塞函数,没有数据就一直拥塞
if(count == -1)
{
printf("recieve data fail!\n");
return;
}
printf("client:%s\n",buf); //打印client发过来的信息
memset(buf, 0, BUFF_LEN);
sprintf(buf, "I have recieved %d bytes data!\n", count); //回复client
printf("server:%s\n",buf); //打印自己发送的信息给
sendto(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, len); //发送信息给client,注意使用了clent_addr结构体指针
}
}
/*
server:
socket-->bind-->recvfrom-->sendto-->close
*/
int main(int argc, char* argv[])
{
int server_fd, ret;
struct sockaddr_in ser_addr;
server_fd = socket(AF_INET, SOCK_DGRAM, 0); //AF_INET:IPV4;SOCK_DGRAM:UDP
if(server_fd < 0)
{
printf("create socket fail!\n");
return -1;
}
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址,需要进行网络序转换,INADDR_ANY:本地地址
ser_addr.sin_port = htons(SERVER_PORT); //端口号,需要网络序转换
ret = bind(server_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
if(ret < 0)
{
printf("socket bind fail!\n");
return -1;
}
handle_udp_msg(server_fd); //处理接收到的数据
close(server_fd);
return 0;
}