一 、网络基础知识
(一)、网络采取分层结构:
1.每一层实现不同的功能,对数据进行透明传输
2.网络每一层向上层提供服务,同时享受下一层的服务
模型图:

(二)、各层典型协议:
1.网络物理接口层:
MAC地址:48位地址,全球唯一,网络身份的标识
ARP/RARP:
ARP:IP地址------->MAC地址
RARP:MAC地址---------->IP地址
PPP协议:拨号协议(GPS/3G/4G)
2.网络层协议
IP (IPv4 · IPv6) (Internet Protocol) 网络之间互连的协议
ICMP (Internet Control Message Protocol )Internet 控制报文协议。它是TCP/IP 协议族的一个子协议,用于在IP 主机、路由器之间传递控制消息。举例:ping命令
3.传输层
TCP (Transmission Control Protocol )传输控制协议提供可靠的面向连接的服务,传输数据前须先建立连接,结束后释放。可靠的全双工信道。可靠、有序、无丢失、不重复。
UDP (User Datagram Protocol )用户数据报协议发送数据前无需建立连接,不使用拥塞控制,不保证可靠交付,最大努力交付。
4.应用层
FTP (File Transfer Protocol )文件传输协议<端口号21>减少或消除不同操作系统下处理文件的不兼容性。
HTTP (Hypertext Transfer Protocol )超文本传输协议 <端口号 80>, 面向事务的应用层协议。
SSH (Secure Shell )安全外壳协议
嵌入式相关:
NTP网络时钟管理
SNMP:简单网络管理(网络设备集中管理)
RTP/RTSP:传输音视屏协议(安防监控)
(三)、网络的分装和拆包

二、网络编程预备知识
1.socket
是一个编程接口
是一种特殊的文件描述符(可以使用read,write等文件io函数),一种网络资源
socket类型
流式套接字(SOCK_STREAM):唯一对应TCP
提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
数据报套接字(SOCK_DGRAM):唯一对应UDP
提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
原始套接字(SOCK_RAW):对应多种协议,使用ping可以直接穿透传输层
可以对较低层次协议如IP、ICMP直接访问。

2.IP地址
Internet中的主机要与别的机器通信必须具有一个IP地址
IP地址为32位****(IPv4)或者128位(IPv6)
每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由
特殊IP:
局域网:192.XXX.XXX.XXX 10.XXX.XXX.XXX
广播:XXX.XXX.XXX.255 255.255.255.255(全网广播)
组播:224.XXX.XXX.XXX ~239.XXX.XXX.XXX
3.端口号
ip地址确认哪台机器,端口号确认哪个进程或者线程(数据传输给哪个任务)
保留端口:FTP 21, SSH 22 ,HTTP 80, HTTPS 469
1025-5000端口不建议使用
可以使用:5000~65535
TCP 和UDP端口是相互独立的
4.字节序
1.cpu访问多字节数据时存在字节序的问题(string字符串则不存在这个问题)
大端字节序:数据高位对应cpu内存地址高位
小端字节序:数据低位对应vpu内存地址高位

2.字节序转换相关函数
(1)主机字节序到网络字节序
u_long htonl (u_long hostlong);
u_short htons (u_short short);
(2)网络字节序到主机字节序
u_long ntohl (u_long hostlong);
u_short ntohs (u_short short);
3.IP地址转换
(1)将strptr所指的字符串转换成32位的网络字节序二进制值
int_addr_t inet_addr(const char *strptr);
(2)将IPV4/IPV6的地址转换成binary格式
int inet_pton(int af, const char *src, void *dst);
逆过程:inet_ntop()
参数:
af:AF_INET(IPV4)AF_INET6(IPV6)
src:IPV4点分十进制
dst:转换后的结果
三、TCP编程

