Socket网络编程--聊天程序(3)
上一小节,已经讲到可以每个人多说话,而且还没有限制,简单的来说,我们已经完成了聊天的功能了,那么接下来我们要实现什么功能呢?一个聊天程序至少应该支持一对多的通讯吧,接下来就实现多个客户端往服务器发送数据,和服务器向多个客户端发送数据。
多对一,单向,各个客户端都可以向服务器发送数据
close函数
#include <unistd.h>
int close(int sockfd); //用于关闭所打开的Socket套接字 返回值:如果为0表示成功,-1表示失败
处理的办法是每一个客户端连接到服务器,此时服务器每个客户端对伊一个进程进行处理。
client.c
这里的client.c总体没有修改太多,就修改了几个bug而已。
server.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <errno.h> 4 #include <string.h> 5 #include <netdb.h> 6 #include <sys/types.h> 7 #include <sys/socket.h> 8 #include <sys/time.h> 9 #include <sys/un.h> 10 #include <sys/ioctl.h> 11 #include <sys/wait.h> 12 #include <netinet/in.h> 13 #include <arpa/inet.h> 14 15 16 #define SERVER_PORT 12138 17 #define BACKLOG 20 18 #define MAX_CON_NO 10 19 #define MAX_DATA_SIZE 4096 20 21 int main(int argc,char *argv[]) 22 { 23 struct sockaddr_in serverSockaddr,clientSockaddr; 24 char sendBuf[MAX_DATA_SIZE],recvBuf[MAX_DATA_SIZE]; 25 int sendSize,recvSize; 26 int sockfd,clientfd; 27 int on=1; 28 socklen_t sinSize=0; 29 char username[32]; 30 int pid; 31 32 if(argc != 2) 33 { 34 printf("usage: ./server [username]\n"); 35 exit(1); 36 } 37 strcpy(username,argv[1]); 38 printf("username:%s\n",username); 39 40 /*establish a socket*/ 41 if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) 42 { 43 perror("fail to establish a socket"); 44 exit(1); 45 } 46 printf("Success to establish a socket...\n"); 47 48 /*init sockaddr_in*/ 49 serverSockaddr.sin_family=AF_INET; 50 serverSockaddr.sin_port=htons(SERVER_PORT); 51 serverSockaddr.sin_addr.s_addr=htonl(INADDR_ANY); 52 bzero(&(serverSockaddr.sin_zero),8); 53 54 setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); 55 56 /*bind socket*/ 57 if(bind(sockfd,(struct sockaddr *)&serverSockaddr,sizeof(struct sockaddr))==-1) 58 { 59 perror("fail to bind"); 60 exit(1); 61 } 62 printf("Success to bind the socket...\n"); 63 64 /*listen on the socket*/ 65 if(listen(sockfd,BACKLOG)==-1) 66 { 67 perror("fail to listen"); 68 exit(1); 69 } 70 71 sinSize=sizeof(clientSockaddr);//注意要写上,否则获取不了IP和端口 72 while(1)//多次accept 73 { 74 /*accept a client's request*/ 75 if((clientfd=accept(sockfd,(struct sockaddr *)&clientSockaddr, &sinSize))==-1) 76 { 77 perror("fail to accept"); 78 exit(1); 79 } 80 printf("Success to accpet a connection request...\n"); 81 printf(" %s:%d join in!\n",inet_ntoa(clientSockaddr.sin_addr),ntohs(clientSockaddr.sin_port)); 82 83 if((pid=fork())<0) 84 { 85 perror("fork error\n"); 86 } 87 else if(pid==0)/*child*/ 88 { 89 while(1) 90 { 91 /*receive datas from client*/ 92 if((recvSize=recv(clientfd,recvBuf,MAX_DATA_SIZE,0))==-1) 93 { 94 perror("fail to receive datas"); 95 exit(1); 96 } 97 printf("Client:%s\n",recvBuf); 98 memset(recvBuf,0,MAX_DATA_SIZE); 99 } 100 } 101 /*send datas to client*/ 102 /* 本程序不发送 103 while(1) 104 { 105 fgets(sendBuf,MAX_DATA_SIZE,stdin); 106 if((sendSize=send(clientfd,sendBuf,strlen(sendBuf),0))!=strlen(sendBuf)) 107 { 108 perror("fail to send datas"); 109 exit(1); 110 } 111 printf("Success to send datas\n"); 112 memset(sendBuf,0,MAX_DATA_SIZE); 113 } 114 */ 115 } 116 close(sockfd); 117 118 return 0; 119 }
下面截取运行结果
一对多,server端向各个客户端发送数据(广播)
接下来就趁热打铁吧,随便完成服务器向各个客户端发送数据,进而实现相互通讯。实现的技术细节是使用一个数组保存每次客户端连接的套接字。然后如果要服务器发送数据,就遍历数组中的所有客户端套接字,然后对每个套接字进行send数据。
出现的问题,由于使用的是阻塞方式,所以要创建多进程,然而使用fork创建的进程是完整的拷贝父进程,所以其他进程accept一个新的连接后修改保存套接字的数组是不影响其他进程的数据的。查了一下,说有个vfork函数可以是父进程和子进程共享数据,但是最后发现,子进程是优先于父进程执行的,而且要子进程执行完后才会执行父进程。所以这个办法不行。一想进程间通信那么多办法总有可以的。就想到了使用信号处理。
signal函数
#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int); //signo是信号,func是捕获该信号后要处理的函数,称为“信号处理程序 signal handler” 我习惯叫这个函数为 注册函数,它就好像是对signo定义一个处理函数
kill函数(kill函数是将信号发送给进程或进程组,一个相似的函数raise是给进程本身发送信号的)
#include <signal.h>
int kill(pid_t pid, int signo); //成功返回0,出错返回-1 。pid>0 将该信号发送给进程ID为pid的进程, pid==0 将信号发给与本身进程属于同一个进程组的所有进程。 pid<0 将信号发送给其进程组ID等于pid的绝对值。
int raise(int signo); //成功返回0,出错返回-1
好了,废话不说了,直接给代码。
client.c
这里的client代码与前面的基本相同。
server.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <errno.h> 4 #include <string.h> 5 #include <netdb.h> 6 #include <sys/types.h> 7 #include <sys/socket.h> 8 #include <sys/time.h> 9 #include <sys/un.h> 10 #include <sys/ioctl.h> 11 #include <sys/wait.h> 12 #include <netinet/in.h> 13 #include <arpa/inet.h> 14 15 16 #define SERVER_PORT 12138 17 #define BACKLOG 20 18 #define MAX_CON_NO 10 19 #define MAX_DATA_SIZE 4096 20 21 static void sig_usr1(int singno) 22 { 23 exit(1); 24 } 25 26 int main(int argc,char *argv[]) 27 { 28 struct sockaddr_in serverSockaddr,clientSockaddr; 29 char sendBuf[MAX_DATA_SIZE],recvBuf[MAX_DATA_SIZE]; 30 int sendSize,recvSize; 31 int sockfd,clientfd; 32 int on=1; 33 socklen_t sinSize=0; 34 char username[32]; 35 int pid; 36 int Queue[MAX_CON_NO+1]; 37 int queue_ptr; 38 int i; 39 40 if(argc != 2) 41 { 42 printf("usage: ./server [username]\n"); 43 exit(1); 44 } 45 strcpy(username,argv[1]); 46 printf("username:%s\n",username); 47 48 /*establish a socket*/ 49 if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) 50 { 51 perror("fail to establish a socket"); 52 exit(1); 53 } 54 printf("Success to establish a socket...\n"); 55 56 /*init sockaddr_in*/ 57 serverSockaddr.sin_family=AF_INET; 58 serverSockaddr.sin_port=htons(SERVER_PORT); 59 serverSockaddr.sin_addr.s_addr=htonl(INADDR_ANY); 60 bzero(&(serverSockaddr.sin_zero),8); 61 62 setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); 63 64 /*bind socket*/ 65 if(bind(sockfd,(struct sockaddr *)&serverSockaddr,sizeof(struct sockaddr))==-1) 66 { 67 perror("fail to bind"); 68 exit(1); 69 } 70 printf("Success to bind the socket...\n"); 71 72 /*listen on the socket*/ 73 if(listen(sockfd,BACKLOG)==-1) 74 { 75 perror("fail to listen"); 76 exit(1); 77 } 78 79 sinSize=sizeof(clientSockaddr);//注意要写上,否则获取不了IP和端口 80 queue_ptr=0; 81 while(1)//多次accept 82 { 83 /*accept a client's request*/ 84 if((clientfd=accept(sockfd,(struct sockaddr *)&clientSockaddr, &sinSize))==-1) 85 { 86 perror("fail to accept"); 87 exit(1); 88 } 89 printf("Success to accpet a connection request...\n"); 90 printf(">>>>>> %s:%d join in!\n",inet_ntoa(clientSockaddr.sin_addr),ntohs(clientSockaddr.sin_port)); 91 Queue[queue_ptr++]=clientfd; 92 93 if(pid!=0) 94 { 95 kill(pid,SIGUSR1); 96 } 97 98 if((pid=fork())<0) 99 { 100 perror("fork error\n"); 101 } 102 else if(pid==0)/*child*/ 103 { 104 while(1) 105 { 106 /*receive datas from client*/ 107 if((recvSize=recv(clientfd,recvBuf,MAX_DATA_SIZE,0))==-1) 108 { 109 perror("fail to receive datas"); 110 exit(1); 111 } 112 printf("Client:%s\n",recvBuf); 113 memset(recvBuf,0,MAX_DATA_SIZE); 114 } 115 } 116 117 if((pid=fork())<0) 118 { 119 perror("fork error"); 120 } 121 else if(pid==0)//child 122 { 123 /*send datas to client*/ 124 signal(SIGUSR1,sig_usr1); 125 printf("现在有%d个人\n",queue_ptr);//没有考虑断开的问题 126 while(1) 127 { 128 fgets(sendBuf,MAX_DATA_SIZE,stdin); 129 for(i=0;i<queue_ptr;i++) 130 { 131 if((sendSize=send(Queue[i],sendBuf,strlen(sendBuf),0))!=strlen(sendBuf)) 132 { 133 perror("fail to send datas"); 134 exit(1); 135 } 136 else 137 { 138 printf("Success to send datas\n"); 139 } 140 141 } 142 memset(sendBuf,0,MAX_DATA_SIZE); 143 } 144 145 } 146 } 147 close(sockfd); 148 149 return 0; 150 }
贴一张运行时的截图
一些关闭的操作没有处理好,反正功能是实现了,容错处理以后再慢慢改吧,下一节将要讲用select来代替这个处理操作。
本文地址: http://www.cnblogs.com/wunaozai/p/3870258.html
Socket网络编程--聊天程序(3)相关推荐
- Socket网络编程--聊天程序(8)
上一节已经完成了对用户的身份验证了,既然有了验证,那么接下来就能对不同的客户端进行区分了,所以这一节讲实现私聊功能.就是通过服务器对客户端的数据进行转发到特定的用户上, 实现私聊功能的聊天程序 实现的 ...
- 基于Udp的Socket网络编程聊天程序
1.新建一个工程区Net 在工作区中添加两个工程 NetSrv 和 NetClient 为两个工程添加库文件 (Link中) ws2_32.lib 2.在工程NetSrv中添加Server.cpp文件 ...
- Socket网络编程--聊天程序(2)
上一节简单如何通过Socket创建一个连接,然后进行通信.只是每个人只能说一句话.而且还是必须说完才会接收到信息,总之是很不方便的事情.所以这一小节我们将对上一次的程序进行修改,修改成每个人可以多说话 ...
- Socket网络编程--小小网盘程序(5)
http://www.cnblogs.com/wunaozai/p/3893469.html 各位好呀!这一小节应该就是这个小小网盘程序的最后一小节了,这一节将实现最后的三个功能,即列出用户在服务器中 ...
- Socket网络编程(2)--服务端实现
中秋了,首先祝大家中秋快乐,闲着无事在家整一个socket的聊天程序,有点仿QQ界面,就是瞎折腾,不知道最后是不是能将所有功能实现. 如果你对socket不了解,请看这篇文章:http://www.c ...
- MFC socket网络编程(流程示例)
MFC socket网络编程(流程示例) 1.TCP流式套接字的编程步骤 在使用之前须链接库函数:工程->设置->Link->输入ws2_32.lib,OK! 服务器端程序: 1.加 ...
- Netty网络编程聊天项目
Netty网络编程聊天项目 后端编写 导入依赖 <dependencies><dependency>&l ...
- BIO,Socket网络编程入门代码示例,NIO网络编程入门代码示例,AIO 网络编程
BIO,Socket网络编程入门代码示例 1.BIO服务器端程序 package cn.itcast.bio;import java.io.InputStream; import java.io.Ou ...
- python网络编程讲解_详解Python Socket网络编程
Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...
最新文章
- java文件读写的两种方式
- 安卓java模拟器_用大白话告诉你:Java 后端到底是在做什么?
- [Sdoi2010] 地精部落
- 训练日志 2018.9.12
- leetcode - 516. 最长回文子序列
- python-flask-Flask-SQLAlchemy与Flask-Migrate联合进行数据化迁移
- 解决vue中axios同步的问题
- java人脸识别Demo(数据库mongo)
- 图书管理系统的分析与设计
- 谈一谈|《黑神话:悟空》实机演示观后感
- 唐诗学习系统-java课程设计
- html页面证书过期,网页证书过期怎么办
- 解决loadrunner使用谷歌浏览器录制时打不开网页的问题
- python成功安装cartopy之后,调用crs时却出现错误,如何解决
- OpenGL---实例 球体 画圆锥
- 经典黑白搭配 现代简约风格设计美学精神
- “插座”,“充电宝”,“数据线”用英语怎么说?
- 源码深度解析之 Spring IOC
- 中国工程院院士邬江兴:未来网络之憧憬——情景网络
- poj 3230 Travel
热门文章
- python面试题_春招苦短,我用百道Python面试题备战
- openCV学习教程(一):Mat类的使用
- 【杂谈】篇篇精华,有三AI不得不看的技术综述(超过100篇核心干货)
- 【知识星球】图像降噪模型和数据集内容开启更新,经典问题永垂不朽!
- 【模型解读】从LeNet到VGG,看卷积+池化串联的网络结构
- 全球及中国N95级医用防护口罩市场销售规模与产量需求预测报告2022版
- 中国海上风力发电行业战略调研与投资风险分析报告2022-2028年
- 全球及中国吉他霉素预混剂行业创新现状与可持续发展分析报告2022-2027年版
- (转)mybatis一级缓存二级缓存
- leetcode 27. Remove Element