Linux 网络编程学习笔记

一、最简单的linux socket TCP 网络通信

服务器:

/*
 *================================================== 
 * @Filename:main.c
 * @Description:socket  TCP  Server
 * @Version:v1.0
 * @Revision:none
 * @Compile:gcc
 * @Author:zhong
 * @Attention: 当接受到客户端的数据为bye,服务器不再接收客户端的数据
 *================================================= 
 */
#include <unistd.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main()
{
//1---创建socket
  int socketfd=socket(AF_INET, SOCK_STREAM,0);
  if(-1==socketfd){
      perror("socket创建失败");
  }
  int opt=1;
//一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。
  setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
/*===========保存服务器信息*/
struct sockaddr_in server_addr;
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;//IPv4
server_addr.sin_port=8000;
server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
/********2-----bind()绑定socket到ip地址和端口*******/
int  ret=bind(socketfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(-1==ret){
    perror("绑定失败");
    exit(1);
}
/**************3--服务器监听客户端的连接*******************************/
 ret =listen(socketfd,10);//每次监听10个
 if(ret==-1){
     perror("监听失败");
     exit(1);
 }
 printf("等待客户端的连接...\n");
 /*保存客户端的信息  */
struct sockaddr_in  client_addr;
int length=sizeof(client_addr);
/**********accept接受客户端的连接(完成3次握手)******************/
int acceptfd=accept(socketfd,(struct socketaddr*)&client_addr,&length);
if(acceptfd==-1){
  perror("accept");
  exit(1);
}
printf("接受客户端的连接%d\n",acceptfd);
char buf[32]={0};
while(1)
{
/*****************************************************************
 * 从acceptfd接受客户端的消息,TCP连接相当于一个文件,fd就是一个文件的描述符
 *,从fd读取数据,就是TCP连接接受数据
 *******************************************************************/
ret=recv(acceptfd,buf,sizeof(buf),0);
if(-1==ret){
  perror("接受数据错误");
  exit(1);
}
if(!strcmp(buf,"bye")){break;}
printf("%s\n",buf);
memset(buf,0,sizeof(buf));
}
  close(acceptfd);
  close(socketfd);
  return 0;
}

客户端:

/*
 *================================================== 
 * @Filename:main.c
 * @Description:socket  TCP  client
 * @Version:v1.0
 * @Revision:none
 * @Compile:gcc
 * @Author:zhong
 * @Attention: 当客户端发送bye 结束发送数据服务器
 * 
 *================================================= 
 */
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}

	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = 8000;
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	//向服务器发起连接
	int ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (-1 == ret)
	{
		perror("connect");
		exit(1);
	}

	char buf[32] = {0};
	while (1)
	{
		scanf("%s", buf);
		ret = send(sockfd, buf, strlen(buf), 0);
		if (-1 == ret)
		{
			perror("send");
			exit(1);
		}
		if (!strcmp(buf, "bye"))
		{
			break;
		}

		memset(buf, 0, sizeof(buf));
	}

	close(sockfd);

	return 0;
}

多线程并发服务器:

/*
 *================================================== 
 * @Filename:main.c
 * @Description:socket TCP 并发服务器(多线程)
 * @Version:v1.0
 * @Revision:none
 * @Compile:gcc
 * @Author:zhong
 * @Attention: 当接受到客户端的数据为bye,服务器不再接收客户端的数据
 *================================================= 
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //网络字节序转换
#include <arpa/inet.h>  //网络IPV4
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
/**
 * @fun:
 * @Description:处理客户端发送过来的数据
 * @Para:void *arg
 * @return NULL 
 */
