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