TCP 介绍、编程流程
TCP 回顾
- 面向连接的流式协议;
- 可靠,出错重传,且每收到一个数据给相应的确认
- 通信之前建立链接
TCP 与 UDP 的差异
TCP C/S 架构
TCP 编程-socket
TCP 套接字创建
UDP 套接字创建回顾
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd <0)
{perror("socket");exit(-1);
}
创建 TCP 套接字
int sockfd;
sockfd &#61; socket(AF_INET, SOCK_STREAM, 0);
if(sockfd <0)
{perror("socket");exit(-1);
}
做为客户端需要具备的条件
- 知道“服务器”的ip&#xff0c;port
- 主动连接“服务器”
- 用到的函数
socket 创建“主动TCP套接字”
connect 连接“服务器”
send 发送数据到“服务器”
recv 接受“服务器”的响应
close 关闭连接
TCP 客户端-connect、send、recv
connect 函数
/**function:* 主动跟服务器建立链接*parameter&#xff1a;* sockfd&#xff1a;socket套接字* addr&#xff1a; 连接的服务器地址结构* len&#xff1a; 地址结构体长度*return&#xff1a;* 成功&#xff1a;0* 失败&#xff1a;其他*note&#xff1a;* connect建立连接之后不会产生新的套接字* 连接成功后才可以开始传输TCP数据
*/
#include int connect(int sockfd, sonst struct sockadr *addr, socklen_t len);
send 函数
/**function&#xff1a;* 发送数据*parameter&#xff1a;* sockfd&#xff1a; 已建立连接的套接字* buf&#xff1a; 发送数据的地址* nbytes&#xff1a; 发送缓数据的大小(字节为单位)* flags&#xff1a; 套接字标志&#xff08;常为0&#xff09;*return&#xff1a;* 成功&#xff1a;发送的字节数*note&#xff1a;* 不能用TCP协议发送 0 长度的数据包*/
#include ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
recv 函数
/**function&#xff1a;* 接收网络数据*paratemer&#xff1a;* sockfd&#xff1a; 套接字* buf: 接收网络数据的缓冲区的地址* nbytes: 接收缓冲区的大小&#xff08;字节为单位&#xff09;* flags: 套接字标志&#xff08;常为0&#xff09;*return:* 成功&#xff1a;接收到字节数*/
#include ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
客户端 code
#include
#include
#include
#include
#include
#include
#include int main(int argc, char *argv[])
{unsigned short port &#61; 8000; //服务器的端口号char *server_ip &#61; "172.20.226.11"; //服务器的IP//给main传参if(argc > 1) //函数参数&#xff0c;可以改服务器IP{server_ip &#61; argv[1];}if(argc > 2) //函数参数&#xff0c;可以改服务器端口号{port &#61; atoi(argv[2]);}//创建TCP套接字int sockfd &#61; 0;sockfd &#61; socket(AF_INET, SOCK_STREAM, 0); //创建通行端点&#xff1a;套接字if(sockfd <0){perror("socket");exit(-1);}//设置连接的IP&#xff0c;端口struct sockaddr_in server_addr;bzero(&server_addr, sizeof(server_addr)); //初始化服务器的地址server_addr.sin_family &#61; AF_INET;server_addr.sin_port &#61; htons(port);inet_pton(AF_INET, server_ip, &server_addr.sin_addr);//连接服务器int err_log &#61; connect(sockfd, (struct sockaddr *)&server_addr,sizeof(server_addr));if(err_log !&#61; 0){perror("connect");close(sockfd);exit(-1);}char send_buf[512] &#61; "";char recv_buf[512] &#61; "";printf("send data to %s&#xff1a;%d \n", server_ip,port);//发送消息printf("send");fgets(send_buf, sizeof(send_buf), stdin);send_buf[strlen(send_buf) - 1] &#61; 0; //除去‘\n’send(sockfd, send_buf, strlen(send_buf), 0); //向服务器发送数据//接收数据recv(sockfd, recv_buf, sizeof(recv_buf), 0); //接受服务器的响应printf("recv: %s \n",recv_buf);close(sockfd);}
TCP 服务器-bind、listen、accept
做为 TCP 服务器需要具备的条件
- 有一个确定的地址
- 操作系统知道一个服务器
- 等待连接的到来
对于面向连接TCP协议来说&#xff0c;连接的建立才是数据通信的开始
bind 示例
int err_log &#61; 0;
unsigned short port &#61; 8000;
struct sockaddr_in my_addr;bzero(&muy_addr, sizeof(my_addr));
my_addr.sin_family &#61; AF_INET;
my_addr.sin_port &#61; htons(port);
my_addr.sin_addr.s_addr &#61; htonl(INADDR_ANY);err_log &#61; bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
if(err_log !&#61; 0)
{perror("binding");close(sockfd);exit(-1);
}
listen 函数
/*
功能:套接字由主动改为被动操作系统为套接字设置一个连续队列&#xff0c;用来记录所有连接到该套接字的连接参数&#xff1a;sockfd&#xff1a;socket监听套接字backlog&#xff1a;连接队列的长度返回值&#xff1a;成功&#xff1a;返回 0失败&#xff1a;其他
*/
#include int listen(int sockfd, int backlog);
accept 函数
/*
功能&#xff1a;从已连接队列中取出一个已经建立的连接&#xff0c;如果没有任何连接可用&#xff0c;则进入睡眠等待&#xff08;阻塞&#xff09;参数&#xff1a;sockfd&#xff1a;socket 监听套接字cliaddr&#xff1a;用于存放客户端套接字地址结构addrlen&#xff1a;套接字地址结构体长度的地址返回值&#xff1a;已连接套接字注意&#xff1a;返回的是一个已连接套接字&#xff0c;该套接字代表当前这个连接*/
#include int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
TCP 服务器例子
#include
#include
#include
#include
#include
#include
#include int main(int argc, char *argv[])
{unsigned short port &#61; 8000;if(argc > 1){port &#61; atoi(argv[1]);}//创建TCP套接字int sockfd &#61; socket(AF_INET, SOCK_STREAM, 0);if(sockfd <0){perror("socket");exit(-1);}//组织本地信息struct sockaddr_in my_addr;bzero(&my_addr, sizeof(my_addr));my_addr.sin_family &#61; AF_INET;my_addr.sin_port &#61; htons(port);my_addr.sin_addr.s_addr &#61; htonl(INADDR_ANY);//绑定信息int err_log &#61; bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));if(err_log !&#61; 0){perror("binding");close(sockfd);exit(-1);}//“主动”变“被动”err_log &#61; listen(sockfd, 10);if(err_log !&#61; 0){perror("listen");close(sockfd);exit(-1);}printf("listen client &poort &#61; %d...\n",port);while(1){struct sockaddr_in client_addr;char cli_ip[INET_ADDRSTRLEN] &#61;"";socklen_t cliaddr_len sizeof(client_addr);int connfd;//等待连接的到来connfd &#61; accept(sockfd, (struct sockaddr *)&client_addr, &cliaddr_len);if(connfd <0){perror("accept");continue;}//转换并打印信息inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf("--------------------------------\n");printf("client ip &#61; %s, port &#61; %d\n",cli_ip, ntohs(client_addr.sin_port));char recv_buf[2048] &#61; "";//接收消息while(recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0){printf("\n recv data: \n");printf("%s\n",recv_buf);}close(connfd); //关闭已连接的套接字printf("client closed \n");}close(sockfd); //关闭监听的套接字return 0;
}
TCP 编程-close、三次握手、四次挥手
close 关闭套接字
关闭一个代表已连接套接字将导致另一端接收到一个0长度的数据包
1 关闭监听套接字将导致服务器无法接收新的连接&#xff0c;但不影响已建立的连接
2 关闭accept返回已连接套接字将导致它代表的连接被关闭&#xff0c;但不会影响服务器的监听
关闭连接就是关闭连接&#xff0c;不意味着其他
三次握手
四次挥手
TCP 并发服务器
多进程实现并发
//#include <头文件>int main(int argc, char *argv[])
{//创建套接字sockfd//绑定&#xff08;bind&#xff09;套接字sockfd//监听&#xff08;listen&#xff09;套接字sockfdwhile(1){int connfd &#61; accept();if(fork() &#61;&#61; 0) //子进程{close(sockfd); //关闭监听套接字sockfdfun(); //服务客户端的具体事件在fun里实现close(connfd); //关闭已连接套接字connfdexit(0); //结束子进程}close(connfd); //关闭已连接套接字connfd}close(sockfd);return 0;
}
多线程实现并发
//#include<头文件>int main(int argc, char *argv[])
{//创建套接字sockfd//绑定&#xff08;bind&#xff09;套接字sockfd//监听&#xff08;listen&#xff09;套接字sockfdwhile(1){int connfd &#61; accept();pthread_t tid;pthread_create(&tid, NULL, (void *)client_fun, (void *)connfd);pthread_detach(tid);}close(sockfd); //关闭监听套接字return 0;
}void *client_fun(void *arg)
{int connfd &#61; (int) arg;fun(); //服务与于客户端的具体程序close(connfd);
}
Web 服务器介绍
web 服务器简介
web服务器&#xff1a;
www服务器&#xff0c;网站服务器
特点&#xff1a;
使用HTTP协议与客户机浏览器进行信息交流
不仅存储信息&#xff0c;还可以在用户通过web浏览器提供的信息的基础上运行脚本和程序
该服务器可安装在UNIX&#xff0c;Linux&#xff0c;Windows等操作系统上
著名的服务器有Apache&#xff0c;Tomcat&#xff0c;IIS
HTTP 协议
webserver HTTP协议
概念
一种详细规定了浏览器和万维网服务器之间互相通信的规则&#xff0c;通过因特网传送万维网文档的数据传送协议
特点
- 支持C/S架构
- 简单快速&#xff1a;客户向服务器请求服务时&#xff0c;只传送请求方法和路径&#xff0c;常用GET&#xff0c;POST
- 无连接&#xff1a;限制每次连接只处理一个请求
- 无状态&#xff1a;如果后续处理需要前面的信息&#xff0c;它必须重传&#xff0c;这样可能导致每次连接传送的数据量增大
Webserver 通信过程