本文提供相关源码,请放心食用,详见网页侧边栏或底部,有疑问请评论或 Issue
前言
进程通信的概念最初来源于单机系统,由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进程之间既互不干扰又协调一致工作,操作系统为进程通信提供了相应设施,如:管道(pipe
)、消息(message
)、共享存储区(shared memory
)和信号量(semaphore
)等。
但是这都仅限于用在本机进程之间通信。网络间进程通信要解决的是不同主机进程间的相互通信问题为此,引入了套接字
。
一、套接字
套接字(socket)
,在Linux环境下,用于表示进程间通信的特殊文件类型(伪文件)。我们知道,在TCP/IP协议中:
IP地址:在网络环境中唯一标识一台主机
端口号:在主机中唯一标识一个进程
IP地址+端口号:在网络环境中唯一标识一个进程
这个在网络中被唯一标识的进程,被称为socket
。在网络通信中,套接字一定是成对出现 的。这两个socket组成的socket pair
就唯一标识一个连接 。
在TCP/IP模型中,套接字位于应用层和传输层之间 :
套接字一般分为以下三种类型:
流式套接字 (SOCK_STREAM)
提供可靠的、面向连接的通信流,通过它发送的数据保证原有顺序不变。它使用的是TCP协议 。
数据报套接字 (SOCK_DGRAM)
定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠、无差错。它使用的是UDP协议 。
原始套接字 (SOCK_RAW)
允许对底层的协议直接访问,主要用于新的网络协议的开发。它功能强大,但使用复杂。
二、预备知识
2.1 网络字节序
我们知道,计算机在内存中存放数据有小端字节序(Little-Endian)
和大端字节序(Big-Endian)
两种方法。举个简单的例子,对于整型数据0x12345678
,有以下两种存放形式:
TCP/IP协议规定了,网络数据应采用大端字节序 。
因此如果主机是大端字节序,网络传输时不要做转换;如果主机是小端字节序,网络传输时就需要做转换。
为了使网络程序具有可移植性,编写socket程序可以使用以下函数来进行网络字节序和主机字节序的转换 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #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) ;
2.2 IP地址转换函数
以IPv4为例,我们平时使用的ip地址像192.168.1.1
这种形式,属于点分十进制字符串 ,但是计算机内部进行传输时则需要将其转换为32位的二进制 ,与此同时还要考虑主机字节序和网络字节序的转换问题。
为了简化我们进行socket编程时对ip地址的操作,提供了以下两个IP地址转换函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <arpa/inet.h> int inet_pton (int af, const char *src, void *dst) ;const char *inet_ntop (int af, const void *src, char *dst, socklen_t size) ;
注:早期的ip转换函数已被废弃(不支持IPV6) ,被废弃的函数包括:
1 2 3 4 5 6 7 8 9 10 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton (const char *cp, struct in_addr *inp) ;in_addr_t inet_addr (const char *cp) ;char *inet_ntoa (struct in_addr in) ;
2.3 struct sockaddr
struct sockaddr
是一个非常古老的结构体,诞生早于IPv4协议。后来随着网络的发展,不得不对该结构体进行细分,划分出了以下三种:
struct sockaddr_in
struct sockaddr_in6
struct sockaddr_un
为了不改变那些使用struct sockaddr
作为参数的函数(例如bind、accpet、connect),struct sockaddr
退化成了泛型 (和 void * 作用相似)。
因此,我们编程时需要根据具体的需求,来确定要定义哪种被细分 的struct sockaddr
结构体,然后在函数中将其强制转换 为struct sockaddr
类型。
注:struct sockaddr现在已经被废弃了,不能使用!
比如在bind()
函数中:
1 2 3 4 5 struct sockaddr_in serv_addr ;bind(lfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr));
2.4 struct sockaddr_in
因为目前IP地址仍然主要使用IPv4协议,因此struct sockaddr_in
结构体是我们当前最为常用的struct sockaddr
结构体,其定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr ; }; struct in_addr { uint32_t s_addr; };
其实struct sockaddr_in
内部不止这些属性,但我们只需关心这些。
我们发现sin_addr
是struct in_addr
类型,而1struct in_addr
类型内部只有一个s_addr
变量。
2.5 TCP与UDP运行流程
三、相关函数
3.1 socket()
3.1.1 概要
1 2 3 4 #include <sys/types.h> #include <sys/socket.h> int socket (int domain, int type, int protocol) ;
成功返回指向新创建的socket的文件描述符 。失败返回-1,错误信息存放于errno。
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程序可以像读文件一样用read/write在网络上收发数据。
3.1.2 参数
1)domain :地址族协议,常用的有以下几个参数:
参数
含义
AF_INET
最常用,使用TCP或UDP来进行传输,使用IPv4的地址
AF_INET6
与上面类似,但使用IPv6的地址
AF_UNIX
本地协议,使用在本地Unix和Linux系统上
2)type :协议类型,常用的有以下几个参数:
参数
含义
SOCK_STREAM
该协议是按照顺序的、可靠的、数据完整的基于字节流的连接,使用TCP进行传输
SOCK_DGRAM
该协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP进行传输
SOCK_SEQPACKET
该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须完整接收包才能进行读取
SOCK_RAW
socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议
SOCK_RDM
很少使用,提供给数据链路层使用,不保证数据包的顺序
3)protocol :传0 即可,表示使用每种协议的默认协议(即SOCK_STREAM使用TCP,SOCK_DGRAM使用UDP)。
3.2 bind()
1 2 3 4 5 6 7 8 9 #include <sys/types.h> #include <sys/socket.h> int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号就可以向服务器发起连接,因此服务器需要调用bind()绑定一个固定的网络地址和端口号 。
bind()的作用是将参数sockfd和addr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听addr所描述的地址和端口号。
前面说过,struct sockaddr *
相当于一个泛型,因此其具体实现的长度各不相同,所以需要第三个参数指定结构体的长度 。
成功返回0。 失败返回-1,错误信息存放于errno。
3.3 listen()
1 2 3 4 5 6 7 8 #include <sys/types.h> #include <sys/socket.h> int listen (int sockfd, int backlog) ;
成功返回0。失败返回-1,错误信息存放于errno。
使用cat /proc/sys/net/ipv4/tcp_max_syn_backlog
可以查看系统默认的backlog。
如果客户端连接数达到backlog,新的客户端连接请求会被忽略 。
3.4 accpet()
1 2 3 4 5 6 7 8 9 #include <sys/types.h> #include <sys/socket.h> int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen) ;
三次握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时没有收到客户端的请求,会一直处于阻塞状态 。
成功返回一个新的 socket文件描述符,用于和客户端通信。失败返回-1,错误信息存放于errno。
3.5 connect()
1 2 3 4 5 6 7 8 9 include <sys/types.h> #include <sys/socket.h> int connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;
客户端需要调用connect()连接服务器,connect()和bind()的参数一致,区别在于bind()的参数是自己的地址 ,而connect()的参数是对方的地址 。
成功返回0。失败返回-1,错误信息存放于errno。
3.6 recv() 和 send()
这两个函数是socket tcp编程下的接收函数(recv)和发送函数(send)。
1 2 3 4 #include <unistd.h> ssize_t read (int fd, void *buf, size_t count) ;ssize_t write (int fd, const void *buf, size_t count) ;
1 2 3 4 5 #include <sys/types.h> #include <sys/socket.h> ssize_t recv (int sockfd, void *buf, size_t len, int flags) ;ssize_t send (int sockfd, const void *buf, size_t len, int flags) ;
recv和send函数和标准的read和write函数很类似,唯一的差别是第四个参数。
第四个参数是一个整型的标志位,我们可以以位或 的形式包含系统允许的一系列标志,从而设置在这一次I/O的特性。
通常情况下被设置为0 ,实现普通read和write的功能。函数返回读取的字节个数。
3.7 recvfrom() 和 sendto()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <sys/types.h> #include <sys/socket.h> ssize_t recvfrom (int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) ;ssize_t sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) ;
recvfrom和sendto函数和recv和send函数比较相似,只是前者是用于UDP协议下的,后者用于TCP协议下的。区别仅仅是多了最后两个参数 。函数返回读取的字节个数。
3.8 getsockname()和getpeername()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <sys/socket.h> int getsockname (int sockfd, struct sockaddr *localaddr, socklen_t *addrlen) ;int getpeername (int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen) ;
对于这两个函数,如果函数调用成功,则返回0,如果调用出错,则返回-1。使用这两个函数,我们可以通过套接字描述符来获取本地的地址信息和对端的地址信息。
对于TCP连接的服务端:
在accept之前 调用getsocketname()
,会获取内核赋予该连接的服务端 的IP地址和本地端口号
在accept之后 调用getsocketname()
,会获取客户端真正连接的服务端 的IP地址和本地端口号
在accept之后 调用getpeername()
,会获取当前连接的客户端 的IP地址和端口号
对于TCP连接的客户端:
在connect之后 调用getpeername()
,会获取当前连接的服务端 的IP地址和端口号
四、小写转大写程序
功能需求:客户端发送字符串,服务器端将其解析为大写后传回给客户端。
首先给出第四章 和第五章 代码的头文件head.h
(放置的位置根据具体程序为准 ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <arpa/inet.h> #include <errno.h> #include <time.h> #include <sys/wait.h> #include <pthread.h> #include <semaphore.h> #define SERV_PORT 2017 #define BACKLOG 10 #define QUEUE_SIZE 5 #define MSG_FILENAME 1 #define MSG_CONTENT 2 #define MSG_ACK 3 #define MSG_DONE 4 #define MSG_ERROR 5 #define MSG_EXIT 6 #define MSG_SIZE BUFSIZ + 2*sizeof(int) struct msg { int type; int len; char data[]; }; void p_error (char *msg) { perror(msg); exit (EXIT_FAILURE); }
4.1 实现单客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include "../head.h" int main (void ) { int lfd, cfd, res, i; char buf[BUFSIZ], clie_ip[BUFSIZ]; struct sockaddr_in serv_addr , clie_addr ; socklen_t clie_addr_len; ssize_t n; lfd = socket(AF_INET, SOCK_STREAM, 0 ); if (lfd == -1 ) perror("socket error.." ); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); res = bind(lfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr)); if (res == -1 ) perror("bind error.." ); res = listen(lfd, BACKLOG); if (res == -1 ) perror("listen error.." ); clie_addr_len = sizeof (clie_addr); cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len); printf ("client IP: %s, client port: %d\n" , inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ip, sizeof (clie_ip)), ntohs(clie_addr.sin_port)); while (1 ) { n = recv(cfd, buf, sizeof (buf), 0 ); printf ("Receive msg: %s" ,buf); if (strcmp ("exit\n" , buf) == 0 ) break ; for (i = 0 ; i < n; i++) buf[i] = toupper (buf[i]); printf ("Send msg: %s" ,buf); send(cfd, buf, strlen (buf), 0 ); memset (buf, 0 , sizeof (buf)); } close(lfd); close(cfd); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include "../head.h" int main (int argc, char *argv[]) { int cfd, res; struct sockaddr_in serv_addr ; char buf[BUFSIZ]; cfd = socket(AF_INET, SOCK_STREAM, 0 ); if (cfd == -1 ) perror("socket error.." ); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1 ], &serv_addr.sin_addr.s_addr); res = connect(cfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr)); if (res == -1 ) perror("connect error..\n" ); while (1 ) { printf ("Send msg : " ); fgets(buf, sizeof (buf), stdin ); send(cfd, buf, strlen (buf), 0 ); if (strcmp ("exit\n" , buf) == 0 ) break ; memset (buf, 0 , sizeof (buf)); n = recv(cfd, buf, sizeof (buf), 0 ); printf ("Receive msg: %s" , buf); } close(cfd); return 0 ; }
4.2 新增对多客户端的支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include "../head.h" void sig_child (int signo) { pid_t pid; int stat; while ((pid = waitpid(-1 , &stat,WNOHANG)) > 0 ) printf ("child %d terminated\n" , pid); return ; } int main (void ) { int lfd, cfd, i; struct sockaddr_in serv_addr , clie_addr ; struct sockaddr_in listen_addr , peer_addr ; char listen_ip[BUFSIZ], peer_ip[BUFSIZ]; char buf[BUFSIZ]; ssize_t n; time_t t; socklen_t clie_len, listen_len, peer_len; if ((lfd = socket(AF_INET, SOCK_STREAM, 0 )) == -1 ) p_error("socket error" ); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); if ((i = bind(lfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr))) == -1 ) p_error("bind error" ); if ((i = listen(lfd, BACKLOG)) == -1 ) p_error("listen error" ); listen_len = sizeof (listen_addr); getsockname(lfd, (struct sockaddr *)&listen_addr, &listen_len); printf ("server listen address = %s:%d\n" , inet_ntop(AF_INET, &listen_addr.sin_addr.s_addr, listen_ip, sizeof (listen_ip)), ntohs(listen_addr.sin_port)); printf ("Server service start success!\n\n" ); while (1 ) { clie_len = sizeof (clie_addr); if ((cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_len))== -1 ) p_error("accept error" ); if ((i = fork()) < 0 ) p_error("fork error" ); else if ( i == 0 ) { close(lfd); peer_len = sizeof (peer_addr); getpeername(cfd, (struct sockaddr *)&peer_addr, &peer_len); printf ("%s:%d login now!\n\n" , inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, peer_ip, sizeof (peer_ip)), ntohs(peer_addr.sin_port)); while (1 ) { if ((n = recv(cfd, &buf, sizeof (buf), 0 )) <= 0 ) continue ; time(&t); printf ("%s" , ctime(&t)); printf ("[%s:%d]: %s\n" , peer_ip, ntohs(peer_addr.sin_port), buf); if (strcmp ("exit\n" , buf) == 0 ) break ; memset (&buf, 0 , sizeof (buf)); } printf ("%s:%d exit now!\n" , peer_ip, ntohs(peer_addr.sin_port)); close(cfd); exit (0 ); } signal(SIGCHLD, sig_child); } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include "../head.h" #define BACKLOG 10 int main (int len, char *argv[]) { int cfd, i; struct sockaddr_in serv_addr ; struct sockaddr_in connect_addr , peer_addr ; char connect_ip[BUFSIZ], peer_ip[BUFSIZ]; char buf[BUFSIZ]; time_t t; socklen_t connect_len, peer_len; cfd = socket(AF_INET, SOCK_STREAM, 0 ); if (cfd == -1 ) { perror("socket error" ); exit (1 ); } memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1 ], &serv_addr.sin_addr.s_addr); i = connect(cfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr)); if (i == -1 ) { if (errno == ECONNREFUSED) perror("connection refuse" ); else if (errno == EHOSTUNREACH) perror("no route to host" ); else if (errno == ETIMEDOUT) perror("connection time out" ); exit (1 ); } else printf ("Connect server succes!\n\n" ); connect_len = sizeof (connect_addr); getsockname(cfd, (struct sockaddr *)&connect_addr, &connect_len); printf ("connect server address = %s:%d\n" , inet_ntop(AF_INET, &connect_addr.sin_addr.s_addr, connect_ip, sizeof (connect_ip)), ntohs(connect_addr.sin_port)); peer_len = sizeof (peer_addr); getpeername(cfd, (struct sockaddr *)&peer_addr, &peer_len); printf ("connect peer address = %s:%d\n" , inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, peer_ip, sizeof (peer_ip)), ntohs(peer_addr.sin_port)); printf ("================\n" ); while (1 ) { time(&t); printf ("%s" , ctime(&t)); printf ("Send msg: " ); fgets(buf, sizeof (buf), stdin ); send(cfd, &buf, strlen (buf), 0 ); if (strcmp ("exit\n" , buf) == 0 ) break ; } close(cfd); return 0 ; }
4.3 基于 I/O 复用模式重写
关于I/O复用模式的基本知识,这里不再赘述,可以参考《Linux IO模型》 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 #include "../head.h" struct sockaddr_in peer_addr ;char peer_ip[BUFSIZ];socklen_t peer_len;int parser_msg (int cfd) { char buf[BUFSIZ]; time_t t; int i; if ((recv(cfd, &buf, sizeof (buf), 0 )) <= 0 ) p_error("recv" ); time(&t); printf ("%s" , ctime(&t)); printf ("Recvice: [%s:%d]: %s" , peer_ip, ntohs(peer_addr.sin_port), buf); if (strcmp ("exit\n" , buf) == 0 ) { printf ("%s:%d exit now!\n" , peer_ip, ntohs(peer_addr.sin_port)); return 1 ; } for (i = 0 ; i < strlen (buf); i++) buf[i] = toupper (buf[i]); printf ("Send: [%s:%d]: %s\n" , peer_ip, ntohs(peer_addr.sin_port), buf); send(cfd, &buf, sizeof (buf), 0 ); return 0 ; } int main (void ) { int i, opt, maxi, maxfd; int lfd, cfd; int nready, client[FD_SETSIZE]; struct sockaddr_in serv_addr , clie_addr ; struct sockaddr_in listen_addr ; char listen_ip[BUFSIZ]; struct timeval tv ; fd_set rset, allset; socklen_t clie_len, listen_len, peer_len; if ((lfd = socket(AF_INET, SOCK_STREAM, 0 )) == -1 ) p_error("socket error" ); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); opt = 1 ; setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)); if (bind(lfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr)) == -1 ) p_error("bind error" ); if (listen(lfd, BACKLOG) == -1 ) perror("listen error" ); listen_len = sizeof (listen_addr); getsockname(lfd, (struct sockaddr *)&listen_addr, &listen_len); printf ("server listen address = %s:%d\n" , inet_ntop(AF_INET, &listen_addr.sin_addr.s_addr, listen_ip, sizeof (listen_ip)), ntohs(listen_addr.sin_port)); printf ("Server service start success!\n\n" ); maxfd = lfd; maxi = -1 ; memset (client, -1 , FD_SETSIZE); FD_ZERO(&allset); FD_SET(lfd, &allset); while (1 ) { rset = allset; tv.tv_sec = 3 ; nready = select(maxfd+1 , &rset, NULL , NULL , &tv); if (nready < 0 ) p_error("select error" ); else if (nready == 0 ) { printf ("timeout..\n" ); continue ; } if (FD_ISSET(lfd, &rset)) { clie_len = sizeof (clie_addr); if ((cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_len)) == -1 ) p_error("accept error" ); peer_len = sizeof (peer_addr); getpeername(cfd, (struct sockaddr *)&peer_addr, &peer_len); printf ("%s:%d login now!\n\n" , inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, peer_ip, sizeof (peer_ip)), ntohs(peer_addr.sin_port)); for (i=0 ; i<FD_SETSIZE; i++) if (client[i] < 0 ) { client[i] = cfd; if (i > maxi) maxi = i; break ; } if (i == FD_SETSIZE) { puts ("too many clients\n" ); exit (EXIT_FAILURE); } FD_SET(cfd, &allset); if (cfd > maxfd) maxfd = cfd; if (--nready <= 0 ) continue ; } for (i=0 ; i<=maxi; i++) { if ((cfd = client[i]) == -1 ) continue ; if (FD_ISSET(cfd, &rset)) { if (parser_msg(cfd) == 1 ) { close(cfd); FD_CLR(cfd, &allset); client[i] = -1 ; } } if (--nready <= 0 ) break ; } } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include "../head.h" int main (int len, char *argv[]) { int cfd; struct sockaddr_in serv_addr ; struct sockaddr_in connect_addr , peer_addr ; char connect_ip[BUFSIZ], peer_ip[BUFSIZ]; char buf[BUFSIZ]; time_t t; socklen_t connect_len, peer_len; if ((cfd = socket(AF_INET, SOCK_STREAM, 0 )) == -1 ) p_error("socket error" ); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1 ], &serv_addr.sin_addr.s_addr); if (connect(cfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr)) == -1 ) switch (errno) { case ECONNREFUSED: p_error("connection refuse" ); case EHOSTUNREACH: p_error("no route to host" ); case ETIMEDOUT: p_error("connection time out" ); default : p_error("unknown" ); } else printf ("Connect server succes!\n\n" ); connect_len = sizeof (connect_addr); getsockname(cfd, (struct sockaddr *)&connect_addr, &connect_len); printf ("connect server address = %s:%d\n" , inet_ntop(AF_INET, &connect_addr.sin_addr.s_addr, connect_ip, sizeof (connect_ip)), ntohs(connect_addr.sin_port)); peer_len = sizeof (peer_addr); getpeername(cfd, (struct sockaddr *)&peer_addr, &peer_len); printf ("connect peer address = %s:%d\n" , inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, peer_ip, sizeof (peer_ip)), ntohs(peer_addr.sin_port)); printf ("================\n" ); while (1 ) { time(&t); printf ("%s Send msg: " , ctime(&t)); fgets(buf, sizeof (buf), stdin ); send(cfd, &buf, strlen (buf), 0 ); if (strcmp ("exit\n" , buf) == 0 ) break ; recv(cfd, &buf, sizeof (buf), 0 ); time(&t); printf ("\n%s Receive msg: %s\n" , ctime(&t), buf); } close(cfd); return 0 ; }
4.4 使用UDP协议重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include "head.h" int main (void ) { int lfd, i; char buf[BUFSIZ], clie_ip[BUFSIZ]; struct sockaddr_in serv_addr , clie_addr ; socklen_t clie_addr_len; ssize_t n; if ((lfd = socket(AF_INET, SOCK_DGRAM, 0 )) == -1 ) p_error("socket error.." ); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); if ((i = bind(lfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr))) == -1 ) p_error("bind error.." ); while (1 ) { clie_addr_len = sizeof (clie_addr); if ((n = recvfrom(lfd, buf, sizeof (buf), 0 , (struct sockaddr *)&clie_addr, &clie_addr_len)) == -1 ) p_error("recvfrom error.." ); printf ("recvfrom ip : %s, port : %d\n" , inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ip, sizeof (clie_ip)), ntohs(clie_addr.sin_port)); printf ("Receive msg: %s" ,buf); if (strcmp ("exit\n" , buf) == 0 ) break ; for (i = 0 ; i < n; i++) buf[i] = toupper (buf[i]); printf ("Send msg: %s" ,buf); if ((n = sendto(lfd, buf, strlen (buf), 0 , (struct sockaddr *)&clie_addr, sizeof (clie_addr))) == -1 ) p_error("sendto error.." ); memset (buf, 0 , sizeof (buf)); } close(lfd); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include "head.h" int main (int argc, char *argv[]) { int cfd; struct sockaddr_in serv_addr ; ssize_t n; char buf[BUFSIZ]; if ((cfd = socket(AF_INET, SOCK_DGRAM, 0 )) == -1 ) p_error("socket error.." ); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1 ], &serv_addr.sin_addr.s_addr); while (fgets(buf, sizeof (buf), stdin ) != NULL ) { printf ("Send msg : %s" , buf); if ((n = sendto(cfd, buf, strlen (buf), 0 , (struct sockaddr *)&serv_addr, sizeof (serv_addr))) == -1 ) p_error("sendto error.." ); if (strcmp ("exit\n" , buf) == 0 ) break ; if ((n = recvfrom(cfd, buf, sizeof (buf), 0 , NULL , 0 )) == -1 ) p_error("recvfrom error.." ); printf ("Receive msg: %s" , buf); memset (buf, 0 , sizeof (buf)); } close(cfd); return 0 ; }
五、文件传输程序
功能需求:客户端发送源文件名和目的文件名,服务器端进行文件传输。
5.1 实现多客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 #include "../head.h" void sig_child (int signo) { pid_t pid; int stat; while ((pid = waitpid(-1 , &stat,WNOHANG)) > 0 ) printf ("child %d terminated\n" , pid); return ; } int main (void ) { int lfd, cfd, i; struct sockaddr_in serv_addr , clie_addr ; struct sockaddr_in listen_addr , peer_addr ; struct msg *send_msg , *rec_msg ; char listen_ip[BUFSIZ], peer_ip[BUFSIZ]; char buf[BUFSIZ], target_filename[BUFSIZ]; time_t start_time, end_time; FILE *fp; socklen_t clie_len, listen_len, peer_len; if ((lfd = socket(AF_INET, SOCK_STREAM, 0 )) == -1 ) p_error("socket error" ); send_msg = (struct msg*)malloc (MSG_SIZE); rec_msg = (struct msg*)malloc (MSG_SIZE); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); if ((i = bind(lfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr))) == -1 ) p_error("bind error" ); if ((i = listen(lfd, BACKLOG)) == -1 ) p_error("listen error" ); listen_len = sizeof (listen_addr); getsockname(lfd, (struct sockaddr *)&listen_addr, &listen_len); printf ("server listen address = %s:%d\n" , inet_ntop(AF_INET, &listen_addr.sin_addr.s_addr, listen_ip, sizeof (listen_ip)), ntohs(listen_addr.sin_port)); printf ("Server service start success!\n\n" ); while (1 ) { clie_len = sizeof (clie_addr); if ((cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_len)) == -1 ) p_error("accept error" ); if ((i = fork()) < 0 ) perror("fork error" ); else if ( i == 0 ) { close(lfd); peer_len = sizeof (peer_addr); getpeername(cfd, (struct sockaddr *)&peer_addr, &peer_len); printf ("%s:%d login now!\n\n" , inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, peer_ip, sizeof (peer_ip)), ntohs(peer_addr.sin_port)); while (1 ){ memset (send_msg, 0 , sizeof (struct msg)); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); switch (rec_msg->type) { case MSG_FILENAME: memset (&target_filename, 0 , sizeof (target_filename)); memcpy (&target_filename, rec_msg->data, rec_msg->len); fp = fopen(target_filename, "w+" ); if (!fp) { perror("fopen error" ); send_msg->type = MSG_ERROR; strcpy (buf, "open file error" ); send_msg->len = strlen (buf); memcpy (send_msg->data, &buf, send_msg->len); send(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 ); } else { time(&start_time); send_msg->type = MSG_ACK; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); } break ; case MSG_CONTENT: if (!fp) { send_msg->type = MSG_ERROR; strcpy (buf, "file not open yet" ); send_msg->len = strlen (buf); memcpy (send_msg->data, &buf, send_msg->len); send(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 ); } else { fwrite(rec_msg->data, sizeof (char ), rec_msg->len, fp); fflush(fp); send_msg->type = MSG_ACK; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); } break ; case MSG_DONE: time(&end_time); printf ("[INFO] %s:%d complete file send\n" , peer_ip, ntohs(peer_addr.sin_port)); printf ("[INFO] Save in : %s\n" ,target_filename); printf ("[INFO] Consume : %lfs\n" , difftime(end_time, start_time)); if (fp) { fclose(fp); fp = NULL ; } send_msg->type = MSG_ACK; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); break ; case MSG_EXIT: printf ("[INFO] client will exit\n" ); send_msg->type = MSG_EXIT; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); break ; default : break ; } } close(cfd); exit (0 ); } signal(SIGCHLD, sig_child); } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 #include "../head.h" int main (int argc, char *argv[]) { FILE *fp; int cfd; struct sockaddr_in serv_addr ; struct sockaddr_in connect_addr , peer_addr ; struct msg *send_msg , *rec_msg ; char connect_ip[BUFSIZ], peer_ip[BUFSIZ]; char buf[BUFSIZ], source_filename[BUFSIZ], target_filename[BUFSIZ]; ssize_t n; time_t start_time, end_time; socklen_t connect_len, peer_len; if ((cfd = socket(AF_INET, SOCK_STREAM, 0 )) == -1 ) p_error("socket error" ); send_msg = (struct msg*)malloc (MSG_SIZE); rec_msg = (struct msg*)malloc (MSG_SIZE); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1 ], &serv_addr.sin_addr.s_addr); if ((connect(cfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr))) == -1 ) { if (errno == ECONNREFUSED) p_error("connection refuse" ); else if (errno == EHOSTUNREACH) p_error("no route to host" ); else if (errno == ETIMEDOUT) p_error("connection time out" ); } else printf ("Connect server succes!\n\n" ); connect_len = sizeof (connect_addr); getsockname(cfd, (struct sockaddr *)&connect_addr, &connect_len); printf ("connect server address = %s:%d\n" , inet_ntop(AF_INET, &connect_addr.sin_addr.s_addr, connect_ip, sizeof (connect_ip)), ntohs(connect_addr.sin_port)); peer_len = sizeof (peer_addr); getpeername(cfd, (struct sockaddr *)&peer_addr, &peer_len); printf ("connect peer address = %s:%d\n" , inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, peer_ip, sizeof (peer_ip)), ntohs(peer_addr.sin_port)); printf ("================\n" ); while (1 ) { printf ("please input source file name: " ); scanf ("%s" , source_filename); if (!(fp = fopen(source_filename, "r" ))) { perror("fopen" ); exit (1 ); } memset (send_msg, 0 , sizeof (struct msg)); printf ("please input target file name: " ); scanf ("%s" , target_filename); send_msg->type = MSG_FILENAME; send_msg->len = strlen (target_filename); memcpy (send_msg->data, &target_filename, send_msg->len); send(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 ); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); if (rec_msg->type == MSG_ERROR) { printf ("[ERROR] " ); puts (rec_msg->data); exit (1 ); } time(&start_time); memset (send_msg, 0 , sizeof (struct msg)); while ((n = fread(&buf, sizeof (char ), sizeof (buf), fp))) { send_msg->type = MSG_CONTENT; send_msg->len = n; memcpy (send_msg->data, &buf, send_msg->len); send(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 ); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); if (rec_msg->type == MSG_ERROR) { printf ("[ERROR] " ); puts (rec_msg->data); exit (1 ); } } memset (send_msg, 0 , sizeof (struct msg)); if (n > 0 ) { send_msg->type = MSG_ERROR; strcpy (buf, "send file content error" ); send_msg->len = strlen (buf); memcpy (send_msg->data, &buf, send_msg->len); send(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 ); } else { time(&end_time); printf ("[INFO] complete send file, consume %lfs.\n" , difftime(end_time, start_time)); if (fp) { fclose(fp); fp = NULL ; } send_msg->type = MSG_DONE; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); } loop: printf (">(input 'q' to quit, input 'c' to continue) " ); scanf ("%s" , buf); if (strcmp ("c" , buf) == 0 ) continue ; else if (strcmp ("q" , buf) == 0 ) { memset (send_msg, 0 , sizeof (struct msg)); send_msg->type = MSG_EXIT; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); if (rec_msg->type == MSG_ERROR) { printf ("[ERROR] " ); puts (rec_msg->data); exit (1 ); } else if (rec_msg->type == MSG_EXIT) { printf ("[INFO] you will exit..\n" ); close(cfd); break ; } } else goto loop; } return 0 ; }
5.2 使用多线程重写客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 #include "../head.h" void sig_child (int signo) { pid_t pid; int stat; while ((pid = waitpid(-1 , &stat,WNOHANG)) > 0 ) printf ("child %d terminated\n" , pid); return ; } int main (void ) { int lfd, cfd, i; struct sockaddr_in serv_addr , clie_addr ; struct sockaddr_in listen_addr , peer_addr ; struct msg *send_msg , *rec_msg ; char listen_ip[BUFSIZ], peer_ip[BUFSIZ]; char buf[BUFSIZ], target_filename[BUFSIZ]; time_t start_time, end_time; FILE *fp; socklen_t clie_len, listen_len, peer_len; if ((lfd = socket(AF_INET, SOCK_STREAM, 0 )) == -1 ) p_error("socket error" ); send_msg = (struct msg*)malloc (MSG_SIZE); rec_msg = (struct msg*)malloc (MSG_SIZE); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); if ( (i = bind(lfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr))) == -1 ) p_error("bind error" ); if ( (i = listen(lfd, BACKLOG)) == -1 ) p_error("listen error" ); listen_len = sizeof (listen_addr); getsockname(lfd, (struct sockaddr *)&listen_addr, &listen_len); printf ("server listen address = %s:%d\n" , inet_ntop(AF_INET, &listen_addr.sin_addr.s_addr, listen_ip, sizeof (listen_ip)), ntohs(listen_addr.sin_port)); printf ("Server service start success!\n\n" ); while (1 ) { clie_len = sizeof (clie_addr); if ( (cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_len)) == -1 ) p_error("accept error" ); if ((i = fork()) < 0 ) p_error("fork error" ); else if (i == 0 ) { close(lfd); peer_len = sizeof (peer_addr); getpeername(cfd, (struct sockaddr *)&peer_addr, &peer_len); printf ("%s:%d login now!\n\n" , inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, peer_ip, sizeof (peer_ip)), ntohs(peer_addr.sin_port)); while (1 ){ memset (send_msg, 0 , sizeof (struct msg)); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); if (rec_msg->type == MSG_FILENAME) { memset (&target_filename, 0 , sizeof (target_filename)); memcpy (&target_filename, rec_msg->data, rec_msg->len); fp = fopen(target_filename, "w+" ); if (!fp) { p_error("fopen error" ); send_msg->type = MSG_ERROR; strcpy (buf, "open file error" ); send_msg->len = strlen (buf); memcpy (send_msg->data, &buf, send_msg->len); send(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 ); } else { time(&start_time); send_msg->type = MSG_ACK; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); } } else if (rec_msg->type == MSG_CONTENT) { if (!fp) { send_msg->type = MSG_ERROR; strcpy (buf, "file not open yet" ); send_msg->len = strlen (buf); memcpy (send_msg->data, &buf, send_msg->len); send(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 ); } else { fwrite(rec_msg->data, sizeof (char ), rec_msg->len, fp); fflush(fp); send_msg->type = MSG_ACK; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); } } else if (rec_msg->type == MSG_DONE) { time(&end_time); printf ("[INFO] %s:%d complete file send\n" , peer_ip, ntohs(peer_addr.sin_port)); printf ("[INFO] Save in : %s\n" ,target_filename); printf ("[INFO] Consume : %lfs\n" , difftime(end_time, start_time)); if (fp) { fclose(fp); fp = NULL ; } send_msg->type = MSG_ACK; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); } else if (rec_msg->type == MSG_EXIT) { printf ("[INFO] client will exit\n" ); send_msg->type = MSG_EXIT; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); break ; } } close(cfd); exit (0 ); } signal(SIGCHLD, sig_child); } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 #include "../head.h" pthread_spinlock_t lock;int rear, head, count;struct msg *queue_buf [QUEUE_SIZE ];void thread (char *filename) { FILE *fp; char buf[BUFSIZ]; ssize_t n; time_t start_time, end_time; struct msg *m ; if (!(fp = fopen(filename, "r" ))) p_error("fopen" ); time(&start_time); while ((n = fread(&buf, sizeof (char ), sizeof (buf), fp))) { if (rear == head) while (count >= QUEUE_SIZE); m = queue_buf[rear]; m->type = MSG_CONTENT; m->len = n; memcpy (m->data, &buf, m->len); rear = (rear + 1 ) % QUEUE_SIZE; pthread_spin_lock(&lock); count++; pthread_spin_unlock(&lock); } if (rear == head) while (count >= QUEUE_SIZE); m = queue_buf[rear]; time(&end_time); printf ("[INFO] complete send file, Consume %lfs.\n" , difftime(end_time, start_time)); if (fp) { fclose(fp); fp = NULL ; } m->type = MSG_DONE; m->len = 0 ; rear = (rear + 1 ) % QUEUE_SIZE; pthread_spin_lock(&lock); count++; pthread_spin_unlock(&lock); } int main (int argc, char *argv[]) { int cfd, i; struct sockaddr_in serv_addr ; struct sockaddr_in connect_addr , peer_addr ; struct msg *send_msg , *rec_msg ; char connect_ip[BUFSIZ], peer_ip[BUFSIZ]; char buf[BUFSIZ], source_filename[BUFSIZ], target_filename[BUFSIZ]; socklen_t connect_len, peer_len; pthread_t tid; void *status; if ((cfd = socket(AF_INET, SOCK_STREAM, 0 )) == -1 ) p_error("socket error" ); for (i=0 ; i<QUEUE_SIZE; i++) queue_buf[i] = (struct msg*)malloc (MSG_SIZE); send_msg = (struct msg*)malloc (MSG_SIZE); rec_msg = (struct msg*)malloc (MSG_SIZE); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1 ], &serv_addr.sin_addr.s_addr); if ((i = connect(cfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr))) == -1 ) { if (errno == ECONNREFUSED) p_error("connection refuse" ); else if (errno == EHOSTUNREACH) p_error("no route to host" ); else if (errno == ETIMEDOUT) p_error("connection time out" ); } else printf ("Connect server succes!\n\n" ); connect_len = sizeof (connect_addr); getsockname(cfd, (struct sockaddr *)&connect_addr, &connect_len); printf ("connect server address = %s:%d\n" , inet_ntop(AF_INET, &connect_addr.sin_addr.s_addr, connect_ip, sizeof (connect_ip)), ntohs(connect_addr.sin_port)); peer_len = sizeof (peer_addr); getpeername(cfd, (struct sockaddr *)&peer_addr, &peer_len); printf ("connect peer address = %s:%d\n" , inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, peer_ip, sizeof (peer_ip)), ntohs(peer_addr.sin_port)); printf ("================\n" ); while (1 ) { printf ("please input source file name: " ); scanf ("%s" , source_filename); memset (send_msg, 0 , sizeof (struct msg)); printf ("please input target file name: " ); scanf ("%s" , target_filename); send_msg->type = MSG_FILENAME; send_msg->len = strlen (target_filename); memcpy (send_msg->data, &target_filename, send_msg->len); send(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 ); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); if (rec_msg->type == MSG_ERROR) { printf ("[ERROR] " ); puts (rec_msg->data); exit (1 ); } if ((i = pthread_create(&tid, NULL , (void *)thread, source_filename)) != 0 ) p_error("pthread_create" ); if ((i = pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE)) != 0 ) p_error("pthread_spin_init" ); while (1 ) { if (rear == head) while (count <= 0 ); struct msg *m = queue_buf[head]; if (m->type != MSG_CONTENT) break ; send(cfd, (void *)m, sizeof (struct msg) + m->len, 0 ); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); if (rec_msg->type == MSG_ERROR) { printf ("[ERROR] " ); puts (rec_msg->data); exit (1 ); } head = (head + 1 ) % QUEUE_SIZE; pthread_spin_lock(&lock); count--; pthread_spin_unlock(&lock); } pthread_join(tid, &status); if (rear == head) while (count <= 0 ); struct msg *m = queue_buf[head]; send(cfd, (void *)m, sizeof (struct msg) + m->len, 0 ); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); head = (head + 1 ) % QUEUE_SIZE; pthread_spin_lock(&lock); count--; pthread_spin_unlock(&lock); loop: printf (">(input 'q' to quit, input 'c' to continue) " ); scanf ("%s" , buf); if (strcmp ("c" , buf) == 0 ) continue ; else if (strcmp ("q" , buf) == 0 ) { memset (send_msg, 0 , sizeof (struct msg)); send_msg->type = MSG_EXIT; send_msg->len = 0 ; send(cfd, (void *)send_msg, sizeof (struct msg), 0 ); recv(cfd, (void *)rec_msg, MSG_SIZE, 0 ); if (rec_msg->type == MSG_ERROR) { printf ("[ERROR] " ); puts (rec_msg->data); exit (1 ); } else if (rec_msg->type == MSG_EXIT) { printf ("[INFO] you will exit..\n" ); close(cfd); break ; } } else goto loop; } return 0 ; }
5.3 使用UDP协议重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 #include "head.h" static pthread_mutex_t mutex;int rear, head, count;struct msg *queue_buf [QUEUE_SIZE ];char client_ip[48 ];int client_port;void thread (char *filename) { FILE *fp; time_t start_time, end_time; int flag = 1 ; if ((fp = fopen(filename, "w+" )) == NULL ) p_error("fopen" ); time(&start_time); while (flag) { if (rear == head) while (count <= 0 ) { usleep(100 ); } struct msg *m = queue_buf[head]; switch (m->type) { case MSG_DONE: if (fp) { fclose(fp); fp = NULL ; } time(&end_time); printf ("[INFO] Complete save file\n" ); printf ("[INFO] Client : %s:%d\n" ,client_ip, client_port); printf ("[INFO] Save path : %s\n" ,filename); printf ("[INFO] Consume : %lfs\n" , difftime(end_time ,start_time)); printf ("============\n" ); head = ( head + 1 ) % QUEUE_SIZE; pthread_mutex_lock(&mutex); count--; pthread_mutex_unlock(&mutex); flag = 0 ; break ; case MSG_CONTENT: fwrite(m->data, sizeof (char ), m->len, fp); fflush(fp); head = ( head + 1 ) % QUEUE_SIZE; pthread_mutex_lock(&mutex); count--; pthread_mutex_unlock(&mutex); break ; default : flag = 0 ; break ; } } pthread_exit(NULL ); } int main (void ) { int lfd, i; struct sockaddr_in serv_addr , clie_addr ; struct msg *send_msg , *rec_msg ; char filename[BUFSIZ]; socklen_t clie_len; pthread_t tid; if ((lfd = socket(AF_INET, SOCK_DGRAM, 0 )) == -1 ) p_error("socket error" ); for (i=0 ; i<QUEUE_SIZE; i++) queue_buf[i] = (struct msg*)malloc (MSG_SIZE); send_msg = (struct msg*)malloc (MSG_SIZE); rec_msg = (struct msg*)malloc (MSG_SIZE); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); int opt = 1 ; setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,&opt, sizeof (opt)); if ((i = bind(lfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr))) == -1 ) p_error("bind error" ); printf ("Server start service..\n" ); while (1 ) { clie_len = sizeof (clie_addr); memset (send_msg, 0 , sizeof (struct msg)); recvfrom(lfd, (void *)rec_msg, MSG_SIZE, 0 , (struct sockaddr *)&clie_addr, &clie_len); rec_msg->data[rec_msg->len] = 0 ; inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, client_ip, sizeof (client_ip)); client_port = ntohs(clie_addr.sin_port); switch (rec_msg->type) { case MSG_FILENAME: strcpy (filename, rec_msg->data); if ((i = pthread_create(&tid, NULL , (void *)thread, filename)) != 0 ) p_error("pthread_create" ); break ; case MSG_CONTENT: if (rear == head) while (count >= QUEUE_SIZE); queue_buf[rear]->type = rec_msg->type; queue_buf[rear]->len = rec_msg->len; memcpy (queue_buf[rear]->data, rec_msg->data, rec_msg->len); rear = (rear + 1 ) % QUEUE_SIZE; pthread_mutex_lock(&mutex); count++; pthread_mutex_unlock(&mutex); break ; case MSG_DONE: if (rear == head) while (count >= QUEUE_SIZE); queue_buf[rear]->type = rec_msg->type; queue_buf[rear]->len = rec_msg->len; memcpy (queue_buf[rear]->data, rec_msg->data, rec_msg->len); rear = (rear + 1 ) % QUEUE_SIZE; pthread_mutex_lock(&mutex); count++; pthread_mutex_unlock(&mutex); break ; default : break ; } } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 #include "head.h" pthread_spinlock_t lock;int rear, head, count;double use_time;struct msg *queue_buf [QUEUE_SIZE ];void thread (char *filename) { FILE *fp; char buf[BUFSIZ]; ssize_t n; time_t start_time, end_time; struct msg *m ; if (!(fp = fopen(filename, "r" ))) p_error("fopen" ); time(&start_time); while (1 ) { if (rear == head) while (count >= QUEUE_SIZE); m = queue_buf[rear]; memset (&buf, 0 , sizeof (buf)); n = fread(&buf, sizeof (char ), sizeof (buf), fp); if (n == 0 ) { if (fp) { fclose(fp); fp = NULL ; } time(&end_time); use_time = difftime(end_time, start_time); m->type = MSG_DONE; m->len = 0 ; m->data[0 ] = '\0' ; rear = (rear + 1 ) % QUEUE_SIZE; pthread_spin_lock(&lock); count++; pthread_spin_unlock(&lock); break ; } else { m->type = MSG_CONTENT; m->len = n; memcpy (m->data, &buf, m->len); rear = (rear + 1 ) % QUEUE_SIZE; pthread_spin_lock(&lock); count++; pthread_spin_unlock(&lock); } } pthread_exit(NULL ); } int main (int argc, char *argv[]) { int cfd, i; struct sockaddr_in serv_addr ; struct msg *send_msg ; char buf[BUFSIZ], source_filename[BUFSIZ], target_filename[BUFSIZ]; pthread_t tid; if ((cfd = socket(AF_INET, SOCK_DGRAM, 0 )) == -1 ) p_error("socket error" ); send_msg = (struct msg*)malloc (MSG_SIZE); memset (&serv_addr, 0 , sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1 ], &serv_addr.sin_addr.s_addr); printf ("Client UDP service start..\n" ); while (1 ) { for (i=0 ; i<QUEUE_SIZE; i++) queue_buf[i] = (struct msg*)malloc (MSG_SIZE); printf ("please input source file name: " ); scanf ("%s" , source_filename); printf ("please input target file name: " ); scanf ("%s" , target_filename); memset (send_msg, 0 , sizeof (struct msg)); send_msg->type = MSG_FILENAME; send_msg->len = strlen (target_filename); memcpy (send_msg->data, &target_filename, send_msg->len); sendto(cfd, (void *)send_msg, sizeof (struct msg) + send_msg->len, 0 , (struct sockaddr *)&serv_addr, sizeof (serv_addr)); if ((i = pthread_create(&tid, NULL , (void *)thread, source_filename)) != 0 ) p_error("pthread_create" ); if ((i = pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE)) != 0 ) p_error("pthread_spin_init" ); while (1 ) { if (rear == head) while (count <= 0 ); struct msg *m = queue_buf[head]; sendto(cfd, (void *)m, sizeof (struct msg) + m->len, 0 , (struct sockaddr *)&serv_addr, sizeof (serv_addr)); if (m->type == MSG_DONE) { printf ("[INFO] complete send file, consume %lfs.\n" , use_time); break ; } head = (head + 1 ) % QUEUE_SIZE; pthread_spin_lock(&lock); count--; pthread_spin_unlock(&lock); } loop: printf (">(input 'q' to quit, input 'c' to continue) " ); scanf ("%s" , buf); if (strcmp ("c" , buf) == 0 ) continue ; else if (strcmp ("q" , buf) == 0 ) { printf ("[INFO] you will exit..\n" ); close(cfd); break ; } else goto loop; } return 0 ; }