标签:style blog http io color ar os 使用 sp
连续四天学习套接字的编程,可见套接字的重要性了。基于TCP和UDP分别写了两个程序。一是利用TCP实现一个服务器对多个客户端,客户端你发送信息,服务器就从事先准备好的五个字符串中随机回复一条。另一个是利用UDP实现两个人的对话,对话时可以是多个信息同时输入。
先是第一个程序。要实现一对多,就要使用线程编程,服务器端在不断监听中,如果有连接请求的话,就用通过accept函数接受并创建一个线程来处理。线程的创建函数为int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);这个函数是一个回调函数,会调用start_routine函数指针指向的函数进行处理,后面的arg为指向函数的参数。线程还有个重要的函数是 int pthread_join(pthread_t thread, void **retval);用来等待一个线程的结束。retval用来存放线程的返回值。下面是这个程序的源代码:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<sys/socket.h> 5 #include<netinet/in.h> 6 7 void *func(void *arg){ 8 9 int ret = 0; 10 int nfd = (int)arg; 11 char revdata[1024] = {0}; 12 char *talk[5] = {0}; 13 *(talk + 0) = "hello world !"; 14 *(talk + 1) = "hello bunfly !"; 15 *(talk + 2) = "hello good !"; 16 *(talk + 3) = "hello xixi !"; 17 *(talk + 4) = "hello lala !"; 18 int i = 0; 19 while(1){ 20 ret = recv(nfd,revdata,1024,0); 21 if(ret < 0){ 22 perror("recv"); 23 exit(EXIT_FAILURE); 24 } 25 ret = send(nfd,*(talk +i),strlen(*(talk+i)),0); 26 if(ret < 0){ 27 perror("recv"); 28 exit(EXIT_FAILURE); 29 } 30 printf("ret is %d\n",ret); 31 i = rand() % 4; 32 } 33 close(nfd); 34 } 35 int main() 36 { 37 /*创建套接口*/ 38 int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 39 if(fd < 0){ 40 perror("socket"); 41 exit(EXIT_FAILURE); 42 } 43 /*服务端信息*/ 44 struct sockaddr_in srv; 45 srv.sin_family = AF_INET; 46 srv.sin_port=htons(9527); 47 srv.sin_addr.s_addr = inet_addr("192.168.31.200"); 48 /*绑定*/ 49 int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr )); 50 if(ret < 0){ 51 perror("bind"); 52 exit(EXIT_FAILURE); 53 } 54 /*监听*/ 55 ret = listen(fd,10); 56 if(ret < 0){ 57 perror("bind"); 58 exit(EXIT_FAILURE); 59 } 60 /*接受*/ 61 struct sockaddr_in snd; 62 int snd_len = 0; 63 pthread_t pid; 64 while(1){ 65 int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len); 66 if(nfd < 0){ 67 perror("accpet"); 68 exit(EXIT_FAILURE); 69 } 70 ret = pthread_create(&pid,NULL,func,(void *)nfd); 71 if(ret < 0){ 72 perror("pthread_create"); 73 return 1; 74 } 75 } 76 close(fd); 77 }
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<sys/socket.h> 5 #include<netinet/in.h> 6 #include<fcntl.h> 7 8 int main() 9 { 10 /*创建套接口*/ 11 int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 12 if(fd < 0){ 13 perror("socket"); 14 exit(EXIT_FAILURE); 15 } 16 /*服务端信息*/ 17 struct sockaddr_in srv; 18 srv.sin_family = AF_INET; 19 srv.sin_port=htons(9527); 20 srv.sin_addr.s_addr = inet_addr("192.168.31.122"); 21 /*链接*/ 22 int ret = connect(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr )); 23 if(ret < 0){ 24 perror("connect"); 25 exit(EXIT_FAILURE); 26 } 27 /*聊天*/ 28 char data[1024] = {0}; 29 char revdata[1024] = {0}; 30 int i = 0; 31 while(1){ 32 ret = read(0,data,1024); 33 if(ret < 0){ 34 perror("read"); 35 exit(EXIT_FAILURE); 36 } 37 ret = send(fd,data,1024,0); 38 if(ret < 0){ 39 perror("send"); 40 exit(EXIT_FAILURE); 41 } 42 ret = recv(fd,revdata,1024,0); 43 if(ret < 0){ 44 perror("send"); 45 exit(EXIT_FAILURE); 46 } 47 printf("service say: %s\n",revdata); 48 } 49 close(fd); 50 51 }
上面的代码如果在实际的操作中有着明显的漏洞。监听程序最大允许接受10个连接请求,如果这十个一直连接不断开的话,后续的连接请求就无法得到处理。正确的方式应该是着几个请求在完成一个连接,执行一个操作后又要重新连接。
TCP可靠的连接方式造成了连接过错比较复杂。消耗资源,所以大部分程序就是使用UDP的方式,比如迅雷,QQ。
使用UDP聊天,这就意味着双方都要进行绑定操作。前面写的程序当发送信息后,程序就处于等待接收的状态。一直等到有信息来了之后才能够继续发送信息。这种状态称为阻塞状态。这种方式明显是不可取的。这时就要利用int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout)函数实现I/O多路复用。具体的实现过程我在代码中有注释。
#include<unistd.h> #include<stdlib.h> #include<stdio.h> #include<string.h> #include<netinet/in.h> #include<sys/socket.h> #include<sys/ioctl.h> #include<net/if.h> unsigned long getip(char *net_name);//获得指定网卡的ip。 int main(void) { int fd = 0; fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);//创建套接字,使用ipv4协议,数据报格式,UDP协议 if(fd < 0){ perror("socket"); exit(EXIT_FAILURE); } struct sockaddr_in gg ; gg.sin_family = AF_INET; gg.sin_port = htons(9527); gg.sin_addr.s_addr = getip("eth0");//获取eth0网卡的ip地址 int ret = bind(fd,(struct sockaddr *)&gg,sizeof(struct sockaddr));//向系统绑定gg的信息 if(ret < 0){ perror("bind"); exit(EXIT_FAILURE); } fd_set rfds; char data[1024] = {0}; char revdata[1024] = {0}; struct sockaddr_in mm ; mm.sin_family = AF_INET; mm.sin_port = htons(9527); mm.sin_addr.s_addr = getip("eth1");//获得eth1网卡的ip地址 int mm_len; while(1){ memset(revdata,0,1024); memset(data,0,1024);//初始化数据, FD_ZERO(&rfds); FD_SET(0,&rfds); FD_SET(fd,&rfds); ret = select(fd+1,&rfds,NULL,NULL,NULL);//多路复用IO,使得fd,0处于非阻塞状态 if(FD_ISSET(fd,&rfds)){//监听到fd有数据读入,执行数据接收 ret = recvfrom(fd,revdata,1024,0,(struct sockaddr *)&mm,&mm_len); if(ret < 0){ perror("recv"); exit(EXIT_FAILURE); } printf("mm say: %s",revdata); } if(FD_ISSET(0,&rfds)){//监听到键盘有数据读入,执行数据的读取和发送 ret = read(0,data,1024); if(ret < 0){ perror("read"); exit(EXIT_FAILURE); } ret = sendto(fd,data,ret,0,(struct sockaddr *)&mm,sizeof(struct sockaddr)); if(ret < 0){ perror("recv"); exit(EXIT_FAILURE); } } } close(fd); } unsigned long getip(char *net_name){ int sock; struct sockaddr_in sin; struct ifreq ifr; sock = socket(AF_INET,SOCK_DGRAM,0); if(sock < 0){ perror("socket"); return -1; } strcpy(ifr.ifr_name,net_name); int ret = ioctl(sock,SIOCGIFADDR,&ifr); if(ret < 0){ perror("ioctl"); return -1; } memcpy(&sin,&ifr.ifr_addr,sizeof(sin)); return inet_addr(inet_ntoa(sin.sin_addr)); }
1 #include<unistd.h> 2 #include<stdlib.h> 3 #include<stdio.h> 4 #include<string.h> 5 #include<netinet/in.h> 6 #include<sys/socket.h> 7 #include<sys/ioctl.h> 8 #include<net/if.h> 9 unsigned long getip(char *net_name); 10 11 int main(void) 12 { 13 int fd = 0; 14 fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); 15 if(fd < 0){ 16 perror("socket"); 17 exit(EXIT_FAILURE); 18 } 19 struct sockaddr_in gg ; 20 gg.sin_family = AF_INET; 21 gg.sin_port = htons(9527); 22 gg.sin_addr.s_addr = getip("eth0"); 23 24 fd_set rfds; 25 char data[1024] = {0}; 26 char revdata[1024] = {0}; 27 struct sockaddr_in mm ; 28 mm.sin_family = AF_INET; 29 mm.sin_port = htons(9527); 30 mm.sin_addr.s_addr = getip("eth1"); 31 int ret = bind(fd,(struct sockaddr *)&mm,sizeof(struct sockaddr)); 32 if(ret < 0){ 33 perror("bind"); 34 exit(EXIT_FAILURE); 35 } 36 int gg_len; 37 while(1){ 38 memset(revdata,0,1024); 39 memset(data,0,1024); 40 FD_ZERO(&rfds); 41 FD_SET(0,&rfds); 42 FD_SET(fd,&rfds); 43 ret = select(fd+1,&rfds,NULL,NULL,NULL); 44 if(FD_ISSET(fd,&rfds)){ 45 ret = recvfrom(fd,revdata,1024,0,(struct sockaddr *)&gg,&gg_len); 46 if(ret < 0){ 47 perror("recv"); 48 exit(EXIT_FAILURE); 49 } 50 printf("gg say: %s",revdata); 51 } 52 if(FD_ISSET(0,&rfds)){ 53 ret = read(0,data,1024); 54 if(ret < 0){ 55 perror("read"); 56 exit(EXIT_FAILURE); 57 } 58 ret = sendto(fd,data,ret,0,(struct sockaddr *)&gg,sizeof(struct sockaddr)); 59 if(ret < 0){ 60 perror("recv"); 61 exit(EXIT_FAILURE); 62 } 63 } 64 } 65 close(fd); 66 } 67 unsigned long getip(char *net_name){ 68 int sock; 69 struct sockaddr_in sin; 70 struct ifreq ifr; 71 sock = socket(AF_INET,SOCK_DGRAM,0); 72 if(sock < 0){ 73 perror("socket"); 74 return -1; 75 } 76 strcpy(ifr.ifr_name,net_name); 77 int ret = ioctl(sock,SIOCGIFADDR,&ifr); 78 if(ret < 0){ 79 perror("ioctl"); 80 return -1; 81 } 82 memcpy(&sin,&ifr.ifr_addr,sizeof(sin)); 83 return inet_addr(inet_ntoa(sin.sin_addr)); 84 }
两方的程序代码差不多。就是发送方和接收方对调。这个程序是实现本机的两个网卡eth0和eth1进行通话。利用getip函数获得指定网卡的ip.
标签:style blog http io color ar os 使用 sp
原文地址:http://www.cnblogs.com/linrong/p/4087882.html