Socket网络编程--聊天程序(3)
2014-07-26 17:48
281 查看
上一小节,已经讲到可以每个人多说话,而且还没有限制,简单的来说,我们已经完成了聊天的功能了,那么接下来我们要实现什么功能呢?一个聊天程序至少应该支持一对多的通讯吧,接下来就实现多个客户端往服务器发送数据,和服务器向多个客户端发送数据。
多对一,单向,各个客户端都可以向服务器发送数据
close函数
#include <unistd.h>
int close(int sockfd); //用于关闭所打开的Socket套接字 返回值:如果为0表示成功,-1表示失败
处理的办法是每一个客户端连接到服务器,此时服务器每个客户端对伊一个进程进行处理。
client.c
这里的client.c总体没有修改太多,就修改了几个bug而已。
server.c
下面截取运行结果
一对多,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
贴一张运行时的截图
一些关闭的操作没有处理好,反正功能是实现了,容错处理以后再慢慢改吧,下一节将要讲用select来代替这个处理操作。
本文地址: /article/6193119.html
多对一,单向,各个客户端都可以向服务器发送数据
close函数
#include <unistd.h>
int close(int sockfd); //用于关闭所打开的Socket套接字 返回值:如果为0表示成功,-1表示失败
处理的办法是每一个客户端连接到服务器,此时服务器每个客户端对伊一个进程进行处理。
client.c
这里的client.c总体没有修改太多,就修改了几个bug而已。
server.c
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/un.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_PORT 12138 #define BACKLOG 20 #define MAX_CON_NO 10 #define MAX_DATA_SIZE 4096 int main(int argc,char *argv[]) { struct sockaddr_in serverSockaddr,clientSockaddr; char sendBuf[MAX_DATA_SIZE],recvBuf[MAX_DATA_SIZE]; int sendSize,recvSize; int sockfd,clientfd; int on=1; socklen_t sinSize=0; char username[32]; int pid; if(argc != 2) { printf("usage: ./server [username]\n"); exit(1); } strcpy(username,argv[1]); printf("username:%s\n",username); /*establish a socket*/ if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) { perror("fail to establish a socket"); exit(1); } printf("Success to establish a socket...\n"); /*init sockaddr_in*/ serverSockaddr.sin_family=AF_INET; serverSockaddr.sin_port=htons(SERVER_PORT); serverSockaddr.sin_addr.s_addr=htonl(INADDR_ANY); bzero(&(serverSockaddr.sin_zero),8); setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); /*bind socket*/ if(bind(sockfd,(struct sockaddr *)&serverSockaddr,sizeof(struct sockaddr))==-1) { perror("fail to bind"); exit(1); } printf("Success to bind the socket...\n"); /*listen on the socket*/ if(listen(sockfd,BACKLOG)==-1) { perror("fail to listen"); exit(1); } sinSize=sizeof(clientSockaddr);//注意要写上,否则获取不了IP和端口 while(1)//多次accept { /*accept a client's request*/ if((clientfd=accept(sockfd,(struct sockaddr *)&clientSockaddr, &sinSize))==-1) { perror("fail to accept"); exit(1); } printf("Success to accpet a connection request...\n"); printf(" %s:%d join in!\n",inet_ntoa(clientSockaddr.sin_addr),ntohs(clientSockaddr.sin_port)); if((pid=fork())<0) { perror("fork error\n"); } else if(pid==0)/*child*/ { while(1) { /*receive datas from client*/ if((recvSize=recv(clientfd,recvBuf,MAX_DATA_SIZE,0))==-1) { perror("fail to receive datas"); exit(1); } printf("Client:%s\n",recvBuf); memset(recvBuf,0,MAX_DATA_SIZE); } } /*send datas to client*/ /* 本程序不发送 while(1) { fgets(sendBuf,MAX_DATA_SIZE,stdin); if((sendSize=send(clientfd,sendBuf,strlen(sendBuf),0))!=strlen(sendBuf)) { perror("fail to send datas"); exit(1); } printf("Success to send datas\n"); memset(sendBuf,0,MAX_DATA_SIZE); } */ } close(sockfd); return 0; }
下面截取运行结果
一对多,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
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/un.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_PORT 12138 #define BACKLOG 20 #define MAX_CON_NO 10 #define MAX_DATA_SIZE 4096 static void sig_usr1(int singno) { exit(1); } int main(int argc,char *argv[]) { struct sockaddr_in serverSockaddr,clientSockaddr; char sendBuf[MAX_DATA_SIZE],recvBuf[MAX_DATA_SIZE]; int sendSize,recvSize; int sockfd,clientfd; int on=1; socklen_t sinSize=0; char username[32]; int pid; int Queue[MAX_CON_NO+1]; int queue_ptr; int i; if(argc != 2) { printf("usage: ./server [username]\n"); exit(1); } strcpy(username,argv[1]); printf("username:%s\n",username); /*establish a socket*/ if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) { perror("fail to establish a socket"); exit(1); } printf("Success to establish a socket...\n"); /*init sockaddr_in*/ serverSockaddr.sin_family=AF_INET; serverSockaddr.sin_port=htons(SERVER_PORT); serverSockaddr.sin_addr.s_addr=htonl(INADDR_ANY); bzero(&(serverSockaddr.sin_zero),8); setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); /*bind socket*/ if(bind(sockfd,(struct sockaddr *)&serverSockaddr,sizeof(struct sockaddr))==-1) { perror("fail to bind"); exit(1); } printf("Success to bind the socket...\n"); /*listen on the socket*/ if(listen(sockfd,BACKLOG)==-1) { perror("fail to listen"); exit(1); } sinSize=sizeof(clientSockaddr);//注意要写上,否则获取不了IP和端口 queue_ptr=0; while(1)//多次accept { /*accept a client's request*/ if((clientfd=accept(sockfd,(struct sockaddr *)&clientSockaddr, &sinSize))==-1) { perror("fail to accept"); exit(1); } printf("Success to accpet a connection request...\n"); printf(">>>>>> %s:%d join in!\n",inet_ntoa(clientSockaddr.sin_addr),ntohs(clientSockaddr.sin_port)); Queue[queue_ptr++]=clientfd; if(pid!=0) { kill(pid,SIGUSR1); } if((pid=fork())<0) { perror("fork error\n"); } else if(pid==0)/*child*/ { while(1) { /*receive datas from client*/ if((recvSize=recv(clientfd,recvBuf,MAX_DATA_SIZE,0))==-1) { perror("fail to receive datas"); exit(1); } printf("Client:%s\n",recvBuf); memset(recvBuf,0,MAX_DATA_SIZE); } } if((pid=fork())<0) { perror("fork error"); } else if(pid==0)//child { /*send datas to client*/ signal(SIGUSR1,sig_usr1); printf("现在有%d个人\n",queue_ptr);//没有考虑断开的问题 while(1) { fgets(sendBuf,MAX_DATA_SIZE,stdin); for(i=0;i<queue_ptr;i++) { if((sendSize=send(Queue[i],sendBuf,strlen(sendBuf),0))!=strlen(sendBuf)) { perror("fail to send datas"); exit(1); } else { printf("Success to send datas\n"); } } memset(sendBuf,0,MAX_DATA_SIZE); } } } close(sockfd); return 0; }
贴一张运行时的截图
一些关闭的操作没有处理好,反正功能是实现了,容错处理以后再慢慢改吧,下一节将要讲用select来代替这个处理操作。
本文地址: /article/6193119.html
相关文章推荐
- 基于Udp的Socket网络编程聊天程序
- Socket网络编程--聊天程序(9)
- java编程_socket_套接字_网络编程_简易的GUI多线程聊天程序
- Socket网络编程--聊天程序(1)
- 基于Udp的Socket网络编程聊天程序
- java 聊天程序 socket网络编程
- Socket网络编程--聊天程序(2)
- Socket网络编程--聊天程序(4)
- 网络编程与多线程的应用--基于socket udp编写一个简单聊天程序
- Socket网络编程--聊天程序(7)
- Socket网络编程--聊天程序(6)
- Delphi-网络编程-第一个网络方面作品(UDP聊天程序)
- 基于socket的Linux网络聊天程序--单线程非阻塞客户端
- c++ 网络编程 socket 聊天客户端/服务器
- java 网络编程【6】 用UDP编写网络聊天程序 以及 TCP编程实例
- iPhone之网络编程初体验-简单的聊天程序
- 基于socket的Linux上的网络聊天程序--多线程的服务器
- Java 网络编程之 (UDP网络聊天程序)
- socket编程-- 基于TCP协议的网络程序