1.TCP和UDP
TCP和UDP属于传输层
TCP属于数据流协议,基于连接,保证传输的稳定和数据完整性。类似于打电话
UDP属于数据报协议,不基于连接,可能导致数据丢失。类似于发短信
2.Linux中的应用
(1)socket(套接字)编程
概念
socket也叫套接字,最初用于进程间通信,当前主要用于网络通信,通过socket可以使网络通信使用固定的步骤来实现连接和传输。
socket在网络编程中类似于文件描述符在文件IO中的应用。用来代表一个网络通信连接,socket描述符本质上也是一个非负的整数。
socket属于TCP/IP中的传输/应用层。
(2)sokcet的应用
1)获取socket描述符的函数 ---------- socket函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//像文件IO的open
参数:
domain - 协议簇(多个协议的集合)
AF_INET6:IPV6通信
type - 通信类型(传输层协议)
SOCK_STREAM:数据流协议(TCP)
SOCK_DGRAM:数据报协议(UDP)
protocol - 协议
由于通信协议已经由前两个参数确定,直接给0
返回值:
成功返回一个socket描述符,失败返回-1
在socket通信中,使用C/S架构,属于服务器客户端模式,一般C/S架构属于一服务器对多客户端,通信由客户端发起,服务器被动响应。
(3)TCP服务器和客户端的实现
1)服务器实现
1.获取socket描述符
int sockfd = socket(AF_INET,SOCK_STREAM,0);
2.准备通信地址(ip和端口号)
struct sockaddr_in addr;
//....
3.绑定socket描述符和通信地址 ------ bind函数
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//参数就是描述符和地址,地址需要强转
4.将socket改为监听模式 ------- listen函数
int listen(int sockfd, int backlog);
//backlog表示处理和监听队列的最大长度
5.等待客户端连接 -------- accept
//该函数是一个阻塞函数,会一直阻塞直到有客户端连上来位置
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//后两个参数传出连上来的客户端的地址
//返回新的socket描述符(用于和连上来的客户端通信)
//连接和通信有不同的两个描述符,一个负责通信,一个负责连接
6.通过上一步得到的新描述符,使用read/write和客户端通信
7.不在通信,使用close关闭描述符断开连接
2)客户端实现
1.获取socket描述符
int sockfd = socket(AF_INET,SOCK_STREAM,0);
2.准备服务器通信地址(ip和端口号)
struct sockaddr_in addr;
//....
3.连接服务器 -------- connect函数
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//参数和bind一致,返回0(成功)和-1(失败)
6.通过上一步得到的新描述符,使用read/write和客户端通信
7.不在通信,使用close关闭描述符断开连接
注意:准备通信地址一般包含字节顺序转换。
代码实现:
//tcp-server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc,char **argv)
{
if(argc!=3){
printf("Usage:%s ip port\n",argv[0]);
exit(-1);
}
//1.创建socket描述符
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1){
perror("socket");
exit(-1);
}
//2.准备通信地址
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));//端口号
addr.sin_addr.s_addr = inet_addr(argv[1]);//IP
//3.bind
int res = bind(sockfd, (struct sockaddr *)&addr,sizeof(addr));
if(res==-1){
perror("bind");
exit(-1);
}
//4.listen
listen(sockfd,100);
//5.等待客户端连接
while(1){
struct sockaddr_in cilent;
socklen_t len = sizeof(cilent);
int newfd = accept(sockfd, (struct sockaddr *)&cilent, &len);
if(newfd==-1){
perror("accept");
exit(-1);
}
printf("%s到此一游!\n",inet_ntoa(cilent.sin_addr));
//6.和客户端通信
char buf[100] = {};
res = read(newfd, buf, sizeof(buf));
if(res<=0)
break;
printf("%s说:%s\n", inet_ntoa(cilent.sin_addr), buf);
close(newfd);
}
close(sockfd);
return 0;
}
//tcp-client
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc,char **argv)
{
if(argc!=3){
printf("Usage:%s ip port\n",argv[0]);
exit(-1);
}
//1.创建socket描述符
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1){
perror("socket");
exit(-1);
}
//2.准备通信地址
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));//端口号
addr.sin_addr.s_addr = inet_addr(argv[1]);//IP
//3.connect
int res = connect(sockfd, (struct sockaddr *)&addr,sizeof(addr));
if(res==-1){
perror("connect");
exit(-1);
}
//和服务器通信
write(sockfd, "hello", 6);
close(sockfd);
return 0;
}