首页 > 代码库 > 第二十三天、二十四天:套接字
第二十三天、二十四天:套接字
Socket可以看成在两个程序进行通讯连接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。
套接字接口也是进程间通信的一种方式,和前面提到的共享内存、管道通信的区别在于套接字可以提供不同主机之间的通信。
这次课程主要是要会使用UDP、TCP协议进行通信。使用了两天的时间来学习。可见这个知识点的重要性了。先是写出使用UDP和TCP进行最简单的通信:发送一段字符串。对里面使用到的一些函数再编写,更加的了解函数功能,如 htonl(),htons(),inet_addr(),inet_hton().
加强的练习包括使用UDP传输大文件以及使用TCP进行网络对话。
先是UDP通信的发送端代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<netinet/in.h> 4 #include<stdlib.h> 5 #include<sys/socket.h> 6 #include<fcntl.h> 7 8 int main() 9 {10 unsigned char data[1024] = {0};11 int ret = read(0,data,1024);12 if(ret < 0){13 perror("read");14 return 1;15 }16 int fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);17 if(fd < 0){18 perror("socket");19 exit(EXIT_FAILURE);20 }21 struct sockaddr_in mm;22 mm.sin_family = AF_INET; 23 mm.sin_port = htons(9527);24 mm.sin_addr.s_addr = htonl(0xc0a81f72);25 ret = sendto(fd,data,strlen(data),0,(struct sockaddr *)&mm,16);26 if(ret < 0){27 perror("sento");28 exit(EXIT_FAILURE);29 }30 close(fd);31 32 }
UDP通信的接收端代码:
1 #include<stdio.h> 2 #include<netinet/in.h> 3 #include<stdlib.h> 4 #include<sys/socket.h> 5 6 int main() 7 { 8 int fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); 9 if(fd < 0){10 perror("socket");11 return 1;12 }13 unsigned char data[1024] = {0};14 struct sockaddr_in mm;15 mm.sin_family = AF_INET;16 mm.sin_port = htons(9527);17 mm.sin_addr.s_addr = htonl(0xc0a81fc8);18 int len = 0;19 int ret = 0;20 ret = bind(fd,(struct sockaddr *)&mm,16);21 if(ret < 0){22 perror("bind");23 return 1;24 }25 struct sockaddr_in gg;26 int gg_len = 0;27 ret = recvfrom(fd,data,1024,0,(struct sockaddr*)&gg,&gg_len);28 if(ret < 0){29 perror("recvfrom");30 return 1;31 }32 printf("data is : %s\n",data);33 close(fd);34 }
UDP的通信是不可靠的,无连接的数据传输协议。从上面代码看,发送端只管发送数据。不知道接收端是否接送到数据。进行 套接口编程的第一步就是要创建一个套接口,使用socket函数实现。这个函数的三个参数就是指定数据传输的方式、协议。这两个代码要注意的是接收端要对自己的信息进行绑定。
使用UDP传输大文件发送端:
1 #include<stdio.h> 2 #include<string.h> 3 #include<sys/socket.h> 4 #include<fcntl.h> 5 #include<netinet/in.h> 6 #include<stdlib.h> 7 8 int main() 9 { 10 11 /*创建套接口*/12 int fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);13 if(fd < 0){14 perror("socket");15 exit(EXIT_FAILURE);16 }17 /*设置发送目标信息,并发送*/18 int ret = 0;19 char addr[16]= {0};20 printf("please input addr\n");21 ret = read(0,addr,16);22 if(ret < 0){23 perror("read");24 exit(EXIT_FAILURE);25 }26 struct sockaddr_in mm;27 mm.sin_family = AF_INET;28 mm.sin_port =htons(9527);29 mm.sin_addr.s_addr = inet_addr(addr);30 /*将文件保存到字符数组中,发送*/31 int fp = open("aa.tar.gz",O_RDWR);32 if(fp < 0){33 perror("open");34 exit(EXIT_FAILURE);35 }36 char data[1024] = {0};37 int file_size = 0;38 char *file_name ="aa.tar.gz";39 ret = sendto(fd,file_name,20,0,(struct sockaddr *)&mm,16);40 if(ret < 0){41 perror("sento");42 exit(EXIT_FAILURE);43 }44 int sum = 0;45 int i = 0;46 int j = 0;47 while(1){48 file_size = read(fp,data,1024);49 if(file_size < 0){50 perror("read");51 exit(EXIT_FAILURE);52 }53 ret = sendto(fd,data,file_size,0,(struct sockaddr *)&mm,16);54 if(ret < 0){55 perror("sento");56 exit(EXIT_FAILURE);57 }58 printf("file_size is %d\n",file_size);59 sum = sum + file_size;60 if(file_size < 1024){61 printf("sum_size is %d\n",sum);62 break;63 }64 for(i=0;i<100;i++)65 for(j=0;j<1000;j++){66 }67 68 }69 close(fd);70 close(fp);71 }
使用UDP传输大文件接收端:
1 #include<stdio.h> 2 #include<string.h> 3 #include<sys/socket.h> 4 #include<fcntl.h> 5 #include<netinet/in.h> 6 #include<stdlib.h> 7 8 int main() 9 { 10 /*创建套接口*/11 int fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);12 if(fd < 0){13 perror("socket");14 exit(EXIT_FAILURE);15 }16 /*设置接收目标*/17 struct sockaddr_in mm;18 mm.sin_family = AF_INET;19 mm.sin_port =htons(9527);20 mm.sin_addr.s_addr = htonl(0xc0a8010a);21 22 /*绑定,并接收*/23 int ret = bind(fd,(struct sockaddr *)&mm,16);24 if(ret < 0){25 perror("bind");26 exit(EXIT_FAILURE);27 }28 struct sockaddr_in gg;29 int gg_len = 0;30 char data[1024] = {0};31 int recvfile = 0;32 char file_name[20] = {0};33 recvfrom(fd,file_name,20,0,(struct sockaddr *)&gg,&gg_len);34 int fp = open(file_name,O_RDWR|O_CREAT,0644);35 if(fp < 0){36 perror("open");37 exit(EXIT_FAILURE);38 }39 int sum = 0;40 while(1){41 recvfile = recvfrom(fd,data,1024,0,(struct sockaddr *)&gg,&gg_len);42 if(recvfile < 0){43 perror("recvfrom");44 exit(EXIT_FAILURE);45 }46 ret = write(fp,data,recvfile);47 if(ret < 0){48 perror("write");49 exit(EXIT_FAILURE);50 }51 sum = sum + recvfile;52 if(recvfile < 1024){53 printf("sum_file is %d\n",sum);54 break;55 }56 57 }58 close(fd);59 60 61 }
传输文件的思想很简单。用两个循环,将文件进行连续的传输。写这个程序的时候遇到个问题,就是接收到的文件总是小于发送的。开始以为是UDP的不可靠性造成的。后来经老刘指点。原来是在发送的太快。接收端来不及接收导致的数据流失。在发送的时候延迟一会儿就能过解决问题。
使用TCP对话服务端:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<sys/socket.h> 5 #include<netinet/in.h> 6 7 int main() 8 { 9 /*创建套接口*/10 int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);11 if(fd < 0){12 perror("socket");13 exit(EXIT_FAILURE);14 }15 /*服务端信息*/16 struct sockaddr_in srv;17 srv.sin_family = AF_INET;18 srv.sin_port=htons(9527);19 srv.sin_addr.s_addr = htonl(0xc0a8010a);20 /*绑定*/21 int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));22 if(ret < 0){23 perror("bind");24 exit(EXIT_FAILURE);25 }26 /*监听*/27 ret = listen(fd,10);28 if(ret < 0){29 perror("bind");30 exit(EXIT_FAILURE);31 }32 /*接受*/33 struct sockaddr_in snd;34 int snd_len = 0;35 int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len);36 if(nfd < 0){37 perror("accpet");38 exit(EXIT_FAILURE);39 }40 /*聊天*/ 41 char data[1024] = {0};42 char revdata[1024] = {0};43 while(1){44 ret = recv(nfd,revdata,1024,0);45 if(ret < 0){46 perror("recv");47 exit(EXIT_FAILURE);48 }49 printf("client say: %s",revdata);50 ret = read(0,data,1024);51 if(ret < 0){52 perror("read");53 exit(EXIT_FAILURE);54 }55 ret = send(nfd,data,1024,0);56 if(ret < 0){57 perror("recv");58 exit(EXIT_FAILURE);59 }60 61 } 62 close(nfd);63 close(fd);64 }
使用TCP对话客户端:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<sys/socket.h> 5 #include<netinet/in.h> 6 7 int main() 8 { 9 /*创建套接口*/10 int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);11 if(fd < 0){12 perror("socket");13 exit(EXIT_FAILURE);14 }15 /*服务端信息*/16 struct sockaddr_in srv;17 srv.sin_family = AF_INET;18 srv.sin_port=htons(9527);19 srv.sin_addr.s_addr = htonl(0xc0a8010a);20 /*链接*/21 int ret = connect(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));22 if(ret < 0){23 perror("connect");24 exit(EXIT_FAILURE);25 }26 /*聊天*/27 char data[1024] = {0};28 char revdata[1024] = {0};29 while(1){30 ret = read(0,data,1024);31 if(ret < 0){32 perror("read");33 exit(EXIT_FAILURE);34 }35 ret = send(fd,data,1024,0);36 if(ret < 0){37 perror("send");38 exit(EXIT_FAILURE);39 }40 ret = recv(fd,revdata,1024,0);41 if(ret < 0){42 perror("send");43 exit(EXIT_FAILURE);44 }45 printf("service say: %s",revdata);46 }47 close(fd);48 49 }
TCP传输的实现,就是对三次握手的具体话,服务端和客户端进行连接有着特定的步骤。要会使用lisent、connect、accept这三个函数
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 unsigned long my_inet_addr(char *ipaddr); 5 int main() 6 { 7 char ip[] = "192.168.1.10"; 8 unsigned long oip = my_inet_addr(ip); 9 printf("oip is %x\n",oip);10 }11 unsigned long my_inet_addr(char *ip){12 char *str; 13 char *tmp[4] = {0};14 int i = 0;15 16 do{ 17 str = strstr(ip,".");18 *(tmp+i) = strtok(ip,".");19 ip = str + 1;20 i++;21 }while(str);22 23 return (atoi(*(tmp+0))<< 24 & 0xff000000) |(atoi(*(tmp+1)) << 16 & 0xff0000) |(atoi(*(tmp+2)) << 8 & 0xff00) |(atoi(*(tmp+3)) << 0 & 0xff);24 }
这个函数是ip地址的点分式装换成十六进制输出。思想是先将字符截断。然后转换成字符格式。最后进行移位拼接。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<stdlib.h> 4 5 unsigned char *myinet_ntoa(unsigned long ip){ 6 unsigned char *p = (unsigned char * )&ip; 7 unsigned char *tmp = malloc(20); 8 sprintf(tmp,"%d.%d.%d.%d\0",p[3],p[2],p[1],p[0]); 9 return tmp;10 }11 int main(void)12 {13 unsigned long ip = 0xc0a8010a;14 char *p=myinet_ntoa(ip);15 printf("%s\n",p);16 17 }
第二十三天、二十四天:套接字