3.1 socket函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数:
1.domain:
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_UNIX Local communication unix(7)
AF_LOCAL Synonym for AF_UNIX
AF_NETLINK Kernel user interface device netlink(7)
AF_PACKET Low-level packet interface packet(7)
2.type
SOCK_STREAM:流式套接字,唯一对应TCP
SOCK_DGRAM:数据报套接字,唯一对应UDP
SOCK_RAM:原始套接字
3.protocol:一般填0,使用原始套接字需要填充
返回值:
RETURN VALUE
On success, a file descriptor for the new socket is returned. On er‐
ror, -1 is returned, and errno is set appropriately.
3.2 bind函数
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd:通过socket拿到的文件描述符
addr:结构体变量的地址
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
具体要我们填充的数据结构体
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 */
}
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
addrlen:地址长度
3.3 listen()函数
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
参数:
backlog:表示三次握手阶段最多允许几个客户端连接,一般设置为5
return:
-1表示失败,0表示成功
3.4 accept()函数
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
addr:存放保存连接客户端的地址
addlen:存放保存连接客户端的地址长度
返回值:
On success, these system calls return a nonnegative integer that is a file descriptor for the accepted socket. On error, -1 is returned, errno is set appropriately, and addrlen is left unchanged.
3.5 connect()函数
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
与bind相类似
client.c
1 #include "net.h"
2
3 int main(){
4 int fd=-1;
5 fd=socket(AF_INET,SOCK_STREAM,0);
6 if(fd<0){
7 perror("socket");
8 exit(1);
9 }
10
11 struct sockaddr_in sin;
12 sin.sin_family=AF_INET;
13 sin.sin_port=htons(SERVE_POET);
14 #if 0
15 sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
16 #else
17 if((inet_pton(AF_INET,SERVE_ADDR,&sin.sin_addr.s_addr)!=1)){
18 perror("inet_pton");
19 exit(1);
20 }
21 #endif
22
23 if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
24 perror("connect");
25 exit(1);
26 }
27 char buf[BUFSIZE];
28 while(1){
29 bzero(buf,strlen(buf));
30 printf(">");
31 if((fgets(buf,32,stdin)==NULL)){
32 perror("fgets");
33 continue;
34 }
35 if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
35 if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
36 break;
37 }
38 write(fd,buf,strlen(buf));
39 }
40
41 close(fd);
42
43
44 }
server.c
1 #include "net.h"
2 int main(){
3
4 /*1.创建socket套接字(文件描述符)*/
5 int fd=-1;
6 if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
7 perror("socket");
8 exit(-1);
9 }
10 /*2.绑定bind*/
11 /*2.1填充结构体数据*/
12 struct sockaddr_in sin;
13 bzero(&sin,sizeof(sin));
14 sin.sin_family=AF_INET;
15 sin.sin_port=htons(SERVE_POET);
16 #if 1
17 // sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
18 /*优化1:能绑定任意服务器*/
19 sin.sin_addr.s_addr=htonl(INADDR_ANY);
20 #else
21
22 if((inet_pton(AF_INET,SERVE_ADDR,(void*)&sin.sin_addr.s_addr)!=1)){
23 perror("inet_pton");
24 exit(1);
25 }
26 #endif
27 /*2.2 bind*/
28 if(( bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
29 perror("bind");
30 exit(1);
31
32 }
33 /*3.利用listen函数将主动套接字变为被动套结字*/
34 if(listen(fd,BACKLOG)<0){
35 perror("listen");
36 exit(1);
37 }
38 /*4.阻塞等待客户端连接*/
39 #if 0
40 int newfd=-1;
41 newfd=accept(fd,NULL,NULL);
42 if(newfd<0){
43 perror("accept");
44 exit(1);
45 }
46 #else
47 //优化2:获取连接客户端信息
48 int newfd=-1;
49 struct sockaddr_in cin;
50 socklen_t addrlen=sizeof(cin);
51 char clientaddr[16];
52 newfd=accept(fd,(struct sockaddr*)&cin,&addrlen);//无法实现多个客户端连接,因此需要多线程来解决
53 if(newfd<0){
54 perror("accept");
55 exit(1);
56 }
57 /*网络字序转换*/
58 if(!(inet_ntop(AF_INET,(void*)&cin.sin_addr.s_addr,clientaddr,sizeof(cin)))){
59 perror("inet_ntop");
60 exit(1);
61 }
62 printf("client addr:%s port:%d\n",clientaddr,ntohs(cin.sin_port));
63
64
65 #endif
66
67 /*5.读写*/
68 char buf[BUFSIZE];
69 int ret=-1;
70 while(1){
71 bzero(buf,sizeof(buf));
72 do{
73 ret=read(newfd,buf,BUFSIZE-1);
74
75 }while(ret<0 && EINTR==errno);
76
77 if(ret<0){
78 perror("read");
79 exit(1);
80 }
81
82 printf("receive %s",buf);
83
84 if(!strncasecmp(STR_QUIT,buf,strlen(STR_QUIT))){
85 printf("Client is exiting\n!");
86 break;
87 }
88 }
89 close(newfd);
90 close(fd);
91 return 0;
92 }
net.h
1 #ifndef _NET_H_
2 #define _NET_H_
3 #include<stdio.h>
4 #include<stdlib.h>
5 #include<unistd.h>
6 #include<string.h>
7 #include<strings.h>
8 #include <sys/types.h> /* See NOTES */
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <netinet/ip.h> /* superset of previous */
12 #include <arpa/inet.h>
13 #include<errno.h>
14
15
16
17 #define SERVE_POET 5001
18 #define SERVE_ADDR "127.0.0.1"
19 #define BACKLOG 5
20 #define BUFSIZE 128
21 #define STR_QUIT "quit"
22
23
24
25
26
27 #endif
28
3.6 TCP并发服务器多线程/多进程
多线程服务器
server.c
1 #include<pthread.h>
2 #include "net.h"
3 void cli_data_handler(void* arg);
4 int main(){
5
6 /*1.创建socket套接字(文件描述符)*/
7 int fd=-1;
8 if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
9 perror("socket");
10 exit(-1);
11 }
12 /*2.绑定bind*/
13 /*2.1填充结构体数据*/
14 struct sockaddr_in sin;
15 bzero(&sin,sizeof(sin));
16 sin.sin_family=AF_INET;
17 sin.sin_port=htons(SERVE_POET);
18 #if 1
19 // sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
20 /*优化1:能绑定任意服务器*/
21 sin.sin_addr.s_addr=htonl(INADDR_ANY);
22 #else
23
24 if((inet_pton(AF_INET,SERVE_ADDR,(void*)&sin.sin_addr.s_addr)!=1)){
25 perror("inet_pton");
26 exit(1);
27 }
28 #endif
29 /*2.2 bind*/
30 if(( bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
31 perror("bind");
32 exit(1);
33
34 }
35 /*3.利用listen函数将主动套接字变为被动套结字*/
36 if(listen(fd,BACKLOG)<0){
37 perror("listen");
38 exit(1);
39 }
40
41 int newfd=-1;
42 while(1){
43 pthread_t pid;
44 struct sockaddr_in cin;
45 socklen_t addrlen=sizeof(cin);
46 char cli_addr[16];
47 newfd=accept(fd,(struct sockaddr*)&cin,&addrlen);
48 if(newfd<0){
49 perror("accept");
50 exit(1);
51 }
52
53 if(!inet_ntop(AF_INET,(void*)&cin.sin_addr.s_addr,cli_addr,addrlen)){
54 perror("inet_ntop");
55 exit(1);
56 }
57 printf("client addr=%s, port=%d\n",cli_addr,(int)ntohs(cin.sin_port));
58 pthread_create(&pid,NULL,(void*)cli_data_handler,(void*)&newfd);
59
60 }
61 close(fd);
62 return 0;
63 }
64
65 void cli_data_handler(void* arg){
66 int newfd=*(int*)arg;
67 printf("newfd=%d\n",newfd);
68 int ret=-1;
69 char buf[BUFSIZ];
70 while(1){
71 do{
72 bzero(buf,BUFSIZE);
73 ret=read(newfd,buf,BUFSIZE);
74
75 }while(ret<0 && EINTR==errno);
76 if(ret<0){
77 perror("read");
78 exit(1);
79 }
80 printf("%s",buf);
81
82 if(!strncasecmp(STR_QUIT,buf,strlen(STR_QUIT))){
83 printf("client is exiting\n");
84 break;
85 }
86
87 }
88 close(newfd);
89 }
client.c
1 #include "net.h"
2 void usage(char* s){
3 printf("\n%s server_ip,server_port",s);
4 printf("\n server_ip:seriver ip address");
5 printf("\n server_port:server port(>5000)\n\n");
6
7 }
8 int main(int argc,void* argv[]){
9 int fd=-1;
10 if(argc<3){
11 usage(argv[0]);
12 exit(1);
13 }
14 short port;
15 fd=socket(AF_INET,SOCK_STREAM,0);
16 if(fd<0){
17 perror("socket");
18 exit(1);
19 }
20
21 port=atoi(argv[2]);
22 if(port<5000){
23 usage(argv[0]);
24 exit(1);
25 }
26
27 struct sockaddr_in sin;
28 sin.sin_family=AF_INET;
29 sin.sin_port=htons(port);
30 #if 0
31 sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
32 #else
33 if((inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)!=1)){
34 perror("inet_pton");
35 exit(1);
36 }
37 #endif
38
39 if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
40 perror("connect");
41 exit(1);
42 }
43 char buf[BUFSIZE];
44 while(1){
45 bzero(buf,strlen(buf));
46 printf(">");
47 if((fgets(buf,32,stdin)==NULL)){
48 perror("fgets");
49 continue;
50 }
51 if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
52 break;
53 }
54 write(fd,buf,strlen(buf));
55 }
56
57 close(fd);
58
59
60 }
TCP多进程
server.c
1 #include<signal.h>
2 #include"net.h"
3 #include <sys/wait.h>
4 void waitact(int sig){
5 if(sig==SIGCHLD){
6 waitpid(-1,NULL,WNOHANG);
7 }
8
9 }
10 int main(){
11 int fd=-1;
12 fd=socket(AF_INET,SOCK_STREAM,0);
13 if(fd<0){
14 perror("socket");
15 exit(0);
16 }
17 //信号机制。回收进程
18 sigaction(SIGCHLD,(void*)&waitact,NULL);
19
20 struct sockaddr_in sin;
21 sin.sin_port=htons(SERVE_POET);
22 sin.sin_family=AF_INET;
23 #if 0
24 if((inet_pton(AF_INET,SERVE_ADDR,&sin.sin_addr.s_addr))<0){
25 perror("inet_pton");
26 exit(0);
27 }
28 #else
29 sin.sin_addr.s_addr=htonl(INADDR_ANY);
30 #endif
31 if((bind(fd,(struct sockaddr*)&sin,sizeof(sin)))<0){
32 perror("bind");
33 exit(1);
34 }
35
36 if((listen(fd,BACKLOG))<0){
37 perror("lsiten");
38 exit(1);
39 }
40 int newfd=-1;
41 struct sockaddr_in cin;
42 socklen_t addrlen=sizeof(cin);
43 char clientaddr[16];
44 while(1){
45 bzero(clientaddr,sizeof(clientaddr));
46 newfd=accept(fd,(struct sockaddr*)&cin.sin_addr.s_addr,&addrlen);
47 if(newfd<0){
48 perror("accept");
49 exit(1);
50 }
51 pid_t pid;
52 pid=fork();
53 if(pid<0){
54 perror("fork");
55 exit(1);
56 }else if(pid==0){
57 close(fd);
58 if((inet_ntop(AF_INET,(struct sockaddr*)&sin.sin_addr.s_addr,clientaddr,addrlen))<0){
59 perror("inet_ntop");
60 exit(1);
61 }
62 printf("client addr=%s,port=%d\n",clientaddr,ntohs(sin.sin_port));
63 char buf[BUFSIZE];
64 int ret=-1;
65 while(1){
66 do{
67 bzero(buf,BUFSIZE);
68 ret=read(newfd,buf,BUFSIZE);
69 }while(ret<0 && EINTR==errno);
70 if(ret<0){
71 perror("read");
72 exit(1);
73 }
74 printf("receive data=%s",buf);
75 if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
76 printf("client is exiting!\n");
77 break;
78 }
79 }
80 close(newfd);
81 }else{//主线程
82 close(newfd);
83 }
84 }
85 close(fd);
86
87
88
89
90 }
四、UDP编程

