- Socket地址复用
-
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);
服务端尽可能使用SO_REUSEADDR,在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。该选项可以使得server不必等待TIME_WAIT状态消失就可以重启服务器(对于TIME_WAIT状态会在后面续有叙述).
可以在bind之前添加代码(完整代码请参照博文最后):
int on = 1;
if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,
&on,sizeof(on)) == -1)
err_exit("setsockopt SO_REUSEADDR error");这个可以用来支持地址的复用
-
- 多线程连接
- 当我们在进行多客户连接的时候会发现,对一个只能连接一个客户端的服务器,你可以用多个客户端连接这个服务器,客户端也不会发出出错返回,而服务器也不会发出出错返回,但是服务器不会为该客户端服务,虽然 看起来客户端确实是连接上了服务器。所以我们可以用一条连接一个线程,提供更高的并发量。
- 这里我们可以写一个server/多个client来学习多线程并发操作。
- TCP通信图
- UDP通信图
- 功能设计实现图
- 代码实现图
- 实现代码思想
- server:创建服务器和客户端的简单的信息,利用地址复用技术充分利用地址;绑定端口;监听端口;循环的与客户端建立连接:
建立连接;创建子进程用于接收客户端消息并发送出去 :在子进程中再次创建子进程用来接收消息(当接收的消息不为空的时候,将客户端发来的消息处理进制之后再发回给客户);1 服务器端代码 2 #include <sys/types.h> 3 #include <sys/socket.h> // 包含套接字函数库 4 #include <stdio.h> 5 #include <netinet/in.h> // 包含AF_INET相关结构 6 #include <arpa/inet.h> // 包含AF_INET相关操作的函数 7 #include <unistd.h> 8 #include<string.h> 9 #include<stdlib.h> 10 #include<fcntl.h> 11 #include<sys/shm.h> 12 13 #define MYPORT 8887 14 #define MYKEY 12345 15 #define SIZE 10240 16 17 int main() 18 { 19 char buf[100]; 20 memset(buf,0,100); //初始化100个0 21 //创建服务器,客户端的信息 22 int server_sockfd,client_sockfd;//socket函数返回的套接字 23 socklen_t server_len,client_len; 24 struct sockaddr_in server_sockaddr,client_sockaddr; 25 printf("\n======================server initialization======================\n"); 26 27 server_sockfd = socket(AF_INET,SOCK_STREAM, 0); // 定义套接字类型 TCP 28 server_sockaddr.sin_family = AF_INET; 29 server_sockaddr.sin_port = htons(MYPORT); 30 server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 31 server_len = sizeof(server_sockaddr); 32 33 //允许重复使用本地地址和套接字绑定 地址复用:在bind()函数前面加上上面这段代码即可 34 int on = 1; 35 setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); 36 37 //绑定端口 38 if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,server_len)==-1) 39 { 40 perror("bind"); 41 exit(1); 42 } 43 44 //监听端口 45 if(listen(server_sockfd,5) == -1) 46 { 47 perror("listen"); 48 exit(1); 49 } 50 51 client_len = sizeof(client_sockaddr); 52 pid_t ppid,pid;//这里pid_t 相当于int 53 54 //循环的接收客户端发来的连接 55 while(1) 56 { 57 //当客户端发来的套接字返回 -1 接收失败 58 if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_sockaddr,&client_len))==-1) 59 { 60 perror("accept error"); 61 exit(1); 62 } 63 //建立连接 64 else 65 { 66 send(client_sockfd,"You have connect Server!",strlen("You have connect Server!"),0); 67 } 68 //将十进制ip地址转换成一个32位的二进制ip序列 69 printf("\n%s:%d Login server!\n\n",inet_ntoa(client_sockaddr.sin_addr), ntohs(client_sockaddr.sin_port)); 70 71 ppid = fork(); //创建子进程 fork:返回-1出错 返回0子进程 返回其他数字表示返回子进程id 72 73 if(ppid == -1) 74 { 75 printf("fork 1 failed:"); 76 } 77 else if(ppid == 0) //子进程用于接收客户端信息并发送 78 { 79 int recvbytes; 80 pid = fork(); //再次创建子进程 81 82 if(pid == -1) 83 { 84 printf("fork 2 failed:"); 85 exit(1); 86 } 87 else if(pid == 0) //子进程的子进程用于接收消息 88 { 89 while(1) 90 { 91 bzero(buf,100); 92 if((recvbytes = recv(client_sockfd,buf,100,0))==-1) 93 { 94 perror("read client_sockfd failed:"); 95 } 96 else if(recvbytes != 0) 97 { 98 buf[recvbytes] = ‘\0‘; 99 usleep(10000); 100 printf("%s:%d said:%s\n",inet_ntoa(client_sockaddr.sin_addr), ntohs(client_sockaddr.sin_port), buf); 101 102 //将客户端发送过来的消息发回给客户 103 if(send(client_sockfd,buf,recvbytes,0)==-1){ 104 perror("send error"); 105 break; 106 } 107 } 108 } 109 } 110 else if(pid>0) //此时的id为子进程id 111 { 112 113 } 114 } 115 else if(ppid>0) 116 { 117 //总父进程中关闭client_sockfd(因为有另一个副本在子进程中运行了)返回等待接收消息 118 close(client_sockfd); 119 } 120 } 121 return 0; 122 }
- client:创建客户端的基本信息并且完善;建立连接;收到服务器发回的数据,当数据字节大小不为空时,开始输入要发送的数据,并且发送至服务器
1 客户端代码 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <errno.h> 5 #include <string.h> 6 #include <sys/types.h> 7 #include <netinet/in.h> 8 #include <sys/socket.h> 9 #include <sys/wait.h> 10 #include<unistd.h> 11 #include <arpa/inet.h> 12 #define SERVER_PORT 8887 /* 客户机连接远程主机的端口 */ 13 #define MAXDATASIZE 100 /* 每次可以接收的最大字节 */ 14 #define SERVER_IP "192.168.11.8" /* 服务器的IP地址 */ 15 16 int main() 17 { 18 int sockfd, numbytes; 19 char buf[MAXDATASIZE]; 20 struct sockaddr_in server_addr; 21 22 printf("\n======================client initialization======================\n"); 23 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 24 { 25 perror("socket"); 26 exit(1); 27 } 28 server_addr.sin_family = AF_INET; 29 server_addr.sin_port = htons(SERVER_PORT); 30 server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); 31 bzero(&(server_addr.sin_zero),sizeof(server_addr.sin_zero)); 32 33 //客户端请求建立连接 34 if (connect(sockfd, (struct sockaddr *)&server_addr,sizeof(struct sockaddr_in)) == -1) 35 { 36 perror("connect"); 37 exit(1); 38 } 39 40 //循环输入文字 41 while(1) 42 { 43 bzero(buf,MAXDATASIZE);//相当于memset函数 44 printf("\nBegin receive...\n"); 45 if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) 46 { 47 perror("recv"); 48 exit(1); 49 } 50 else if (numbytes > 0) 51 { 52 int len, bytes_sent; 53 buf[numbytes] = ‘\0‘; 54 printf("Received: %s\n",buf); 55 56 printf("Send:"); 57 char *msg; 58 scanf("%s",msg); 59 len = strlen(msg); 60 61 //发送至服务器 62 if(send(sockfd,msg,len,0) == -1) 63 { 64 perror("send error"); 65 } 66 } 67 else 68 { 69 //numbytes=0,表示socket已断开 70 printf("soket end!\n"); 71 } 72 73 } 74 75 close(sockfd); 76 return 0; 77 }
- server:创建服务器和客户端的简单的信息,利用地址复用技术充分利用地址;绑定端口;监听端口;循环的与客户端建立连接:
- Linux下进行编译:
gcc Server.c -o server
gcc Client.c -o client
时间: 2024-10-29 19:11:22