网络编程之套接字(tcp)
经过几天高强度的学习,对套接字的编程有了初步的认识,今天对这几天所学的知识总结一下;首先简单阐述一下tcp通信;
TCP提供的是可靠的,顺序的,以及不会重复的数据传输,处理流控制,由于TCP是可靠的,连接的,顺序的,所以TCP一般用于都应用于对传输的完整性,正确性要求严的场合;编写基于tcp的服务器-客户端模型的程序简单流程如下:
服务器端:
(1)调用socket()创建一个套接口
(2)调用bind()函数是服务器进程与一个端口绑定
(3)调用listen()设置客户接入队列的大小
(4)调用accept()接受一个连接,如果介入的队列不为空,则返回一个已连接的套接口描述符,
(5)调用sned()和recv()函数用来在已连接的套接口间进行发送和接收数据
客户端:
(1)调用socket()创建套接字
(2)调用connect()函数向服务器发送连接请求;
(3)调用send()函数和recv()函数
下面是服务器端的代码;
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 5 //server 6 int main() 7 { 8 int fd = 0; 9 int nfd = 0; 10 int ret = 0; 11 unsigned char data[1024] = {0}; 12 13 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 14 if(fd < 0) { 15 perror("socket"); 16 return 1; 17 } 18 /*服务端信息*/ 19 struct sockaddr_in ser; 20 ser.sin_family = AF_INET; 21 ser.sin_port = htons(2009); 22 ser.sin_addr.s_addr = htonl(0xc0a8010a);//192.168.1.10 23 struct sockaddr_in clt; 24 int len = 0; 25 26 /*绑定*/ 27 ret = bind(fd, (struct sockaddr *)&ser, 16); 28 if(ret < 0) { 29 perror("bind"); 30 return 1; 31 } 32 33 /*监听*/ 34 ret = listen(fd, 10); 35 if(ret == -1) { 36 perror("listen"); 37 return 1; 38 } 39 40 /*接收连接,并且返回一个新的套接字描述符nfd*/ 41 nfd = accept(fd, (struct sockaddr *)&clt, &len); 42 if(ret < 0) { 43 perror("accept"); 44 return 1; 45 } 46 47 /*接收*/ 48 ret = recv(nfd, data, 1024, 0); 49 if(ret < 0) { 50 perror("recv"); 51 return 1; 52 } 53 printf("clt said: %s\n", data); 54 close(fd); 55 56 return 0; 57 }
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <string.h> 5 6 //client 7 int main() 8 { 9 int sock_fd = 0; 10 int ret = 0; 11 unsigned char *buf = "hello, hao ara you"; 12 13 /*创建一个套接口*/ 14 sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 15 if(sock_fd < 0) { 16 perror("socket"); 17 return 1; 18 } 19 20 /*服务端信息*/ 21 struct sockaddr_in ser; 22 ser.sin_family = AF_INET; 23 ser.sin_port = htons(2009); 24 ser.sin_addr.s_addr = inet_addr("192.168.1.10"); 25 26 //建立链接 27 ret = connect(sock_fd, (struct sockaddr *)&ser, 16); 28 if(ret < 0) { 29 perror("connect"); 30 return 1; 31 } 32 33 /*发送*/ 34 ret = send(sock_fd, buf, strlen(buf), 0); 35 if(ret < 0) { 36 perror("send"); 37 return 1; 38 } 39 40 close(sock_fd); 41 return 0; 42 }
上面程序是基于tcp的简单通信,下面我们利用tcp实现一个服务器多个客户机;要实现一对多,就要使用线程编程,服务器端在不断监听中,如果有连接请求的话,就用通过 accept函数接受并创建一个线程来处理。线程的创建函数为int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);
下面是这个程序的源码
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <string.h> 5 #include <pthread.h> 6 7 #define PORT 9527 8 9 void *function(void *d);//线程要执行的函数 10 11 int main() 12 { 13 /*创建套接口*/ 14 pthread_t pid= 0; 15 int nfd = 0; 16 int fd = 0; 17 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 18 if(fd < 0) { 19 perror("sock"); 20 return 1; 21 } 22 /*服务器信息*/ 23 struct sockaddr_in ser; 24 ser.sin_family = AF_INET; 25 ser.sin_port = htons(PORT); 26 ser.sin_addr.s_addr = inet_addr("192.168.1.10"); 27 struct sockaddr_in clt; 28 int len = 0; 29 30 /*绑定*/ 31 int ret = bind(fd, (struct sockaddr *)&ser, 16); 32 if(ret == -1) { 33 perror("bind"); 34 return 1; 35 } 36 37 /*监听*/ 38 ret = listen(fd, 10); 39 if(ret < 0) { 40 perror("listen"); 41 return 1; 42 } 43 44 while(1) { 45 /*接受链接*/ 46 nfd = accept(fd, (struct sockaddr *)&clt, &len); 47 if(nfd < 0) { 48 perror("accept"); 49 return 1; 50 } 51 52 /*创建一个线程*/ 53 ret = pthread_create(&pid, NULL, function, (void *)nfd); 54 if(ret != 0) { 55 perror("pthread_create"); 56 return 1; 57 } 58 59 pthread_join(pid, NULL); 60 61 close(nfd); 62 } 63 64 close(fd); 65 return 0; 66 } 67 68 void *function(void *d) 69 { 70 unsigned char buf[1024] = {0}; 71 int nfd = (int )d; 72 int ret = 0; 73 74 memset(buf, 0, 1024); 75 ret = recv(nfd, buf, 1024, 0); 76 if(ret < 0) { 77 perror("recv"); 78 return NULL; 79 } 80 printf("client said: %s\n", buf); 81 82 ret = send(nfd, "recv ok", 7, 0); 83 if(ret < 0) { 84 perror("send"); 85 return NULL; 86 } 87 88 89 }
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <string.h> 5 6 #define PORT 9527 7 8 int main(int argc, char **argv) 9 { 10 if(argc != 3) { 11 printf("using %s <ip address> <message>\n", argv[0]); 12 return 0; 13 } 14 /*创建套接口*/ 15 int fd = 0; 16 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 17 if(fd < 0) { 18 perror("socket"); 19 return 1; 20 } 21 22 /*服务器信息*/ 23 struct sockaddr_in ser; 24 ser.sin_family = AF_INET; 25 ser.sin_port = htons(PORT); 26 ser.sin_addr.s_addr = inet_addr(argv[1]); 27 28 /*创建链接*/ 29 int ret = connect(fd, (struct sockaddr *)&ser, 16); 30 if(ret < 0) { 31 perror("connect"); 32 return 1; 33 } 34 35 /*访问*/ 36 ret = send(fd, argv[2], strlen(argv[2]), 0); 37 if(ret < 0) { 38 perror("send"); 39 return 1; 40 } 41 42 char buf[1024] = {0}; 43 ret = recv(fd, buf, 1024, 0); 44 if(ret < 0) { 45 perror("recv"); 46 return 1; 47 } 48 printf("server: %s\n", buf); 49 close(fd); 50 51 return 0; 52 }
上面代码需要注意的是,监听程序最大允许接受10个连接请求,如果这十个一直连接不断开的话,后续的连接请求就无法得到处理,所以我们需要在每次请求完毕之后就关闭nfd;下次请求再重新连接;
第三个程序我们实现基于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 /*服务端信息*/ 17 struct sockaddr_in srv; 18 srv.sin_family = AF_INET; 19 srv.sin_port=htons(9527); 20 srv.sin_addr.s_addr = htonl(INADDR_ANY); 21 22 /*绑定*/ 23 int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr )); 24 if(ret < 0){ 25 perror("bind"); 26 exit(EXIT_FAILURE); 27 } 28 ret = listen(fd,10); 29 if(ret < 0){ 30 perror("bind"); 31 exit(EXIT_FAILURE); 32 } 33 struct sockaddr_in snd; 34 int snd_len = 0; 35 36 /*接受*/ 37 int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len); 38 if(nfd < 0){ 39 perror("accpet"); 40 exit(EXIT_FAILURE); 41 } 42 43 char data[1024] = {0}; 44 char revdata[1024] = {0}; 45 /*聊天*/ 46 while(1){ 47 memset(revdata, 0, 1024); 48 memset(data, 0, 1024); 49 ret = recv(nfd,revdata,1024,0); 50 if(ret < 0){ 51 perror("recv"); 52 exit(EXIT_FAILURE); 53 } 54 55 printf("client say: %s\n",revdata); 56 if(strcmp(revdata, "end") == 0) { 57 break; 58 } 59 60 ret = read(0,data,1024); 61 if(ret < 0){ 62 perror("read"); 63 exit(EXIT_FAILURE); 64 } 65 ret = send(nfd,data,1024,0); 66 if(ret < 0){ 67 perror("recv"); 68 exit(EXIT_FAILURE); 69 } 70 71 72 } 73 close(nfd); 74 close(fd); 75 return 0; 76 }
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <string.h> 5 #include <sys/types.h> 6 #include <unistd.h> 7 8 int main() 9 { 10 /*创建套接口*/ 11 int sock_fd = 0; 12 sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 13 if(sock_fd < 0) { 14 perror("socket"); 15 return 1; 16 } 17 /*服务端信息*/ 18 struct sockaddr_in ser; 19 ser.sin_family = AF_INET; 20 ser.sin_port = htons(9527); 21 ser.sin_addr.s_addr = inet_addr("192.168.1.10"); 22 23 /*建立链接*/ 24 int ret = connect(sock_fd, (struct sockaddr *)&ser, 16); 25 if(ret == -1) { 26 perror("connect"); 27 return 1; 28 } 29 30 /*聊天*/ 31 unsigned char data[1024] = {0}; 32 unsigned char rec[1024] = {0}; 33 while(1) { 34 memset(data, 0, 1024); 35 memset(rec, 0, 1024); 36 int r_size = read(0, data, 1024); 37 if(r_size < 0) { 38 perror("read"); 39 return 1; 40 } 41 42 ret = send(sock_fd, data, strlen(data), 0); 43 if(ret < 0) { 44 perror("send"); 45 return 1; 46 } 47 48 ret = recv(sock_fd, rec, 1024, 0); 49 if(ret < 0) { 50 perror("recv"); 51 return 1; 52 } 53 printf("server said: %s\n", rec); 54 } 55 close(sock_fd); 56 return 0; 57 }
上面这个代码存在的缺陷是,发送方跟接收只能发送一句接收一句,不能一次性发送多句,要解决这个问题就要需用到IO多路服用,可以通过这个函数来实现:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
下面贴出代码:
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 /*服务端信息*/ 17 struct sockaddr_in srv; 18 srv.sin_family = AF_INET; 19 srv.sin_port=htons(9527); 20 srv.sin_addr.s_addr = htonl(INADDR_ANY); 21 22 /*绑定*/ 23 int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr )); 24 if(ret < 0){ 25 perror("bind"); 26 exit(EXIT_FAILURE); 27 } 28 ret = listen(fd,10); 29 if(ret < 0){ 30 perror("bind"); 31 exit(EXIT_FAILURE); 32 } 33 struct sockaddr_in snd; 34 int snd_len = 0; 35 36 /*接受*/ 37 int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len); 38 if(nfd < 0){ 39 perror("accpet"); 40 exit(EXIT_FAILURE); 41 } 42 43 fd_set rfds; 44 char data[1024] = {0}; 45 char revdata[1024] = {0}; 46 /*聊天*/ 47 while(1){ 48 49 FD_ZERO(&rfds); 50 FD_SET(0,&rfds); 51 FD_SET(nfd,&rfds); 52 ret = select(nfd+1,&rfds,NULL,NULL,NULL); 53 if(FD_ISSET(nfd, &rfds)){ 54 ret = recv(nfd,revdata,1024,0); 55 if(ret < 0){ 56 perror("recv"); 57 exit(EXIT_FAILURE); 58 } 59 60 printf("client say: %s\n",revdata); 61 if(strcmp(revdata, "end") == 0) { 62 break; 63 } 64 } 65 if(FD_ISSET(0, &rfds)) { 66 ret = read(0,data,1024); 67 if(ret < 0){ 68 perror("read"); 69 exit(EXIT_FAILURE); 70 } 71 ret = send(nfd,data,1024,0); 72 if(ret < 0){ 73 perror("recv"); 74 exit(EXIT_FAILURE); 75 } 76 } 77 memset(revdata, 0, 1024); 78 memset(data, 0, 1024); 79 } 80 close(nfd); 81 close(fd); 82 return 0; 83 }
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int main() { /*创建套接口*/ int sock_fd = 0; sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sock_fd < 0) { perror("socket"); return 1; } /*服务端信息*/ struct sockaddr_in ser; ser.sin_family = AF_INET; ser.sin_port = htons(9527); ser.sin_addr.s_addr = inet_addr("192.168.1.10"); /*建立链接*/ int ret = connect(sock_fd, (struct sockaddr *)&ser, 16); if(ret == -1) { perror("connect"); return 1; } /*聊天*/ fd_set rfds; unsigned char data[1024] = {0}; unsigned char rec[1024] = {0}; while(1) { memset(data, 0, 1024); memset(rec, 0, 1024); FD_ZERO(&rfds); //清空 FD_SET(0,&rfds);//(标准输入) FD_SET(sock_fd,&rfds);//添加监听描述符(套接字) /*多路复用IO*/ ret = select(sock_fd+1,&rfds,NULL,NULL,NULL); if(FD_ISSET(0, &rfds)){//监听键盘是否有输入,执行接收 int r_size = read(0, data, 1024); if(r_size < 0) { perror("read"); return 1; } ret = send(sock_fd, data, strlen(data), 0); if(ret < 0) { perror("send"); return 1; } } if(FD_ISSET(sock_fd, &rfds)) {//监听sock_fd是否有输入,执行接收 ret = recv(sock_fd, rec, 1024, 0); if(ret < 0) { perror("recv"); return 1; } printf("server said: %s\n", rec); } } close(sock_fd); return 0; }