TCP拓展API:
send()/write():
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
参数:flag:
为0.则和write()函数一样
MSG_DONTWAIT:不阻塞
MSG_OOB:(out-of-band data)外带函数,tcp类型
recv()/read()
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
参数: falgs
为0则和read一样
MSG_DONTWAIT:不阻塞
MSG_OOB:(out-of-band data)外带函数,tcp类型
MSG_PEEK:读取数据都是从头开始,而不移动读取位置
server.c
1 #include "net.h"
2
3 int main(){
4 int fd=-1;
5 fd=socket(AF_INET,SOCK_DGRAM,0);
6 if(fd<0){
7 perror("socket");
8 exit(0);
9
10 }
11
12 int b_reuse = 1;
13 setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
14
15
16 struct sockaddr_in sin;
17 sin.sin_family=AF_INET;
18 sin.sin_port=htons(SERVE_POET);
19 sin.sin_addr.s_addr=htonl(INADDR_ANY);
20
21 if(bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0){
22 perror("bind");
23 exit(0);
24 }
25
26 struct sockaddr_in cin;
27 char buf[BUFSIZE];
28 socklen_t addrlen=sizeof(cin);
29 char cli_addr[16];
30 printf("UDPserver is starting ......\n");
31 while(1){
32 bzero(buf,BUFSIZE);
33 if(recvfrom(fd,buf,BUFSIZE-1,0,(struct sockaddr *)&cin,&addrlen)<0){
34 perror("recvfrom");
35 continue;
36 }
37
38 if(inet_ntop(AF_INET,(struct sockaddr*)&cin.sin_addr.s_addr,cli_addr,addrlen)<0){
39 perror("inet_ntop");
40 continue;
41 }
42 printf("client(%s:%d):receive data=%s\n",cli_addr,ntohs(cin.sin_port),buf);
43
44 if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
45 printf("client(%s:%d) is exit!\n",cli_addr,ntohs(cin.sin_port));
46 }
47
48 }
49 close(fd);
50 return 0;
51
52 }
client .c
1 #include "net.h"
2
3 void usage(char* s){
4 printf("\n%s:server_ip,server_port:\n",s);
5 printf("server_ip:server ipaddress\n");
6 printf("server_port:sever port(>5000)\n");
7
8 }
9 int main(int argc,void* argv[]){
10 int fd=-1;
11 fd=socket(AF_INET,SOCK_DGRAM,0);
12 if(fd<0){
13 perror("socket");
14 exit(1);
15 }
16 if(argc<3){
17 usage(argv[0]);
18 exit(0);
19 }
20 int port;
21 port=atoi(argv[2]);
22 if(port<5000){
23 usage(argv[0]);
24 exit(1);
25 }
26
27 char buf[BUFSIZE];
28 struct sockaddr_in sin;
29 sin.sin_port=htons(port);
30 sin.sin_family=AF_INET;
31 if(inet_pton(AF_INET,argv[1],(void*)&sin.sin_addr.s_addr)<0){
32 perror("inet_pton");
33 exit(1);
34 }
35
36 while(1){
37 bzero(buf,sizeof(buf));
38 printf(">");
39 if(fgets(buf,BUFSIZE,stdin)==NULL){
40 perror("fgets");
41 continue;
42 }
43
44 if(sendto(fd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0){
45 perror("sendto");
46 continue;
47 }
48 if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
49 printf("client is exited!\n");
50 break;
51 }
52
53 }
54 close(fd);
55
56
57
58 return 0;
59 }
五、IO多路复用
linux下四种常见I/O模型:
阻塞I/O :最常用
非阻塞I/O:需要轮询
I/O多路复用:允许对多个I/O控制
信号驱动I/O:异步通信模型
5.1多路复用(select函数使用,解决阻塞的问题)
步骤:
1.fd_se集合,将关心的文件描述符加入到fd_set集合中
2.select函数阻塞监控
3.1个或者多个文件描述符有数据exit
4.判断哪个文件描述符有数据
5.依次对数据进行处理
相关函数介绍
1、 fd_set
void FD_CLR(int fd, fd_set *set);//将fd从集合中清除
int FD_ISSET(int fd, fd_set *set);//判断fd是否在集合中
void FD_SET(int fd, fd_set *set);//将fd加入到集合中
void FD_ZERO(fd_set *set);//集合初始化
2.select
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds:maxfd+1
readfds:读集合
writerfds:写集合,一般为NULL
expectfds:异常处理集合(带外数据),一般为NULL
timerout:阻塞超时时间
notes:select函数前后,集合性质发生了变化,select是将关心的fd加入集合,select是有数据的fd保留了下来,因此需要用FD_ISSET判断进行相应的操作。
client.c
1 #include"net.h"
2 void usage(char*s){
3 printf("\n%s: server_addr,server_port(>5000)\n",s);
4 }
5 int main(int argc,void *argv[]){
6 int fd=-1;
7 fd=socket(AF_INET,SOCK_STREAM,0);
8 if(fd<0){
9 perror("socket");
10 exit(1);
11 }
12 if(argc<3){
13 usage(argv[0]);
14 exit(1);
15 }
16 int port=atoi(argv[2]);
17 if(port<5000){
18 usage(argv[0]);
19 exit(1);
20 }
21
22 struct sockaddr_in sin;
23 sin.sin_family=AF_INET;
24 sin.sin_port=htons(port);
25 if(inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)<0){
26 perror("inet_pton");
27 exit(1);
28 }
29
30 if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0){
31 perror("connect");
32 exit(1);
33 }
34
35 fd_set rset;
36 int maxfd=-1;
37 struct timeval timeout;
38 timeout.tv_sec=5;
39 timeout.tv_usec=0;
40 int ret=-1;
41 char buf[BUFSIZE];
42 while(1){
43 bzero(buf,BUFSIZE);
44 FD_ZERO(&rset);
45 FD_SET(0,&rset);
46 FD_SET(fd,&rset);
47 maxfd=fd;
48 select(maxfd+1,&rset,NULL,NULL,&timeout);
49
50 if(FD_ISSET(0,&rset)){//从标准输入当中读取字符
51 do{
52 ret=read(0,buf,BUFSIZE-1);
53 }while(ret<0 && EINTR==errno);
54
55 if(ret<0){
56 perror("read");
57 continue;
58 }
59 if(!ret)continue;//没有字符输入
60
61 write(fd,buf,BUFSIZE);
62 if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
63 printf("client is exited!\n");
64 break;
65 }
66 }
67 if(FD_ISSET(fd,&rset)){
68 bzero(buf,BUFSIZE);
69 do{
70 ret=read(fd,buf,BUFSIZE);
71
72 }while(ret<0 && EINTR==errno);
73 if(ret<0){
74 perror("read");
75 continue;
76 }
77 if(!ret)break;//服务器关闭
78 printf("sever data:%s\n",buf);
79 if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
80 printf("server is closed\n");
81 break;
82 }
83
84 }
85
86 }
87 close(fd);
88
89
90 }
六、TCP IP协议
1.以太网头

