语法总结1(学习笔记1)
语法总结1(学习笔记1)所以tcp包头至少20字节 第三步(A->B),客户端A收到服务器端发送来的SYN ACK报文后,向服务器发送确认ACK(ack =k 1)报文,客户端和服务器端进入ESTABLISHED状态,完成tcp连接。tcp是向应用层提供可靠的面向连接的数据流传输服务 第一步(A->B),主机A(客户端)向主机B(服务器端)发送一个包含SYN(同步,syn=j)标志的tcp报文,并进入SYN_SEND状态,等待服务器确认。 第二步(B->A),主机B收到客户端A的SYN报文后,将返回一个SYN ACK(ack=j 1 syn=k)的报文,此时服务器端进入SYN_RECV状态。
Linux网络编程网络体系结构OSI和TCP/IP模型OSI模型
OSI应用层表示层会话层传输层网络层数据链路层物理层
TCP/IP模型
TCP/IP模型网络接口层
tcp是向应用层提供可靠的面向连接的数据流传输服务
- 三次握手协议
第一步(A->B),主机A(客户端)向主机B(服务器端)发送一个包含SYN(同步,syn=j)标志的tcp报文,并进入SYN_SEND状态,等待服务器确认。
第二步(B->A),主机B收到客户端A的SYN报文后,将返回一个SYN ACK(ack=j 1 syn=k)的报文,此时服务器端进入SYN_RECV状态。
第三步(A->B),客户端A收到服务器端发送来的SYN ACK报文后,向服务器发送确认ACK(ack =k 1)报文,客户端和服务器端进入ESTABLISHED状态,完成tcp连接。
- tcp数据包头
源端口、目的端口:都是2个字节。
顺序号:4字节,标识发送数据包的顺序。
确认号:4字节,希望收到的下一个数据包的序列号。
下面的是一些标志位,SYN用于建立连接、FIN用于关闭连接、ACK标识数据包是否有确认星系、RST用于复位主机,拒绝非法连接请求。
所以tcp包头至少20字节
UDP
udp即用户数据报协议,是一种面向无连接的不可靠传输协议
- udp包头
因此udp包头是8个字节
协议的选择
- 网络状况良好的时候建议使用udp减少网络负荷,网络状况不好时使用tcp防止数据丢失
- 对数据可靠性要求很高的的应用使用tcp。
- 实时性高的应用建议使用udp,提升速度。
- 流式套接字(SOCK_STREAM)流式套接字提供可靠的、面向连接的通信流。tcp通讯使用的就是流式套接字。
- 数据报套接字(SOCK_DGRAM)数据报套接字提供了一种面向无连接,不可靠的服务。udp通信就是使用的该套接字。
- 原始套接字(SOCK_RAW)原始套接字允许对底层协议(如ip或icmp)进行直接访问,它功能强大但使用较为不便,主要用于一些协议的开发。
ip地址用于标识网络中的一台主机
- IP地址格式转换
ip地址有十进制点分制和32位二进制形式,网络传输中以第二种形式存储。
ipv4的地址转换函数有,inet_aton()、inet_addr()、inet_ntoa()等
inet_addr(“192.168.0.1”); //将点分十进制转换为二进制
字节序
指计算机中多字节整型数据的存储方式,分为大端和小端
小端:数据高位存放在计算机地址的高位,低位存放在计算机地址的低位
大端:数据高位存放在计算机地址的低位,高位存放在计算机地址的高位
在网络中的字节序都是大端模式
字节序转换函数
- 长整型数据转换 htonl()、ntohl(),h表示主机,n表示网络
- 短整型数据转换 htons()、ntohs()。
客户端
- 创建通讯套接字 socket(int family int type int protocol)
family:协议族,IPV4协议(AP_INET)
tpye:套接字类型,这里是流式套接字(SOCK_STREAM)
protocol:0,表示默认匹配相关的协议
返回值:成功(非负套接字描述符)失败(-1) - 请求连接服务器端 connect(int sockfd struct sockaddr *addr int addrlen)
sockfd:创建好的套接字文件描述符
addr:一个结构体,包含服务器端ip地址、端口号、协议
addlen:地址长度
返回值:成功(0) 失败(-1) - 发送消息 send(int sockfd const void *buf int len int flags)
sockfd:创建好的套接字文件描述符
buf:发送数据的缓冲区
len:发送数据长度
flags:一般为0,表示阻塞
返回值:成功(实际接收到的字节数) 失败(-1) - 接收消息 recv(int sockfd const void *buf int len int flags)
同发送 - 关闭 close(int sockfd)
服务器端
- 创建套接字 socket(int family int type int protocol)
- 绑定服务器端的ip地址和端口号 bind(int sockfd struct sockaddr *addr int addrlen)
sockfd:创建好的套接字文件描述符
addr:一个结构体,包含自己的ip地址、端口号、协议
addlen:地址长度
返回值:成功(0) 失败(-1) - 将套接字设置为监听状态 listen(int sockfd int backlog)
sockfd:设置监听的套接字
backlog:同时最大连接数,默认为5 - 等待客户端连接,连接成功后生成一个通信套接字 accept(int sockfd struct sockaddr *addr socklen_t *addrlen)
sockfd:监听的套接字描述符
addr:存放客户端的ip等信息
addrlen:长度
返回值:成功(通信套接字描述符) 失败(-1) - 发送消息 send(int sockfd const void *buf int len int flags)
同客户端 - 接收消息 recv(int sockfd const void *buf int len int flags)
同发送 - 关闭 close(int sockfd)
示例代码
- 客户端
#include "header.h"
void *recive(void *p){
sleep(1);
int sockfd = *(int *)p;
char buff[SIZE] = {0};
while(1){
recv(sockfd buff sizeof(buff) 0);
printf("%s\n" buff);
memset(buff 0 sizeof(buff));
}
}
int main(int argc const char *argv[]){
if(argc != 3){
printf("argc error");
return -1;
}
pthread_t pid;
int lfd = socket(AF_INET SOCK_STREAM 0);
if(lfd == -1){
perror("lfd fail..\n");
return -1;
}
struct sockaddr_in toaddr;
toaddr.sin_family = AF_INET;
toaddr.sin_port = htons(atoi(argv[2]));
toaddr.sin_addr.s_addr = inet_addr(argv[1]);
if(-1 == connect(lfd (struct sockaddr *)&toaddr sizeof(toaddr))){
perror("connect fail:");
return 1;
}
printf("connect succ \n");
pthread_create(&pid NULL recive &lfd);
char buff[SIZE];
while(1){
scanf("%s" buff);
send(lfd buff strlen(buff) 1 0);
}
return 0;
}
- 头文件
#ifndef _HEADER_H_#define _HEADER_H_
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define SIZE 1024
#endif
- 服务器端
#include "header.h"
void *send(void *q){
int sockfd = *(int *)q;
while(1){
char buff[SIZE];
printf("server send : ");
scanf("%s" buff);
send(sockfd buff sizeof(buff) 0);
}
}
int main(int argc const char *argv[]){
if(argc != 3){
printf("argc error");
return -1;
}
int lfd = socket(AF_INET SOCK_STREAM 0);
pthread_t pid;
if(lfd == -1){
printf("sock fail\n");
return 1;
}
struct sockaddr_in myaddr youraddr;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(atoi(argv[2]));
myaddr.sin_addr.s_addr = inet_addr("192.168.168.140");
if(-1 == bind(lfd (struct sockaddr *)&myaddr sizeof(myaddr))){
perror("bind fail");
return 1;
}
listen(lfd 10);
int your_len = sizeof(youraddr);
int newfd = accept(lfd (struct sockaddr *)&youraddr &your_len);
pthread_create(&pid NULL send &newfd);
char buff[SIZE]={0};
while(1){
recv(newfd buff sizeof(buff) 0);
printf("server recive messg : %s\n" buff);
memset(buff 0 sizeof(buff));
}
return 0;
}
UDP编程
- 客户端
#include "header.h"
int main(int argc const char *argv[]){
int sockfs = socket(AF_INET SOCK_DGRAM 0);
if(-1 == sockfs){
printf("socket fail\n");
return -1;
}
struct sockaddr_in toaddr;
toaddr.sin_family = AF_INET;
toaddr.sin_port = htons(4444);
toaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
bind(sockfs (struct sockaddr *)&toaddr sizeof(toaddr));
char buff[SIZE];
while(1){
scanf("%s" buff);
sendto(sockfs buff strlen(buff) 1 0 (struct sockaddr*)&toaddr sizeof(toaddr));
memset(buff 0 sizeof(buff));
int len = sizeof(toaddr);
recvfrom(sockfs buff sizeof(buff) 0 (struct sockaddr*)&toaddr &len);
printf("port =%d ip=%s\n" ntohs(toaddr.sin_port) (char *)inet_ntoa(toaddr.sin_addr.s_addr));
printf("%s" buff);
}
return 0;
}
- 服务器端
#include "header.h"int main(int argc const char *argv[]){
int sockfr = socket(AF_INET SOCK_DGRAM 0);
if(-1 == sockfr)
return -1;
struct sockaddr_in myaddr youaddr;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(4444);
myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
bind(sockfr (struct sockaddr *)&myaddr sizeof(myaddr));
char buff[100];
int len = sizeof(youaddr);
while(1){
recvfrom(sockfr buff sizeof(buff) 0 (struct sockaddr*)&youaddr &len);
printf("%s" buff);
printf("port =%d ip=%s\n" ntohs(youaddr.sin_port) (char *)inet_ntoa(youaddr.sin_addr.s_addr));
memset(buff 0 sizeof(buff));
// strcpy(buff "recved!");
scanf("%s" buff);
sendto(sockfr buff strlen(buff) 1 0 (struct sockaddr*)&youaddr sizeof(youaddr));
memset(buff 0 sizeof(buff));
}
return 0;
}
并发服务器多线程并发
#include "header.h"#define N 256
void func(int num){
while((waitpid(0 NULL 0))>0);
return;
}
int main(int argc const char *argv[]){
if(argc != 3){
perror("argc fail");
return -1;
}
int fld newfd;
char buff[50];
struct sockaddr_in myaddr = {0};
struct sockaddr_in youaddr = {0};
int you_len = sizeof(youaddr);
pid_t pid;
if((fld = socket(AF_INET SOCK_STREAM 0)) < 0){
perror("socket fail");
return -1;
}
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(atoi(argv[2]));
myaddr.sin_addr.s_addr = inet_addr(argv[1]);
if(-1 == bind(fld (struct sockaddr *)&myaddr sizeof(myaddr))){
perror("bind fail");
return -1;
}
printf("bind succ \n");
listen(fld 10);
while(1){
if((newfd = accept(fld (struct sockaddr *)&youaddr &you_len)) < 0 ){
perror("accept fail");
return -1;
}
printf("accept succ\n");
pid = fork();
if(-1 == pid){
perror("fork fail");
return -1;
}else if(pid > 0){
signal(SIGCHLD func);
continue;
}else{
printf("star recv\n");
while(1){
recv(newfd buff sizeof(buff) 0);
printf("recv port = %d ip = %s string : %s\n" ntohs(youaddr.sin_port) (char *)inet_ntoa(youaddr.sin_addr.s_addr) buff);
strcpy(buff "server recved!");
send(newfd buff strlen(buff) 1 0);
memset(buff 0 sizeof(buff));
}
}
}
return 0;
}
多进程并发
void func(int num){
while(waitpid(0 NULL WNOHANG)>0);
return;
}
int main(int argc char *argv[]){
if(argc!=3){
perror("para err:./server Ip port\n");
return -1;
}
int lfd = socket(AF_INET SOCK_STREAM 0);
if(-1 == lfd){
perror("lfd create fail\n");
return -1;
}
struct sockaddr_in myaddr;
struct sockaddr_in youaddr;
pid_t pid;
int newfd;
int len = sizeof(youaddr);
bzero(&myaddr sizeof(myaddr));
bzero(&youaddr sizeof(youaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(atoi(argv[2]));
myaddr.sin_addr.s_addr = inet_addr(argv[1]);
if(-1== bind(lfd (struct sockaddr*)&myaddr sizeof(myaddr))){
perror("bind fail...\n");
return -1;
}
listen(lfd 10);
while(1){
newfd = accept(lfd (struct sockaddr*)&youaddr &len);//就是阻塞
if(-1 == newfd){
perror("newfd create fail\n");
return -1;
}
pid = fork();
if(-1 == pid){
perror("fork fail...\n");
return -1;
}
else if(pid>0){
close(newfd);
signal(SIGCHLD func);
continue;
}else if(0 == pid){
char buff[100]={0};
while(1){
recv(newfd buff sizeof(buff) 0);
printf("buff=%s ip=%s\n" buff (char *)inet_ntoa(youaddr.sin_addr.s_addr));
//send、recv
int i = 0;
while(buff[i]!=0)
{
buff[i] = toupper(buff[i]);//将小写字母转换成大小字母
i ;
}
send(newfd buff strlen(buff) 1 0);
}
close(lfd);
}
}
}
IO多路复用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXLINE 50
typedef struct sockaddr SA;
int main(int argc char **argv){
int listenfd connfd maxfd i nbyte;
struct sockaddr_in myaddr;//用来保存服务器自己的IP地址和端口号
char buf[MAXLINE];
fd_set global_rdfs current_rdfs;//global全局的 current临时的
//创建socket
//sockfd = socket();
if ((listenfd = socket(AF_INET SOCK_STREAM 0)) < 0)
{
perror("fail to socket");
exit(-1);
}
printf("listenfd is %d\n" listenfd);
bzero(&myaddr sizeof(myaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //htonl(INADDR_ANY);
//inet_addr("127.0.0.1");
myaddr.sin_port = htons(55555); /* port number */
//绑定IP和端口号
if(bind(listenfd (SA *)&myaddr sizeof(myaddr)) < 0)
{
perror("fail to bind");
exit(-1);
}
//设置同时允许客户端连接的上限
listen(listenfd 5);
FD_ZERO(&global_rdfs); //清空文件描述符集
FD_SET(listenfd &global_rdfs); //listenfd就是socket函数的返回值 将listenfd global_rdfs中
maxfd = listenfd; //maxfd保存当前最大的文件描述符
while(1)
{
current_rdfs = global_rdfs; //global 实际保存 current_rdfs //临时
//在select函数调用之前,current_rdfs 保存的是 read阻塞相关的文件描述符
if (select(maxfd 1 ¤t_rdfs NULL NULL 0) < 0) //阻塞
//让当前select函数解除阻塞的两个可能是
//1.新的客户端连接服务器accept解除阻塞
//2.recv数据解除阻塞
//在select函数调用结束之后,也就是解除了阻塞(accept 和 recv),current_rdfs保存的是引起解除阻塞文件描述符
{
perror("fail to select");
exit(-1);
}
else //解除阻塞了 对解除阻塞的原因进行条件判断
{
for (i=3; i<=maxfd; i )//循环遍历文件描述符
{
if (FD_ISSET(i ¤t_rdfs)) //判断文件描述符是否在current_rdfs集中
{
if (i == listenfd) // accept函数引起解除阻塞,说明有新的客户端连接服务器
{
connfd = accept(listenfd NULL NULL);//分析出原因后,立刻调用accept函数,让客户端连接
printf("connfd is %d\n" connfd);
FD_SET(connfd &global_rdfs);//将新的文件描述符newsockfd添加到global_rdfsh中
maxfd = maxfd > connfd ? maxfd : connfd; //当前最大maxfd为newsockfd
}
else //客户端发送来消息,引起的解除阻塞 recv()
{
if((nbyte = recv(i buf sizeof(buf) 0)) <= 0) //说明客户端断开连接或异常关闭,容错处理
{
close(i);
FD_CLR(i &global_rdfs); //将该文件描述符从global_rdfs中删除
}
else //接收到了数据
{
printf("recv from client is %s\n" buf);//打印接收到的数据
send(i buf strlen(buf) 1 0);//将接收到的数据回传给客户端
}
}
}
} // end for
}
}
return 0;
}