1、多进程server.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include<ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
//回收子进程
void wait_child(int signo)
{
while(waitpid(0,NULL,WNOHANG)>0);
return ;
}
int main()
{
//1、创建套接字
int listenfd = socket(AF_INET,SOCK_STREAM,0);
//2、绑定套接字
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr("192.168.3.65");
serveraddr.sin_port = htons(8888);
bind(listenfd,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr));
//3、监听套接字
listen(listenfd,10);//之后的所有客户端连接都存储在监听队列中
socklen_t len;
struct sockaddr_in client;
pid_t pid;
char buf[50];
int num;
int clientfd;
char clie_ip[100];
while(2){
//循环取出监听队列的链接套接字
clientfd = accept(listenfd,(struct sockaddr *)&client,&len);
//打印其他客户端的连接信息
printf("client ip:%s client port:%d \n",inet_ntop(AF_INET,&client.sin_addr.s_addr,clie_ip,sizeof(clie_ip)),ntohs(client.sin_port));
pid = fork();
if(pid<0){
perror(" pid error\n");
exit(1);
}
if(pid==0)
{
close(listenfd);
break;
}
else{
close(clientfd);
signal(SIGCHLD,wait_child);
}
}
if(pid==0){
while(1)
{
bzero(buf,50);
num = read(clientfd,buf,sizeof(buf));
if(num==0){
// close(clientfd);
return 0;
}else if(num == -1){
perror("read error");
exit(1);
}
else{
for(int i=0;i<num;i++)
buf[i] = toupper(buf[i]);
write(clientfd,buf,num);
write(STDOUT_FILENO,buf,num);
}
}
}
return 0;
}
2、多线程server.c
#include <sys/types.h> /* See NOTES */
#include <signal.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
//线程处理函数
void * connect_hander(void * arg)
{
int clientfd = *(int *)arg;//得到通信的套接字
char buf[50];
//----通信
while(1)
{
bzero(buf,50);
read(clientfd,buf,50);
printf("%s",buf);
write(clientfd,buf,50);
if(strcmp(buf,"close\n") == 0)
{
close(clientfd);
pthread_exit(NULL);
}
}
}
int main()
{
//1、创建套接字
int listenfd = socket(AF_INET,SOCK_STREAM,0);
//2、绑定套接字
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr("192.168.3.65");
serveraddr.sin_port = htons(8888);
bind(listenfd,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr));
//3、监听套接字
listen(listenfd,10);//之后的所有客户端连接都存储在监听队列中
//循环取出监听队列的链接套接字,如果有连接就创建一个线程进行通信,猪线程继续循环监听
int clientfd = -1;
while(1)
{
struct sockaddr_in client;
bzero(&client,sizeof(struct sockaddr));
socklen_t len;
clientfd = accept(listenfd,(struct sockaddr *)&client,&len);
printf("accept ok\n");
//创建线程
pthread_t tid;
pthread_create(&tid,NULL,connect_hander,&clientfd);
//主线程继续检测连接,进行下次循环accept
}
close(listenfd);
return 0;
}
测试代码,可以打开多个进程测试
nc 192.168.3.163 8888
3、客户端client.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
struct sockaddr_in seraddr,local;
int lfd = socket(AF_INET,SOCK_STREAM,0);
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = inet_addr("192.168.3.65");
local.sin_family= AF_INET;
local.sin_port = ntohs(9999);
local.sin_addr.s_addr = inet_addr("192.168.3.65");
bind(lfd,(struct sockaddr*)&local,sizeof(struct sockaddr));
int ret = connect(lfd,(struct sockaddr*)&seraddr,sizeof(struct sockaddr));
if(ret<0)
{
perror("connect error");
return -1;
}
printf("connect ok\n");
char buf[128];
while(1){
fgets(buf,sizeof(buf),stdin);
write(lfd,buf,128);
bzero(buf,128);
read(lfd,buf,sizeof(buf));
printf("%s\n",buf);
}
return 0;
}
随记:
端口复用实现(不需要再等2msl时间就可以重开服务器端口)
int opt =1;
setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
主机字节序和网络字节序
端口号发送时htons
端口号接收时ntohs
ip地址inet_addr()
发生强转的几个
bind accept(第三个参数是地址,客户端) connect
记录连接的ip地址和端口号
char clieip[100];
printf("client ip:%s client port:%d \n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,cline_ip,sizeof(clie_ip)),
ntohs(clieaddr.sin_port));
多进程并发:
当子进程退出时,父进程无法去回收子进程,此时需要调用waitpid()回调机制
使用信号捕捉SIGCHLD()进行回收,内核回收效率更高
signal(SIGCHLD,wait_child);
然后写wait_child函数
void wait_child(int signo)
{
while(waitpid(0,NULL,WNOHANG)>0);
return ;
}
查看进程
netstat -apn | grep 8888