/*commom.h*/
#ifndef _COMMOM_H_
#define _COMMOM_H_
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SIZE 512
char buf[SIZE];
int create_socket()
{
int lisfd;
lisfd = socket(AF_INET, SOCK_STREAM, 0);//IPV4 数据流 TCP类型
if (lisfd == -1)
{
perror("create_socket failed!\n");
exit(1);
}
return lisfd;
}
int sock_bind(int lisfd, int port)
{
struct sockaddr_in myaddr;
memset((char *)&myaddr, 0, sizeof(struct sockaddr_in));//清零
myaddr.sin_family = AF_INET;//IPV4
myaddr.sin_port = htons(port);//端口
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);//允许连接到所有本地地址上
if (bind(lisfd, (struct sockaddr *)&myaddr, sizeof(struct sockaddr))==-1)
{
perror("sock_bind failed!\n");
exit(1);
}
return 0;
}
int sock_listen(int lisfd, int max_con)
{
if (listen(lisfd, max_con)==-1)
{
perror("sock_listen failed!\n");
exit(1);
}
return 0;
}
int sock_accept(int lisfd)
{
struct sockaddr_in remoaddr;
int clifd;
socklen_t size = sizeof(struct sockaddr);
memset((char *)&remoaddr, 0, sizeof(struct sockaddr));
clifd = accept(lisfd, (struct sockaddr *)&remoaddr, &size);
if (clifd == -1)
{
perror("sock_accept failed!\n");
exit(1);
}
printf("clifd: %d\n", clifd);
printf("connected from %s, port: %d\n", inet_ntoa(remoaddr.sin_addr), ntohs(remoaddr.sin_port));
return clifd;
}
int sock_connect(int clifd, char *ipaddr, int port)
{
struct sockaddr_in remoaddr;
memset((char *)&remoaddr, 0, sizeof(struct sockaddr_in));
remoaddr.sin_family = AF_INET;//IPV4地址
remoaddr.sin_port = htons(port);//端口
remoaddr.sin_addr.s_addr = inet_addr(ipaddr);//服务器端IP地址
if (connect(clifd, (struct sockaddr *)&remoaddr, sizeof(struct sockaddr))==-1)
{
perror("sock_connect failed!\n");
exit(1);
}
return 0;
}
int sock_send(int sockfd, void *buf, size_t len, int flags)
{
int nbytes;
nbytes = send(sockfd, buf, len, flags);
if (nbytes == -1)
{
perror("sock_send failed!\n");
exit(1);
}
return nbytes;
}
int sock_recv(int sockfd, void *buf, size_t len, int flags)
{
int nbytes;
nbytes = recv(sockfd, buf, len, flags);
if (nbytes == -1)
{
perror("sock_send failed!\n");
exit(1);
}
return nbytes;
}
#endif
/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include "commom.h"
#include <errno.h>
#include <sys/wait.h>
#define MAXNUM 10
#define SELECT //定义选用SELECT并发服务,否则用fork()子进程并发服务
void sig_child(int signo)/*用于fork并发时候使用,防止出现僵死进程*/
{
int stat;
pid_t pid;
while((pid=waitpid(-1, &stat, WNOHANG)) > 0)
{
printf("child %d terminated.\n", pid);
}
return ;
}
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
int lisfd, clifd, sockfd;
int i, n, ret;
int nready;
int client[FD_SETSIZE];
int maxfd, maxi;
fd_set rset;
fd_set allset;
signal(SIGCHLD, sig_child);
signal(SIGPIPE, SIG_IGN);//忽略对端TCP终止的信号
bzero(buf, sizeof(buf));
lisfd = create_socket();
int opt = 1;
setsockopt(lisfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//地址重用
sock_bind(lisfd, atoi(argv[1]));
sock_listen(lisfd, MAXNUM);
maxfd = lisfd;
maxi = -1;
for (i=0; i<FD_SETSIZE; i++)//初始化客户端数组,为-1表示可用
{
client[i] = -1;
}
FD_ZERO(&allset);
FD_SET(lisfd, &allset);
#ifdef SELECT
while(1)
{
rset = allset;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if (FD_ISSET(lisfd, &rset))//新的客户端连接
{
clifd = sock_accept(lisfd);
for (i=0; i<FD_SETSIZE; i++)
{
if (client[i] < 0)
{
client[i] = clifd;//保存文件描述符
break;
}
}
if (i == FD_SETSIZE)
{
printf("too many client!\n");
exit(1);
}
FD_SET(clifd, &allset);//增加一个描述符到集合里
if (clifd > maxfd)
{
maxfd = clifd;
}
if (i > maxi)
{
maxi = i;
}
if (--nready <= 0)
{
continue;//没有可读的数据
}
}
for (i=0; i<= maxi; i++)//检测所有客户端的数据
{
if ((sockfd = client[i])<0)
{
continue;
}
if (FD_ISSET(sockfd, &rset))
{
n = sock_recv(sockfd, buf, sizeof(buf), 0);
if (n == 0)
{
printf("The other side has been closed.\n");
fflush(stdout);
close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
}
else
{
printf("recv: %s\n", buf);
for (i=0; i<strlen(buf); i++)
{
buf[i] = toupper(buf[i]);
}
sock_send(sockfd, buf, 512, 0);
}
if (--nready <= 0)
{
break;
}
}
}
}
#else
while(1)
{
clifd = sock_accept(lisfd);//套接字引用计数为1
if (errno == EINTR)
continue;//中断时继续
ret = fork();//fork之后套接字共享,引用计数加一(为2)
if (ret == -1)
{
perror("fork failed!\n");
exit(1);
}else if (ret == 0)//子进程
{
close(lisfd);//套接字引用计数减一
while(1)
{
sock_recv(clifd, buf, sizeof(buf), 0);
printf("recv: %s\n", buf);
for (i=0; i<strlen(buf); i++)
{
(buf)[i] = toupper((buf)[i]);
}
sock_send(clifd, buf, 512, 0);
}
close(clifd);//关闭连接,引用计数减一,为0时释放资源
exit(1);
}else
{
close(clifd);//套接字引用计数减一
}
}
#endif
return 0;
}
/*client.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "commom.h"
int main(int argc, char **argv)
{
if (argc != 3)
{
printf("Usage: %s <server_ipaddr> <port>\n", argv[0]);
exit(1);
}
int clifd;
signal(SIGCHLD, SIG_IGN);//忽略信号
signal(SIGPIPE, SIG_IGN);
bzero(buf, sizeof(buf));
clifd = create_socket();
sock_connect(clifd, argv[1], atoi(argv[2]));
while(fgets(buf, 512, stdin) != NULL)
{
sock_send(clifd, buf, 512, 0);
sock_recv(clifd, buf, sizeof(buf), 0);
printf("recv: %s\n", buf);
}
close(clifd);
return 0;
}
用poll来实现的server.c
/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include "commom.h"
#include <sys/poll.h>
#include <errno.h>
#define MAXNUM 10
#define MAXCON 10
#define INFTIM -1 //
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
int lisfd, clifd, sockfd;
int i, n;
int nready;
int maxi;
struct pollfd client[MAXCON];
signal(SIGPIPE, SIG_IGN);//忽略对端TCP终止的信号
bzero(buf, sizeof(buf));
lisfd = create_socket();
int opt = 1;
setsockopt(lisfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//地址重用
sock_bind(lisfd, atoi(argv[1]));
sock_listen(lisfd, MAXNUM);
maxi = 0;
client[0].fd = lisfd; //client[0]用作监听
client[0].events = POLLIN;
for (i=1; i<MAXCON; i++)//初始化客户端数组,为-1表示可用
{
client[i].fd = -1;
}
while(1)
{
nready = poll(client, maxi+1, INFTIM);
if (client[0].revents & POLLIN)//新的客户端连接
{
clifd = sock_accept(lisfd);
for (i=1; i<MAXCON; i++)
{
if (client[i].fd < 0)
{
client[i].fd = clifd;//保存文件描述符
client[i].events = POLLIN;//测试条件普通数据可读
break;
}
}
if (i == MAXCON)
{
printf("too many client!\n");
exit(1);
}
if (i > maxi)
{
maxi = i;
}
if (--nready <= 0)
{
continue;//没有可读的数据
}
}
for (i=1; i<= maxi; i++)//检测所有客户端的数据
{
if ((sockfd = client[i].fd)<0)
{
continue;
}
if (client[i].revents & (POLLIN|POLLERR))
{
n = sock_recv(sockfd, buf, sizeof(buf), 0);
if (n == 0 || errno == ECONNRESET)
{
printf("The other side has been closed.\n");
fflush(stdout);
close(sockfd);
client[i].fd = -1;
}
else
{
printf("recv: %s\n", buf);
for (i=0; i<strlen(buf); i++)
{
buf[i] = toupper(buf[i]);
}
sock_send(sockfd, buf, 512, 0);
}
if (--nready <= 0)
{
break;
}
}
}
}
return 0;
}
#makefile
CFLAGS = -Wall -g
CC = gcc
TARGET1 = server
TARGET2 = client
all:$(TARGET1) $(TARGET2)
$(TARGET1):$(TARGET1).o
$(CC) $(CFLAGS) -o $@ $^
$(TARGET2):$(TARGET2).o
$(CC) $(CFLAGS) -o $@ $^
%.o:%.c %.h
$(CC) -c $(CFLAGS) -o $@ $<
clean:
rm -f $(TARGET1) $(TARGET2) *.o