网络编程
编程
linux操作系统
[用户空间] 应用层 //程序员实现
------------------------------------------------------
[内核空间] 传输层 [网络协议栈] //内核已经实现好的 属于网络功能
网络层
数据链路层
物理层
程序发送数据
系统调用 --- 通过系统调用来使用操作系统提供的网络功能
函数接口 --- socket
socket:
1. 操作系统提供的函数接口 //通过这个接口可以使用网络功能
2. 套接字
在使用的思路上,套接字 被抽象成了文件
特殊文件 --- 专门用来进行网络通信
文件 描述符
read
write
网络编程模型:
1.c/s 模型
client ----- server
QQ
2.b/s 模型
Brower / server //浏览器 - 服务器
3.p2p 模型
peer 2 peer //点 对 点
迅雷下载
云盘下载
------------------------------------------------------------------------------------------------------------
c/s 模型
基于 两种传输协议实现
tcp 协议
传输层的协议 --- 控制传输过程
tcp
特点:
1.面向链接
2.可靠传输
3.字节流
应用:
登录网站 ---tcp
qq登录
udp 协议
侧重 传输效率
udp
特点:
1.无连接
2.不可靠
3.数据报
应用:
视频传输
tcp的连接:
三次握手
client -------------- server
1--连接请求-->
2<--回应------
3---确认----->
client //作为主动的角色 客户端
server //被动角色 服务器
可靠传输:
无差错
无丢失 --- 每个字节 都编号
无失序 ---
无重复 ---
tcp的编程:
流程
1.建立连接
2.数据通信
//通信模型 cs --- 打电话
//客户端
1.socket //买了个手机
2.bind (可选) // sim 卡
3.connect // 拨打电话
//服务器端
1.socket //买了个手机
2.bind // sim 卡
3.listen // 监听 --待机
4.accept // 接听
int socket(int domain, int type, int protocol);
功能:程序向内核提出创建一个基于内存的套接字描述符
参数:domain 地址族,PF_INET == AF_INET ==>互联网程序
PF_UNIX == AF_UNIX ==>单机程序
type 套接字类型:
SOCK_STREAM 流式套接字 ===》TCP
SOCK_DGRAM 用户数据报套接字===>UDP
SOCK_RAW 原始套接字 ===》IP
protocol 协议 ==》0 表示自动适应应用层协议。
返回值:成功 返回申请的套接字id
失败 -1;
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:该函数固定有客户端使用,表示从当前主机向目标
主机发起链接请求。
参数:sockfd 本地socket创建的套接子id
addr 远程目标主机的地址信息。
addrlen: 参数2的长度。
返回值:成功 0
失败 -1;
//客户端
#include<stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<strings.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd<0)
{
perror("socket fail\n");
return -1;
}
struct sockaddr_in seraddr;
bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(50000);
seraddr.sin_addr.s_addr = inet_addr("192.168.0.165");
printf("fd = %d\n",fd);
if(connect(fd,(struct sockaddr*)&seraddr,sizeof(seraddr))<0)
{
perror("connect success!\n");
return -1;
}
write(fd,"hello",6);
printf("connect success\n");
return 0;
}
int bind(int sockfd, struct sockaddr *my_addr,
socklen_t addrlen);
功能:如果该函数在服务器端调用,则表示将参数1相关
的文件描述符文件与参数2 指定的接口地址关联,
用于从该接口接受数据。
如果该函数在客户端调用,则表示要将数据从
参数1所在的描述符中取出并从参数2所在的接口
设备上发送出去。
注意:如果是客户端,则该函数可以省略,由默认
接口发送数据。
返回值:
成功 0
失败 -1
int listen(int sockfd, int backlog);
功能:在参数1所在的套接字id上监听等待链接。
参数:sockfd 套接字id
backlog 允许链接的个数。
返回值:成功 0
失败 -1;
int accept(int sockfd, struct sockaddr *addr,
socklen_t *addrlen);
功能:从已经监听到的队列中取出有效的客户端链接并
接入到当前程序。
参数:sockfd 套接字id
addr
如果该值为NULL ,表示不论客户端是谁都接入。
如果要获取客户端信息,则事先定义变量
并传入变量地址,函数执行完毕将会将客户端
信息存储到该变量中。
addrlen: 参数2的长度,如果参数2为NULL,则该值
也为NULL;
如果参数不是NULL,&len;
一定要写成len = sizeof(struct sockaddr);
返回值:成功 返回一个用于通信的新套接字id;
从该代码之后所有通信都基于该id
失败 -1;
//服务器
#include<stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<strings.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
//socket
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd<0)
{
perror("socket fail\n");
return -1;
}
struct sockaddr_in seraddr;
bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(50000);
seraddr.sin_addr.s_addr = inet_addr("192.168.0.165");
printf("fd = %d\n",fd);
//bind
if(bind(fd,(const struct sockaddr*)&seraddr,sizeof(seraddr))<0)
{
perror("connect success!\n");
return -1;
}
printf("connect success\n");
//listen
if(listen(fd,5)<0)
{
perror("listen fail");
return -1;
}
//accept
int connfd = accept(fd,NULL,NULL);
if(connfd < 0)
{
perror("accept fail");
return -1;
}
printf("--------client-----connectted\n");
read(connfd,"hello",6);
return 0;
}
拓展:
网络通信过程
QQ_A QQ_B
[用户空间] | |
-------------------socket-----------------------socket--------------
[内核空间] [传输层tcp] [传输层tcp]
|
[网络层] [网络层]
|
[数据链路层] [数据链路层]
|
[物理层] -----------------> [物理层]
网络中找到主机 ---- ip
找到主机中进程 ---- 端口号
端口号
作用: 用来标识一个进程
组成:
16位的数据
0~65535
//
1~1023 // 知名端口号
80 http
23
21
1024~50000 //系统注册的
>=50000 //动态的
ip+端口号 ==> 网络中进程的唯一地址
struct sockaddr //地址的类型
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
network byte order //网络字节序 ---大端
//字节序转换
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong); //主机到网络
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong); //网络到主机
uint16_t ntohs(uint16_t netshort);
//ip地址转换
in_addr_t inet_addr(const char *cp);
"192.168.0.1"
练习:点对点聊天(进程或线程;一个进程读,一个进程写;一个线程读,一个线程写)
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
void handler(int signo)
{
wait(NULL);
}
//./client ip port
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("%s <ip> <port>\n",argv[0]);
return -1;
}
int fd = socket(AF_INET,SOCK_STREAM,0);
if (fd < 0)
{
printf("socket fail");
return -1;
}
struct sockaddr_in seraddr;
bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr(argv[1]);
seraddr.sin_port = htons(atoi(argv[2]));
if (connect(fd,(const struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)
{
perror("conncet fail");
return -1;
}
pid_t pid = fork();
if (pid < 0)
{
perror("fork fail");
return -1;
}
char buf[1024];
if (pid > 0)
{
signal(SIGCHLD,handler);
while (1)
{
printf(">");
fgets(buf,sizeof(buf),stdin);
write(fd,buf,strlen(buf)+1);
if (strncmp(buf,"quit",4) == 0)
{
kill(pid,SIGUSR1);
wait(NULL);
exit(0);
}
}
}else if (pid == 0)
{
while (1)
{
read(fd,buf,sizeof(buf));
printf("ser: %s",buf);
if (strncmp(buf,"quit",4) == 0)
{
kill(getppid(),SIGUSR1);
exit(0);
}
}
}
return 0;
}
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
void handler(int signo)
{
printf("father exit....\n");
exit(0);
}
//./client ip port
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("%s <ip> <port>\n",argv[0]);
return -1;
}
int fd = socket(AF_INET,SOCK_STREAM,0);
if (fd < 0)
{
printf("socket fail");
return -1;
}
struct sockaddr_in seraddr;
bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr(argv[1]);
seraddr.sin_port = htons(atoi(argv[2]));
if (bind(fd,(const struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)
{
perror("bind fail");
return -1;
}
if (listen(fd,5) < 0)
{
perror("listen fail");
return -1;
}
int connfd = 0;
#if 0
if ((connfd = accept(fd,NULL,NULL)) < 0)
{
perror("accept fail");
return -1;
}
#endif
//while (1)
{
struct sockaddr_in cliaddr;
bzero(&cliaddr,sizeof(cliaddr));
socklen_t len = sizeof(cliaddr);
if ((connfd = accept(fd,(struct sockaddr*)&cliaddr,&len)) < 0)
{
perror("accept fail");
return -1;
}
printf("-------client info------\n");
printf("ip : %s\n",inet_ntoa(cliaddr.sin_addr));
printf("port: %d\n",ntohs(cliaddr.sin_port));
printf("------------------------\n");
#if 1
pid_t pid = fork();
if (pid < 0)
{
perror("fork fail");
return -1;
}
char buf[1024];
if (pid > 0)
{
signal(SIGUSR1,handler);
while (1)
{
printf(">");
fgets(buf,sizeof(buf),stdin);
write(connfd,buf,strlen(buf)+1);
if (strncmp(buf,"quit",4) == 0)
{
kill(pid,SIGUSR1);
wait(NULL);
exit(0);
}
}
}else if (pid == 0)
{
while (1)
{
read(connfd,buf,sizeof(buf));
printf("cli: %s",buf);
if (strncmp(buf,"quit",4) == 0)
{
kill(getppid(),SIGUSR1);
exit(0);
}
}
}
#endif
}
return 0;
}
线程
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <pthread.h>
void *do_write(void *arg)
{
int fd = *(int *)arg;
char buf[1024];
while (1)
{
printf(">");
fgets(buf,sizeof(buf),stdin);
write(fd,buf,strlen(buf)+1);
if (strncmp(buf,"quit",4) == 0)
{
exit(0);
}
}
}
void *do_read(void *arg)
{
int fd = *(int *)arg;
char buf[1024] = {0};
while (1)
{
read(fd,buf,sizeof(buf));
printf("ser: %s",buf);
if (strncmp(buf,"quit",4) == 0)
{
exit(0);
}
}
}
//./client ip port
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("%s <ip> <port>\n",argv[0]);
return -1;
}
int fd = socket(AF_INET,SOCK_STREAM,0);
if (fd < 0)
{
printf("socket fail");
return -1;
}
struct sockaddr_in seraddr;
bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr(argv[1]);
seraddr.sin_port = htons(atoi(argv[2]));
if (connect(fd,(const struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)
{
perror("conncet fail");
return -1;
}
pthread_t tid[2];
int ret = 0;
if ((ret = pthread_create(&tid[0],NULL,do_read,&fd)) != 0)
{
errno = ret;
perror("pthread_create fail");
exit(0);
}
if ((ret = pthread_create(&tid[1],NULL,do_write,&fd)) != 0)
{
errno = ret;
perror("pthread_create fail");
exit(0);
}
printf("-----main----\n");
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
return 0;
}
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <pthread.h>
void *do_write(void *arg)
{
int fd = *(int *)arg;
char buf[1024];
while (1)
{
printf(">");
fgets(buf,sizeof(buf),stdin);
write(fd,buf,strlen(buf)+1);
if (strncmp(buf,"quit",4) == 0)
{
exit(0);
}
}
}
void *do_read(void *arg)
{
int fd = *(int *)arg;
char buf[1024] = {0};
while (1)
{
int ret = read(fd,buf,sizeof(buf));
printf("ret = %d cli: %s",ret,buf);
if (strncmp(buf,"quit",4) == 0)
{
exit(0);
}
}
}
//./client ip port
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("%s <ip> <port>\n",argv[0]);
return -1;
}
int fd = socket(AF_INET,SOCK_STREAM,0);
if (fd < 0)
{
printf("socket fail");
return -1;
}
struct sockaddr_in seraddr;
bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr(argv[1]);
seraddr.sin_port = htons(atoi(argv[2]));
if (bind(fd,(const struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)
{
perror("bind fail");
return -1;
}
if (listen(fd,5) < 0)
{
perror("listen fail");
return -1;
}
int connfd = 0;
#if 0
if ((connfd = accept(fd,NULL,NULL)) < 0)
{
perror("accept fail");
return -1;
}
#endif
//while (1)
{
struct sockaddr_in cliaddr;
bzero(&cliaddr,sizeof(cliaddr));
socklen_t len = sizeof(cliaddr);
if ((connfd = accept(fd,(struct sockaddr*)&cliaddr,&len)) < 0)
{
perror("accept fail");
return -1;
}
printf("-------client info------\n");
printf("ip : %s\n",inet_ntoa(cliaddr.sin_addr));
printf("port: %d\n",ntohs(cliaddr.sin_port));
printf("------------------------\n");
#if 1
pthread_t tid[2];
int ret = 0;
if ((ret = pthread_create(&tid[0],NULL,do_read,&connfd)) != 0)
{
errno = ret;
perror("pthread_create fail");
exit(0);
}
if ((ret = pthread_create(&tid[1],NULL,do_write,&connfd)) != 0)
{
errno = ret;
perror("pthread_create fail");
exit(0);
}
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
#endif
}
return 0;
}
练习:tcp传文件
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
typedef struct
{
int size;
char data[256];
}msg_t;
//./client port ip filename
int main(int argc, const char *argv[])
{
if (argc != 4)
{
printf("Usage: %s <port> <ip> <filename>\n",argv[0]);
return -1;
}
int fd = socket(AF_INET,SOCK_STREAM,0);
if (fd < 0)
{
perror("socket fail\n");
return -1;
}
struct sockaddr_in seraddr;
bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(atoi(argv[1]));
seraddr.sin_addr.s_addr = inet_addr(argv[2]);
printf("fd = %d\n",fd);
if (connect(fd,(struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)
{
perror("connect fail");
return -1;
}
//名字
//send(fd,argv[3],strlen(argv[3])+1,0);
send(fd,argv[3],strlen(argv[3]),0);
sleep(1);
//创建文件
int fd_s = open(argv[3],O_RDONLY);
if (fd_s < 0)
{
perror("open fail");
return -1;
}
char buf[1024];
while (1)
{
int ret = read(fd_s,buf,sizeof(buf));
if (ret == 0)
break;
send(fd,buf,ret,0);
}
sleep(1000);
close(fd_s);
close(fd);
return 0;
}
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
typedef struct
{
int size;
char data[256];
}msg_t;
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("Usage: %s <port> <ip>\n",argv[0]);
return -1;
}
//1.socket 创建通信一端
int fd = socket(AF_INET,SOCK_STREAM,0);
if (fd < 0)
{
perror("socket fail\n");
return -1;
}
struct sockaddr_in seraddr;
bzero(&seraddr,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(atoi(argv[1]));
seraddr.sin_addr.s_addr = inet_addr(argv[2]);
printf("fd = %d\n",fd);
//2.bind -- 绑定服务器端的地址信息
if (bind(fd,(const struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)
{
perror("connect fail");
return -1;
}
printf("connect success!\n");
//3.listen -- 设置监听
if (listen(fd,5) < 0)
{
perror("listen fail");
return -1;
}
//4.accept
int connfd = accept(fd,NULL,NULL);
if (connfd < 0)
{
perror("accept fail");
return -1;
}
printf("----client --- connectted\n");
char buf[1024];
msg_t msg;
int ret = recv(connfd,&msg,sizeof(msg),0);
printf("ret = %d %s\n",ret,msg.data);
int fd_d = open(msg.data,O_WRONLY|O_CREAT|O_TRUNC,0666);
if (fd_d < 0)
{
perror("open fail");
return -1;
}
while (1)
{
ret = recv(connfd,&msg,sizeof(msg),0);
printf("ret = %d %s\n",msg.size,msg.data);
if (msg.size == 0)
break;
write(fd_d,msg.data,msg.size);
}
close(fd);
close(connfd);
return 0;
}