版权声明:私藏源代码是违反人性的罪恶行为!博客转载无需告知,学无止境。 https://blog.csdn.net/qq_41822235/article/details/84944700
一、思路
1.1 大致轮廓
正如图1 所示的那样,main主线程核心就在于循环探测是否有客户端的连接请求;一旦有客户端的连接请求,就立刻建立连接然后将客户端套接字移交给函数线程。主线程负责建立连接,函数线程负责具体业务的处理。这是非常清晰而且简单的想法。
1.2 要点
主线程如何将建立好的客户端套接字交付给函数线程?
进程的所有信息对该进程的所有进程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。
第4个参数是一个无类型的指针,调用的时候 &clifd 还是 (void *)clifd 呢?细细品味的话,我们发现只能是后者。因为如果把变量的地址传入的话,main主线程改变该变量值,函数线程中变量的值也会随之改变,毫无疑问,这是我们不希望看到的。
主线程体能否执行close(clifd)操作?这是不行的,因为这是同一个进程内的不同线程,压根就不存在引用计数的概念。如果在main主线程将clifd即客户端套接字关闭的话,函数线程的就关闭了,因为是共享的,所以导致这个结果。将创建客户端套接字clifd放在main主线程,关闭clifd的工作必须交给函数线程。
二、服务器
2.1 头文件及函数声明
#include<stdio.h> //printf()
#include<stdlib.h> //atoi()
#include<sys/socket.h>
#include<unistd.h> //close()
#include<arpa/inet.h> //inet_addr()
#include<netinet/in.h> //htons()
#include<pthread.h> //pthread_create()
#include<assert.h> //assert()
#include<string.h> //strlen()
#define SIZE 128 //用于接受数据的数组大小
int create_socket(const char *ip, int port, int num); //tcp编程流程
void *fun_cb(void *arg); //作为线程的回调函数使用
2.2 main主函数
int main(int argc, char *argv[])
{
if(argc <= 2)
{
printf("argument is not enough.\n");
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int listenfd = create_socket(ip, port, 5); //tcp编程流程
while(1)
{
struct sockaddr_in cli;
socklen_t len = sizeof(cli);
int clifd = accept(listenfd, (struct sockaddr*)&cli, &len);
if(clifd < 0)
{
printf("link error.\n");
continue;
}
pthread_t t;
int res = pthread_create(&t, NULL, fun_cb, (void*)clifd);
assert(res != -1);
}
close(listenfd); //关闭监听套接字
return 0;
}
2.3 其他函数
int create_socket(const char *ip, int port, int num) //tcp编程流程
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0); //生成套接字
assert(-1 != listenfd);
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(port);
ser.sin_addr.s_addr= inet_addr(ip);
int res = 0;
res = bind(listenfd, (struct sockaddr*)&ser, sizeof(ser));
assert(-1 != res);
res = listen(listenfd, num);
assert(-1 != res);
return listenfd;
}
void *fun_cb(void *arg) //作为线程的回调函数使用
{
int clifd = (int)arg; //获取客户端套接字
while(1)
{
char buff[SIZE] = {'\0'};
int n = recv(clifd, buff, SIZE-1, 0);
if(n <= 0) //如果没有数据发送或者客户端断开连接,立即关闭客户端套接字
{
close(clifd);
printf("%d is over.\n", clifd);
break;
}
printf("%d : %s\n", clifd, buff);
send(clifd, "OK", strlen("OK"), 0);
}
}
三、客户端
#include<stdio.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h> //htons()
#include<arpa/inet.h> //inet_addr
#include<unistd.h> //close()
#include<string.h> //string()
#define SIZE 128
int main()
{
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
assert(-1 != listenFd);
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(6000);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(listenFd, (struct sockaddr*)&ser, sizeof(ser));
assert(-1 != res);
while(1)
{
printf("please input: ");
fflush(stdout);
char buf[128] = {'\0'};
scanf("%s", buf);
if(0 == strcmp(buf, "end"))
{
close(listenFd);
break;
}
send(listenFd, SIZE, SIZE-1, 0);
char ans[SIZE] = {'\0'};
recv(listenFd, ans, SIZE-1, 0);
printf("%s\n", ans);
}
close(listenfd);
return 0;
}