2.IP头

3.tcp头

4.tcp握手过程分析

7网络编程扩展
1.网络信息检索
域名解析相关函数
SYNOPSIS
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);//域名解析
#include <sys/socket.h> /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr,
socklen_t len, int type);
void herror(const char *s);//报错提示
const char *hstrerror(int err);
/* System V/POSIX extension */
struct hostent *gethostent(void);
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility */
RETURN VALUE
The gethostbyname() and gethostbyaddr() functions return the hostent structure or a null pointer if an error occurs.
On error, the h_errno variable holds an error number. When non-NULL, the return value may point at static data, see the notes below.
1 #include<netdb.h>
2 #include "net.h"
3 void usage(char* s){
4 printf("\n%s server_name,server_port",s);
5 printf("\n server_name:seriver ip address");
6 printf("\n server_port:server port(>5000)\n\n");
7
8 }
9 int main(int argc,void* argv[]){
10 int fd=-1;
11 if(argc<3){
12 usage(argv[0]);
13 exit(1);
14 }
15 short port;
16 fd=socket(AF_INET,SOCK_STREAM,0);
17 if(fd<0){
18 perror("socket");
19 exit(1);
20 }
21
22 port=atoi(argv[2]);
23 if(port<5000){
24 usage(argv[0]);
25 exit(1);
26 }
27 //域名处理
28 struct hostent *hs;
29 if((hs=gethostbyname(argv[1]))==NULL){
30 herror("gethostname");
31 exit(1);
32 }
33 struct sockaddr_in sin;
34 sin.sin_family=AF_INET;
35 sin.sin_port=htons(port);
36 #if 1
37 sin.sin_addr.s_addr=*(uint32_t *)hs->h_addr;//域名转换
38 #else
39 if((inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)!=1)){
40 perror("inet_pton");
41 exit(1);
42 }
43 #endif
44
45 if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
46 perror("connect");
47 exit(1);
48 }
49 char buf[BUFSIZE];
50 while(1){
51 bzero(buf,strlen(buf));
52 printf(">");
53 if((fgets(buf,32,stdin)==NULL)){
54 perror("fgets");
55 continue;
56 }
57 if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
58 break;
59 }
60 write(fd,buf,strlen(buf));
61 }
62
63 close(fd);
64
65
66 }
2 网络属性设置
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
level指定控制套接字的层次.可以取三种值:
1)SOL_SOCKET:通用套接字选项. (应用层)
2)IPPROTO_IP:IP选项. (网络层)
3)IPPROTO_TCP:TCP选项. (传输层)
optname指定控制的方式(选项的名称),我们下面详细解释

