具体解释都在代码旁边了,不懂得话在评论区提问。
推荐查看得视频:Linux系统编程服务器知识
在上面视频里面找到线程部分好好看一看,多看几遍。
残留问题:客户端连接不上,不知道问题在哪?
#include<stdio.h>
#include<string.h>
#include<arpa/inet.h>
#include<pthread.h>//写线程要加的头文件
#include<ctype.h>
#include<unistd.h>
#include<fcntl.h>
#include"wrap.h"
#define SERV_PORT 9527
#define MAXLINE 8192
struct s_info{
struct sockaddr_in Client_addr;//存储两端链接需要的地址结构,把它理解为一个两端牵线的绳子
int Cfd; //存储文件描述符,就是客户端的名字保存到这里
};
void *do_work(void *arg)//线程调用函数
{
//主要干的事情:读,转大写,写
int n;
char buf[MAXLINE];//这里面用来保存客户端传给服务器的数据
char str[INET_ADDRSTRLEN];//#define INET_ADDRSTRLEN 16
struct s_info *ts = (struct s_info*)arg;//定义一个结构体指针,并把传进来的指针参数付给它,
//注意上面传进来的参数就是通过pthread_create的最后一个参数传过来的
while(1)
{
n= Read(ts->Cfd,buf,MAXLINE);//把客户端传过来的数据(存在套接字)读出来存到buf数组中
if(n==0)//字节数为0,也就是什么都没读到
{
printf("the client %d closed...\n",ts->Cfd);//把这个客户端的编号打印出来
break;//跳出循环,关闭客户端cfd
}
//把读到结构体里的数据打印出来
printf("received from %s at port %d\n",inet_ntop(AF_INET,&(*ts).Client_addr.sin_addr,str,sizeof(str)),ntohs(ts->Client_addr.sin_port));
for(int i=0; i<n; i++)
{
str[i]=toupper(str[i]);//小写转大写
}
Write(STDOUT_FILENO,buf,n);//把read读到的数据被转大写后打印到屏幕上
Write(ts->Cfd,buf,n);//再把转大写后的数据写回给客户端
}
Close(ts->Cfd);//文件描述符使用结束后记得关闭
return (void*)0;
}
//这两个是传出参数,第一个代表命令行总的参数个数
//第二个代表数组存储所有的参数,不过第0个是参数是程序名
int main(int argc, char* argv[])
{
int lfd,cfd;
socklen_t client_len;
pthread_t tid;
struct sockaddr_in serv_addr,client_addr; //定义客户端和服务器端的地址结构,也就是他们连接别人的绳子
struct s_info ts[256];//用这个接收客户端的IP和端口
int i =0;
//一定一定要把这个创建套接字放在对服务器端的绳子进行初始化定义之前!!!!!!!!!!!!!!!!!!!!!!!!
//1.先创建套接字
lfd=Socket(AF_INET,SOCK_STREAM,0);
bzero(&serv_addr,sizeof(serv_addr));//先清零
//对服务器端的绳子进行初始化定义
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//2.将套接字绑定服务器
Bind(lfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
//3.监听客户端函数
Listen(lfd,128);
printf("Accepting client connect ...\n");
//4.接收
while(1)
{
client_len=sizeof(client_addr);
//接收函数第二个参数是用来存储接收到的客户端的绳子
cfd=Accept(lfd,(struct sockaddr*)&client_addr,&client_len);
//然后用结构体把客户端的绳子存储起来
ts[i].Client_addr = client_addr;//两个都是struct sockaddr*类型
ts[i].Cfd = cfd;
//下面开始创建线程了,这部分知识建议根据上上次的链接系统的学习一下
//第一个传出参数,指向新建线程的线程ID
//第二个参数指线程的初始化,分离,线地址,栈大小等属性。一般用NULL
//第三个参数是线程运行函数的起始地址。
//最后一个参数是运行函数的参数。
pthread_create(&tid,NULL,do_work,(void*)&ts[i]);
pthread_detach(tid);//有了它就不用专门回收了
i++;
}
return 0;
}