TCP建立连接 (可靠传输,丢包重发)
UDP不建立连接 (不可靠传输,丢包不重发)适合:视频点播
网络畅通时,使用UDP较快,不易丢包,阻塞时容易丢包使用TCP
UDP比TCP开销小,没有校验等,速度比TCP快
IP协议是管理路径选择, TCP是管理是否传送正确
为什么TCP是一段一段的发送数据包,是因为不让一直占用端口,页可以和其他需要同端口的一起用。
socket 是网络套接口,TCP 和 UDP想进行网络通信,必须使用它(进程之间通信,不同主机之间通信)
创建套接口成功返回文件描述符(当文件),设备文件不可以定位,普通文件可以定位
传上网络的可以写函数:write, send, sendto
从网上传下来的读函数:read, recv, recvfrom
TCP协议上面三种都可以使用
UDP协议只能使用sendto 和 recvfrom
TCP,IP,网络接口都在内核态
socket是用户态与内核态之间的桥梁
套接口的类型
1.流式套接字(SOCK_STREAM)TCP协议使用
2.数据报套接字(SOCK_DGRAM)UDP协议使用
3.原始套接字(SOCK_RAM)用于测试新的网络协议
IP只能找到主机,端口号区分不同进程
地址结构
API使用的地址就是这样的类型
这个是IPV4的类型,使用它之后要强转为上面那个通用类型,因为API使用的是通用类型。
地址转换
int inet_pton()
int inet_ntop()
gethostbyname() // 通过输入网址找IP
inet_addt("");//里面写点分字符串,不需要使用字节序转换,其他的就要用字节序转了
计算机都是小端字节序,网络字节序都是大端字节序。
在传输内容时,如果是字符串就不用转换,因为一个字符就占一个字节不存在高位和地位
如果不是字符串,就需要转了。
TCP模型
客户端:用connect, socket函数
服务器:用listen accept,socket函数
socket创建的接口是主动建立请求
listen:两个功能,一是监听队列长度,二是把socket创建的主动变为被动
accept是建立连接
bind:绑定IP地址和端口号到socket上(绑定的是自己的地址)
客户端和服务器都可以使用,客户端可用可以不用,服务器端必须使用
connect也需要配置地址,这个地址是需要连接的地址
accept返回的是一个套接口,是连接套接口,用于通信,参数是套接口(监听)
TCP(网络通信)
1.创建
2.配置地址
3.绑定
无法绑定问题?
为什么无法绑定,因为服务器有TIME_WAIT函数,突然关闭服务器之后,在启动就无法绑定,要等半分钟,因为服务器关闭之后,要等半分钟套接口绑定才会消失。
客户端下线,服务器怎么知道
1.看套接口返回值
2.客户端发信息。
重复绑定需要在socket() 和bind之间插入
第一个参数:套接口
第二个参数:绑定那一层
第三个参数:允许地址重复绑定
第四个参数:opt
第五个参数:opt大小
4.监听
5.accept()
accept返回值是一个新的套接口,用于专门与客户端连接。
connect函数是客户端主动连接服务器,在此之前要配置服务器地址(这个地址配置不能是任意的,只能是唯一的)
6.sendto 和 recvfrom函数
TCP连接
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define SERVPORT 8888
//服务器 TCP
int main(int argc, char **argv)
{
int listenfd, connfd; //定义套接口,两个套接口,一个监听,一个是传输用的
char buf[100]; //缓冲区
int n; //返回值,字节数
socklen_t len; //客户端地址长度
struct sockaddr_in servaddr,cliaddr; //地址结构类型,客户端和服务器端
//创建套接口
listenfd = socket(AF_INET, SOCK_STREAM, 3);//地址协议类型IPV4,协议类型(TCP,), 0
bzero(&servaddr, sizeof(servaddr)); //将服务器地址清空
//配置地址
servaddr.sin_family = AF_INET; //IPV4
servaddr.sin_port = htons(SERVPORT); //端口号设置
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //服务器地址,可以是任意一个
//绑定服务器地址在套接口上
bind(listenfd, (struct sockaddr *)&servaddr,sizeof(servaddr));//套接口文件描述符,地址初始位置(注意强制类型转换),地址空间长度
//监听函数,监听多少
listen(listenfd, 1024); //套接口文件描述符,监听长度
for(;;)
{
len = sizeof(cliaddr); //客户端地址长度
//链接
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len); //这里长度需要先定义,拿到之后,再取地址,拿到客户端地址长度,返回值是一个套接口
while(1)
{
//接受客户端发过来的消息
n = recvfrom(connfd, buf, 100, 0, (struct sockaddr *) &cliaddr, &len);
if(0 == n)
{
printf("客户端下线!\n");
break;
}
//到这里我们就可以收到客户端发过来的消息
//对收到的数据进行处理
buf[n] = '\0'; //变成字符串
printf("*******************\n");
printf("收到的数据如下:\n");
printf("%s",buf);
//大小写转化
for(int i = 0; buf[i] != '\0'; i++)
{
if(buf[i] >= 'A' && buf[i] <= 'Z')
{
buf[i] += 32;
}
else if(buf[i] >= 'a' && buf[i] <= 'z')
{
buf[i] -= 32;
}
}
/* for(int i = 0; buf[i] != '#'; i++)
{
printf("%c",buf[i]);
}
*/
printf("*******************\n");
//处理完就要发送回去
sendto(connfd, buf, n, 0,(struct sockaddr*)&cliaddr, len);
}
//关闭套接口
close(connfd);
}
close(listenfd);
return 0;
}
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#define SERVPORT 8888
//客户端 TCP
int main(int argc, char **argv)
{
int sockfd; //一个套接口皆可以了
int n; //收到的字节数;
char sendbuf[100]; //发送缓冲区
char recvbuf[100]; //接收缓冲区
struct sockaddr_in servaddr,cliaddr; //定义结构体变量
//这里地址从命令行输入
if(argc != 2)
{
printf("need server IP\n");
exit(-1);
}
//创建套接口
sockfd = socket(AF_INET, SOCK_STREAM, 0); //地址协议类型,tcp,0
//清空地址空间
bzero(&servaddr, sizeof(servaddr));
//配置地址
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVPORT);
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
//链接
connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
//从键盘上获取
while(fgets(sendbuf,100,stdin) != NULL)
{
//发送到服务器
sendto(sockfd,sendbuf,strlen(sendbuf), 0, (struct sockaddr *) &servaddr,sizeof(servaddr));
//接收服务器返回的数据
n = recvfrom(sockfd, recvbuf, 100, 0, NULL, NULL);
recvbuf[n] = '\0';
fputs(recvbuf,stdout);
}
close(sockfd);
return 0;
}