举例:
允许地址快速重用
int reuse=1;
setsockopt(fd,SOL_SOCKET,SO_RESUSERADDR,&reuse,sizeof(int))
3 网络超时优化
方法一:
设置socket的属性 SO_RCVTIMEO
参考代码如下
struct timeval tv;
tv.tv_sec = 5; // 设置5秒时间
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(tv)); // 设置接收超时
recv() / recvfrom() // 从socket读取数据
方法二:
用select检测socket是否’ready’
参考代码如下
struct fd_set rdfs;
struct timeval tv = {5 , 0}; // 设置5秒时间
FD_ZERO(&rdfs);
FD_SET(sockfd, &rdfs);
if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0) // socket就绪
{
recv() / recvfrom() // 从socket读取数据
}
方法三:
设置定时器(timer), 捕捉SIGALRM信号
参考代码如下
void handler(int signo) { return; }
struct sigaction act;
sigaction(SIGALRM, NULL, &act);
act.sa_handler = handler;
act.sa_flags &= ~SA_RESTART;
sigaction(SIGALRM, &act, NULL);
alarm(5);
if (recv(,,,) < 0) ……
心跳检测:
4 广播发送
sender:UDP客户端
receiver:UDP服务端
广播发送:只需要修改客户端即可
加上如下代码:
int so_br=1;
setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&so_br,sizeof(int));
5 多播
组播接收
1.创建用户数据报套接字
2.加入多播组
3.绑定本机IP地址和端口
4.绑定的端口必须和发送方指定的端口相同
5.等待接收数据
加入多播组
struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(“235.10.10.3”);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
struct ip_mreq
{
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
};
7 UINX域套接字(本地进程通信)
1.进程间通信方式:
管道,消息队列,共享内存,信号,unix域套接字
易用性:消息队列>unix套接字>管道>共享内存
效率性:共享内存>unix套接字>管道>消息队列
2.异步通信
信号
2.同步和互斥
信号量
unix域套接字
和普通tcp和udp套接字流程是一样的,唯一区别就是套接字类型不同,适用于本地通信
socket(AF_UNIX/AF_LOACL,SOCK_STRAM,0);
bind地址结构的需要改变:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX /
char sun_path[108]; / Pathname */
};
还需要利用access()函数判断文件是否存在?

196

被折叠的 条评论
为什么被折叠?



