- 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(" ======================server initialization====================== "); 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(" %s:%d Login server! ",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] = '
- server:创建服务器和客户端的简单的信息,利用地址复用技术充分利用地址;绑定端口;监听端口;循环的与客户端建立连接:
- TCP通信图