socket ipv4服务端
1)创建socket fd
listen_st = socket(AF_INET,SOCK_STREAM,0);
2)初始化server端ip以及端口的信息
struct sockaddr_in sockaddr;
setsockopt(listen_st,SOL_SOCKET,SO_REUSEADDR,&ON,sizeof(on); //设置ip可以重用
sockaddr.sin_port = htons(port);
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//任何ip都可以连接
3)把sockaddr_in 绑定到socket_fd上
bind(listen_st,(struct sockaddr *)&sockaddr,sizeof(sockaddr)
4)监听端口,等待连接
listen(listen_st,5) //监听最多5个连接
5)阻塞接收,获取外部客户端连接过来的文件id accept_st
struct sockaddr_in accept_sockaddr;//用于存储连接过来的socket信息
socklen_t addrlen = sizeof(accept_sockaddr);
accept_st = (listen_st,(struct sockaddr*)&accept_sockaddr,&addrlen);
在已建立连接的listen_st上监听,给accept_sockaddr
6)建立发送线程
phread_create(&send_thrd,NULL,thread_send,&accept_st);
pthread_detach(send_thrd);子线程与主线程分离,自己独立执行
7)建立接收线程
struct pthread_socket{
int socket_d;
pthread_t thrd;
};
pthread_socket ps;
ps.socket_d = accept_st; //接收文件id,linux一切皆为文件
ps.pthread_t thrd; //为什么给了发送线程,因为接收完毕后, 需要发送给客户
pthread_create(&recv_thrd,NULL,thread_recv,&ps);
pthread_detach(recv_thrd); 接收线程独立开来
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MXCONN 2
#define ERRORCODE -1
#define BUFFSIZE 1024
int count_connect = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// https://blog.csdn.net/bandaoyu/article/details/83312254
struct pthread_socket{
int socket_d;
pthread_t thrd;
};
static void *thread_send(void *arg){
char buf[BUFFSIZE];
int sd = *(int *)arg; //this is accept_st
memset(buf,0,sizeof(buf));
strcpy(buf,"hello,welcome to you !\n");
if(send(sd,buf,strlen(buf),0)==-1){
printf("send error:%s \n",strerror(errno));
return NULL;
}
while(1){
memset(buf,0,sizeof(buf));
read(STDIN_FILENO,buf,sizeof(buf)); //键盘输入的信息 阻塞在这儿
if(send(sd,buf,strlen(buf),0)==-1){
printf("send error:%s \n",strerror(errno));
break;
}
}
return NULL;
}
static void* thread_recv(void *arg){
char buf[BUFFSIZE];
struct pthread_socket *pt = (struct pthread_socket *)arg;
int sd = pt->socket_d; //链接过来的accept_st
pthread_t thrd = pt->thrd; //发送线程
while(1){
memset(buf,0,sizeof(buf));
int rv = recv(sd,buf,sizeof(buf),0); //阻塞
if(rv<0){
printf("recv error:%s\n",strerror(errno));
break;
}
if(rv ==0){ //这种情况说明client已经关闭socket连接
break;
}
printf("%s",buf);//输出接收内容
}
pthread_cancel(thrd); //取消了发送线程
pthread_mutex_lock(&mutex);
count_connect--;
pthread_mutex_unlock(&mutex);
close(sd);
return NULL;
}
static int create_listen(int port){
int listen_st;
struct sockaddr_in sockaddr; //定义IP地址结构
int on = 1;
listen_st = socket(AF_INET,SOCK_STREAM,0); //初始化socket(TCP)
if(listen_st == -1){
printf("socket create error:%s \n",strerror(errno));
return ERRORCODE;
}
if(setsockopt(listen_st,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))==-1){ //设置IP地址可重用
printf("setsockopt error:%s \n",strerror(errno));
return ERRORCODE;
}
sockaddr.sin_port = htons(port); //指定一个端口号并将hosts字节型转化成int型(大端或者小端)
sockaddr.sin_family = AF_INET; //设置为tcp/ip模式
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); //服务端等待别人来连接,不需要找谁的ip
//这里写一个常量INADDR_ANY表示server上所有IP,这个server可能有多个ip地址,因为可能有多网卡
if(bind(listen_st,(struct sockaddr *)&sockaddr,sizeof(sockaddr))==-1){
printf("bind error:%s \n",strerror(errno));
return ERRORCODE;
}
if(listen(listen_st,5)==-1){
printf("listen error:%s \n",strerror(errno));
return ERRORCODE;
}
return listen_st;
}
int accept_socket(int listen_st){
int accept_st;
struct sockaddr_in accept_sockaddr;
socklen_t addrlen = sizeof(accept_sockaddr);
memset(&accept_sockaddr,0,addrlen);
accept_st = accept(listen_st,(struct sockaddr *)&accept_sockaddr,&addrlen);
//accept 会阻塞知道客户端链接过来 服务端这个socket只负责listen 是不是有客户端连接过来
//是通过acept返回socket通信的
if(accept_st == -1){
printf("accept error:%s \n",strerror(errno));
return ERRORCODE;
}
printf("accept ip;%s \n",inet_ntoa(accept_sockaddr.sin_addr));
return accept_st;
}
int run_sersver(int port){
int listen_st = create_listen(port);
pthread_t send_thrd,recv_thrd;
struct pthread_socket ps;
int accept_st;
if(listen_st==-1){
return ERRORCODE;
}
printf("server start \n");
while(1){
accept_st = accept_socket(listen_st);
if(accept_st == -1){
return ERRORCODE;
}
if(count_connect >=_PC_MAX_CANON){
printf("connet have already be full! \n");
close(accept_st);
continue;
}
pthread_mutex_lock(&mutex);
count_connect++;
pthread_mutex_unlock(&mutex);
if(pthread_create(&send_thrd,NULL,thread_send,&accept_st)!=0){ //创建发送信息线程
printf("create thrad error:%s \n",strerror(errno));
break;
}
pthread_detach(send_thrd); //设置线程可分离,这样的话主线程就不用Join
ps.socket_d = accept_st;
ps.thrd = send_thrd;
if(pthread_create(&recv_thrd,NULL,thread_recv,&ps)!=0){
printf("create thread error %s\n",strerror(errno));
break;
}
pthread_detach(recv_thrd); //独立与主线程操作
}
close(accept_st);
close(listen_st);
return 0;
}
int main(int argc,char * argv[]){
if(argc<2){
printf("Usage:port ,example:8080 \n");
return -1;
}
int port = atoi(argv[1]);
if(port==0){
printf("port error \n");
}else{
run_sersver(port);
}
return 0;
}
参考:https://blog.csdn.net/bandaoyu/article/details/83312254