void *clientHandler(void *arg)
{
   int ret;
   int fd=*(int*) arg; 
   char buf[32]={0};
   /*实现线程分离,线程主动与主线程分分开,其退出状态不能由其他的线程获取,自己直接释放*/
   pthread_detach(pthread_self());//pthread_self();获取线程id
   while(1)
   {
    ret= recv(fd,buf,sizeof(buf),0);
    if(-1==ret){
        perror("接受数据错误");
        exit(1);
    }
    else if (0 == ret)    //客户端异常退出
    {
	    break;
    }
    if(!strcmp(buf,"bye")){
       break;
    }
    printf("接受%d客户端%s\n",fd,buf);
    memset(buf,0,sizeof(buf));
   }
   printf("%d客户端退出\n",fd);
   close(fd);
}
 int main()
 { 
    
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(-1==sockfd){
      perror("创建失败");
      exit(1);
    }
   
    int opt=1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//地址可以被重复使用
    struct sockaddr_in server_addr;  //保存服务器的信息
    memset(&server_addr,0,sizeof(server_addr));

    server_addr.sin_family=AF_INET;
    server_addr.sin_port=8000;
    server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    ///绑定信息
	int ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if(-1==ret){
        perror("bind error");
        exit(1);
    } 

    //设置监听队列
    ret=listen(sockfd,10);
    if(-1==ret){
        perror("listen error");
        exit(1);
    }
   
    struct sockaddr_in  client_addr;   //用于保存客户端的信息
    int  length=sizeof(client_addr);
    while(1){  
    //接受连接(三次握手完成,接受客户端发送来数据)
    printf("等待客户端连接\n");
     int  fd=accept(sockfd,(struct sockaddr*)&client_addr,&length);
     if(-1==fd){
         perror("accept fail");
         exit(1);
        
     }
     printf("接受客户端的数据...\n");
     //为每个客户端创建一个线程
     pthread_t  tid;
     ret=pthread_create(&tid,NULL,clientHandler,&fd);
     if(ret!=0){
         perror("创建线程失败");
         exit(1);
     }
     usleep(1000);
    }
    close(sockfd);
    return 0;
 }
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

#define MAXSIZE 256

/*
epoll---使用流程:
1-epoll_create:创建一个epoll句柄(Linux内核会在epoll文件系统中创建一个file节点,同时创建一个eventpoll结构体)
            :eventpoll结构体包括:rbr红黑数 ,rdllist是一个双向链表用于存放就绪的事件供epol调用
2-epoll_ctl:向epoll 句柄中中添加需要监听的socketfd和事件event

3-epoll_wait :当有事件就绪的时候,epollwait就会通知处理程序去读写

*/
int main()
{
  //1--创建socket
  int sockfd=socket(AF_INET,SOCK_STREAM,0);
  if(-1==sockfd){
    perror("socket create fail");
  }
  //
  int opt=1;
  //确保地址可以重复绑定
  setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); 
  struct sockaddr_in server_addr;
  memset(&server_addr,0,sizeof(server_addr));
  //设定服务器的IP与端口号
  server_addr.sin_family=AF_INET;
  server_addr.sin_port=8000;
  server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
  //bind 绑定服务器的IP与端口号,这样内核就不会给服务器自动分配IP地址。客户端不需要绑定资料
  int ret=bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
  if(-1==ret){
   perror("bind error");
   exit(1);
  }
  //设定服务器可以监听的客户端的数量
  ret=listen(sockfd,10);
  if(ret==-1)
  {
   perror("listen error");
   exit(1);
  }
  以上就是准备工作,下面就是accept 处理监听套接字事件和处理已经连接的套接字事件//
  int epfd=epoll_create(MAXSIZE); //创建epoll文件节点与创建一个eventpol结构体(红黑树加链表)
  if(-1==epfd){
    perror("epoll_create");
    exit(1);
  }
struct epoll_event ev,events[MAXSIZE]={0}; //创建事件对象
ev.events=EPOLLIN;   
ev.data.fd=sockfd;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);//控制某个epoll监控的文件描述符上的事件:注册修改删除
if(-1==ret)
{
  perror("epoll_ctr");
  exit(1);
}
struct sockaddr_in client_addr;  //用于保存客户端的程序
int length=sizeof(client_addr);
int i;
char buf[32]={0};//用于保存客户端的信息
while(1){
//等待所监控的文件描述符上事件的产生
  int num=epoll_wait(epfd,events,MAXSIZE,-1);//-1表示阻塞
  if(-1==num){
    perror("epoll_wait");
    exit(1);
  }
  for(i=0;i<num;i++){
    if(events[i].data.fd==sockfd){
     //有客户端发起连接,三次握手结束
      int fd=accept(sockfd,(struct sockaddr*)&client_addr,&length);
      if(-1==fd)
      {
        perror("accept");
        exit(1);
      }
      printf("accept from fd=%d\n",inet_ntoa(client_addr.sin_addr),fd);
      ev.data.fd=fd;
      ev.events=EPOLLIN;
      ret=epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);
      if(-1==ret){
        perror("epoll_ctr");
      }
    }
    else
    {
     if(events[i].events  & EPOLLIN){
       ret=recv(events[i].data.fd,buf,sizeof(buf),0);
       if(-1==ret){
         perror("recv");
       }else if(0==ret){
            ev.data.fd=events[i].data.fd;
            ev.events=EPOLLIN;
            epoll_ctl(epfd,EPOLL_CTL_DEL,events[i].data.fd,&ev);
            close(events[i].data.fd);

       }else{
         printf("receive %d client message%s\n",events[i].data.fd,buf);
       }
       memset(buf,0,sizeof(buf));
     }
    }
  }
}
  return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值