每连接一个客户端都创建一个子线程去处理信息,线程设置成分离态,线程结束系统自动回收资源。线程处理函数中不能使用exit函数 。运行程序。./可执行程序名 +IP地址+端口号。
server.c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define ERRLOG(ermsg) \
do { \
printf("%s %s %d:", __FILE__, __func__, __LINE__); \
perror(ermsg); \
exit(-1); \
} while (0)
//exit();进程退出函数,在线程里不能使用,在线程处理函数中禁用
typedef struct MSG {
int acceptfd;
struct sockaddr_in clientaddr;
} msg_t;
void* func(void* arg)
{
msg_t msg = *(msg_t*)arg; // 这里用结构体变量,如果是指针,指针变量指向主线程的msg
printf("客户端[%s:%d]连接到服务器\n", inet_ntoa(msg.clientaddr.sin_addr),
ntohs(msg.clientaddr.sin_port));
char buf[128];
while (1) {
memset(buf, 0, sizeof(buf));
int ret = recv(msg.acceptfd, buf, sizeof(buf), 0);
if (ret == -1) {
perror("recv error");
break;
}
if (ret == 0) {
printf("客户端[%s:%d]断开连接\n", inet_ntoa(msg.clientaddr.sin_addr),
ntohs(msg.clientaddr.sin_port));
break;
}
if (!strcmp("quit", buf)) {
printf("客户端[%s:%d]退出了\n", inet_ntoa(msg.clientaddr.sin_addr),
ntohs(msg.clientaddr.sin_port));
break;
}
printf("客户端[%s:%d]发来数据[%s]\n", inet_ntoa(msg.clientaddr.sin_addr),
ntohs(msg.clientaddr.sin_port), buf);
sprintf(buf, "%s-cmcc", buf);
if (-1 == send(msg.acceptfd, buf, sizeof(buf), 0)) {
perror("send error");
break;
}
}
close(msg.acceptfd);
pthread_exit(NULL);
}
int main(int argc, const char* argv[])
{
if (argc != 3) {
printf("retype!\n");
return -1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
ERRLOG("socket error");
}
// 填充服务器信息结构体
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t serveraddr_len = sizeof(serveraddr);
if (-1 == bind(sockfd, (struct sockaddr*)&serveraddr, serveraddr_len)) {
ERRLOG("bind error");
}
if (-1 == listen(sockfd, 10)) {
ERRLOG("listen error");
}
// 定义客户端消息结构体
struct sockaddr_in clientaddr;
memset(&clientaddr, 0, sizeof(clientaddr));
socklen_t clientaddr_len = sizeof(clientaddr);
msg_t msg;
memset(&msg, 0, sizeof(msg));//在while外清0
pthread_t tid = 0;
while (1) {
int acceptfd = accept(sockfd, (struct sockaddr*)&clientaddr, &clientaddr_len);
if (acceptfd == -1) {
ERRLOG("accept error");
}
msg.acceptfd = acceptfd;
msg.clientaddr = clientaddr; // 结构体变量类型一样可以相互赋值
if (0 != pthread_create(&tid, NULL, func, &msg)) {
ERRLOG("pthread creat error");
}
pthread_detach(tid);
}
return 0;
}
client.c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define ERRLOG(ermsg) \
do { \
printf("%s %s %d:", __FILE__, __func__, __LINE__); \
perror(ermsg); \
exit(-1); \
} while (0)
int main(int argc, const char* argv[])
{
if (argc != 3) {
printf("Usage : %s <IP> <PORT>\n", argv[0]);
return -1;
}
// 1创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET:IPV4,SOCK_STREAM:TCP
if (-1 == sockfd) {
ERRLOG("socket error");
}
// 2填充服务器网络信息结构体
struct sockaddr_in serveraddr = {
.sin_family = AF_INET,
.sin_port = htons(atoi(argv[2])), // 端口号 argv[2]输入进来的是char *字符串,需要转换成数用atoi
.sin_addr.s_addr = inet_addr(argv[1]), // IP地址
};
socklen_t serveraddr_len = sizeof(serveraddr);
char buf[128];
if (connect(sockfd, (struct sockaddr*)&serveraddr, serveraddr_len) == -1) {
ERRLOG("connect error");
}
printf("与服务器连接成功\n");
while (1) {
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
if (-1 == send(sockfd, buf, sizeof(buf), 0)) {
ERRLOG("send error");
}
int ret = recv(sockfd, buf, sizeof(buf), 0);
if (ret == -1) {
ERRLOG("recv error");
}
if (ret == 0) {//服务器断开连接
break;
}
printf("接收的数据%s\n", buf);
}
close(sockfd);
return 0;
}