一、最简单的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;
}