服务端与客户端单连接
服务端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
void * thread_write(void *argv)
{
unsigned char * buf_sock_write;
buf_sock_write = (unsigned char *)malloc(30);
int client_fd = *((int *)argv);
//以ctrl+D为结束信号,结束本端的写线程
while (read(STDIN_FILENO,buf_sock_write,30))
{
send(client_fd,buf_sock_write,30,0);
memset(buf_sock_write,0,30);
}
printf("服务端收到关闭信号\n");
//关闭WR时,对端的recv会收到一个0字节的数据包,从而结束对端读线程,不同时关闭读端是为了接收对端的关闭信号
//本端关闭写端,但是仍然可以接收数据包
shutdown(client_fd,SHUT_WR);
free(buf_sock_write);
}
void * thread_read(void *argv)
{
unsigned char * buf_sock_read;
buf_sock_read = (unsigned char *)malloc(30);
int client_fd = *((int *)argv);
//对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程
while (recv(client_fd,buf_sock_read,30,0))
{
printf("%s",buf_sock_read);
memset(buf_sock_read,0,30);
}
printf("客户端断开连接\n");
free(buf_sock_read);
}
int main(int argc, char const *argv[])
{
int server_fd,tmp,client_fd;
//创建服务端,客户端ip地址结构体
struct sockaddr_in server,client;
memset(&server,0,sizeof(server));
memset(&client,0,sizeof(client));
//1,创建服务端套接字并返回文件描述符,家族选择AF_INET(IPV4),使用TCP就选择SOCK_STREAM,UDP就选择SOCK_DGRAM
server_fd = socket(AF_INET,SOCK_STREAM,0);
//2,设置服务端的ip和端口号
server.sin_family = AF_INET;
inet_pton(AF_INET,"0.0.0.0",&server.sin_addr);
server.sin_port = htons(8888);
//3,将套接字绑定地址,我们使用的是IPV4专属的struct sockaddr_in结构体,需要强转为专用的struct sockaddr *
tmp = bind(server_fd,(struct sockaddr *)&server,sizeof(server));
handle_error("bind",tmp);
//4,服务端进入监听模式,随时准备接受连接
tmp = listen(server_fd,128);
handle_error("listen",tmp);
//5,服务端接受客户端的连接,同时会获取到客户端套接字的文件描述符和ip地址等信息,通过文件描述符和客户端进行通信
socklen_t client_addr_len;
client_fd = accept(server_fd,(struct sockaddr *)&client,&client_addr_len);
printf("客户端连接成功\n");
pthread_t thread_id_write;
pthread_t thread_id_read;
pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);
pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);
pthread_join(thread_id_write,NULL);
pthread_join(thread_id_read,NULL);
close(server_fd);
close(client_fd);
return 0;
}
客户端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
void * thread_write(void *argv)
{
int client_fd = *((int *)argv);
unsigned char * buf_sock_write = (unsigned char *)malloc(30);
//客户端同样以ctrl+D为关闭信号,关闭写端
while (read(STDIN_FILENO,buf_sock_write,30))
{
send(client_fd,buf_sock_write,30,0);
memset(buf_sock_write,0,30);
}
printf("客户端收到关闭信号\n");
//关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号
//本端关闭写端,但是仍然可以接收数据包
shutdown(client_fd,SHUT_WR);
free(buf_sock_write);
}
void * thread_read(void *argv)
{
int client_fd = *((int *)argv);
unsigned char * buf_sock_read = (unsigned char *)malloc(30);
//对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程
while (recv(client_fd,buf_sock_read,30,0))
{
printf("%s",buf_sock_read);
memset(buf_sock_read,0,30);
}
printf("服务端关闭连接\n");
free(buf_sock_read);
}
int main(int argc, char const *argv[])
{
int client_fd,tmp;
struct sockaddr_in server_addr,client_addr;
memset(&server_addr,0,sizeof(server_addr));
memset(&client_addr,0,sizeof(client_addr));
//1,创建套接字
client_fd = socket(AF_INET,SOCK_STREAM,0);
handle_error("socket",client_fd);
//2,设置服务端的ip地址和端口号
inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);//端口号要和服务端设置的相匹配
//3,绑定套接字,客户端可以跳过本步骤,会自动分配ip地址和端口号
inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr);
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(6666);//要避免使用和客户端相同的端口,因为是本机通信,端口不能被多个进程同时使用
tmp = bind(client_fd,(struct sockaddr *)&client_addr,sizeof(client_addr));
handle_error("bind",tmp);
//4,连接服务端
tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
handle_error("connect",tmp);
printf("成功连接到服务端\n");
pthread_t thread_id_write;
pthread_t thread_id_read;
pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);
pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);
pthread_join(thread_id_write,NULL);
pthread_join(thread_id_read,NULL);
close(client_fd);
return 0;
}
过程及结果解释
客户端和服务端代码均使用多线程,分别为读写线程,均通过终端接收数据并向对端发送数据,接收到对端的数据之后就会打印在终端上。在服务端/客户端关闭本端的写功能之后,都可以继续接收对端的数据,但是无法发送数据。
在两个终端中分别运行服务端和客户端代码,要先运行服务端代码,不然客户端会连接不到。
服务端发送一次hello csdn,客户端发送一次hello wrold
关闭服务端,客户端继续发送数据
关闭客户端
服务端基于多线程的支持多个客户端连接
服务端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
void *thread_recv_send(void *argv)
{
int client_fd;
ssize_t cnt;
client_fd = *((int *)argv);
char *buf = (char *)malloc(100);
while (cnt = recv(client_fd,buf,100,0))
{
handle_error("recv",cnt);
cnt = send(client_fd,buf,cnt,0);
handle_error("send",cnt);
printf("客户端%d发来消息,内容为%s",client_fd,buf);
memset(buf,0,100);
}
shutdown(client_fd,SHUT_WR);
printf("有客户端断开连接,文件描述符为%d\n",client_fd);
free(buf);
close(client_fd);
}
int main(int argc, char const *argv[])
{
int server_fd,client_fd,tmp;
struct sockaddr_in server_addr,client_addr;
//创建服务端套接字
server_fd = socket(AF_INET,SOCK_STREAM,0);
handle_error("socket",server_fd);
//设置服务端ip地址和端口号
server_addr.sin_family = AF_INET;
inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
server_addr.sin_port = htons(8888);
//绑定服务端套接字
tmp = bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
handle_error("bind",tmp);
tmp = listen(server_fd,128);
handle_error("listen",tmp);
while (1)
{
socklen_t sock_len;
client_fd = accept(server_fd,(struct sockaddr *)&client_addr,&sock_len);
handle_error("accept",client_fd);
printf("有客户端连接,ip地址为%s,端口号为%d,文件描述符为%d\n",
inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd);
pthread_t thread_id_sock;
pthread_create(&thread_id_sock,NULL,thread_recv_send,(void *)&client_fd);
pthread_detach(thread_id_sock);
}
close(server_fd);
return 0;
}
客户端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
void * thread_write(void *argv)
{
int client_fd = *((int *)argv);
unsigned char * buf_sock_write = (unsigned char *)malloc(30);
//客户端同样以ctrl+D为关闭信号,关闭写端
while (read(STDIN_FILENO,buf_sock_write,30))
{
send(client_fd,buf_sock_write,30,0);
memset(buf_sock_write,0,30);
}
printf("客户端收到关闭信号\n");
//关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号
//本端关闭写端,但是仍然可以接收数据包
shutdown(client_fd,SHUT_WR);
free(buf_sock_write);
}
void * thread_read(void *argv)
{
int client_fd = *((int *)argv);
unsigned char * buf_sock_read = (unsigned char *)malloc(30);
//对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程
while (recv(client_fd,buf_sock_read,30,0))
{
printf("%s",buf_sock_read);
memset(buf_sock_read,0,30);
}
printf("服务端关闭连接\n");
free(buf_sock_read);
}
int main(int argc, char const *argv[])
{
int client_fd,tmp;
struct sockaddr_in server_addr,client_addr;
memset(&server_addr,0,sizeof(server_addr));
memset(&client_addr,0,sizeof(client_addr));
//1,创建套接字
client_fd = socket(AF_INET,SOCK_STREAM,0);
handle_error("socket",client_fd);
//2,设置服务端的ip地址和端口号
inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);//端口号要和服务端设置的相匹配
//3,绑定套接字,客户端可以跳过本步骤,会自动分配ip地址和端口号,如果固定端口号的话
//多个线程无法共用一个端口,导致无法创建多连接
/*inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr);
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(6666);//要避免使用和客户端相同的端口,因为是本机通信,端口不能被多个进程同时使用
tmp = bind(client_fd,(struct sockaddr *)&client_addr,sizeof(client_addr));
handle_error("bind",tmp);*/
//4,连接服务端
tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
handle_error("connect",tmp);
printf("成功连接到服务端\n");
pthread_t thread_id_write;
pthread_t thread_id_read;
pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);
pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);
pthread_join(thread_id_write,NULL);
pthread_join(thread_id_read,NULL);
close(client_fd);
return 0;
}
过程及结果解释
客户端的代码几乎没有变化,只是取消了ip地址、端口号与套接字的绑定,因为客户端会默认设置本机的ip地址,并选择一个空闲端口使用,所以无需进行绑定。相反如果固定了端口号,因为端口号不能被多个进程共用,反而会导致无法创建多连接。
服务端的接受连接的逻辑与之前相同,在每次接受连接之后都会创建一个新线程并传入服务端的套接字,并将线程设置为detach状态,使其在结束之后自动回收资源,不使用join,从而不会阻挡创立新连接。在新线程中维护与客户端的连接并进行数据传输,将服务端接收到的消息发送回客户端并打印到终端上。当客户端从终端中接收了ctrl+D之后,客户端退出,调用shutdown会向服务端发送挥手信号,此时recv接收到的数据长度为0,服务端也随之退出。
打开三个终端,在终端1中运行服务端,终端2,3运行客户端
终端2,3分别发送消息
终端2,3退出
可以看到此时客户端已经全部退出,服务端仍然在接收连接
终端2,3继续运行客户端,与服务端建立连接
服务端基于多进程的支持客户端多连接
服务端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
void singal_handle(int sig)
{
while (waitpid(-1,NULL,WNOHANG))
{
}
}
void recv_send(int client_fd,struct sockaddr_in * client_addr)
{
ssize_t cnt;
char *buf = malloc(30);
while (cnt = recv(client_fd,buf,30,0))
{
send(client_fd,buf,cnt,0);
printf("客户端%d发来消息,内容为%s",ntohs(client_addr->sin_port),buf);
memset(buf,0,30);
}
printf("客户端%d断开连接\n",client_fd);
shutdown(client_fd,SHUT_WR);
free(buf);
}
int main(int argc, char const *argv[])
{
int server_fd,client_fd,tmp;
struct sockaddr_in server_addr,client_addr;
server_fd = socket(AF_INET,SOCK_STREAM,0);
handle_error("socket",server_fd);
server_addr.sin_family = AF_INET;
inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
server_addr.sin_port = htons(8888);
tmp = bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
handle_error("bind",tmp);
tmp = listen(server_fd,128);
handle_error("listen",tmp);
//注册信号处理函数,调用waitpid回收子进程防止成为僵尸进程
signal(SIGCHLD,singal_handle);
while (1)
{
socklen_t client_len;
client_fd = accept(server_fd,(struct sockaddr*)&client_addr,&client_len);
handle_error("accept",client_fd);
printf("有客户端建立连接,ip地址为%s,端口号为%d,文件描述符为%d\n",
inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd);
pid_t pid = fork();
if(pid == 0)//子进程
{
//子进程不需要使用服务端描述符
close(server_fd);
recv_send(client_fd,&client_addr);
close(client_fd);
exit(EXIT_SUCCESS);
}
else if(pid > 0)
{
//父进程不需要使用客户端描述符
close(client_fd);
}
}
return 0;
}
客户端代码
客户端代码和多线程的相同。
运行结果及过程解释
服务端通过fork创建多个进程来维护与客户端的连接,在子进程中执行数据传输,父进程阻塞等待客户端的连接。子进程在客户端断开之后调用exit退出进程,向父进程发送SIGCHLD信号,父进程通过注册信号处理函数调用waitpid来回收每一个子进程,防止出现僵尸进程。
程序的运行结果和多线程的基本相同。开启三个终端,终端1执行服务端,2,3执行客户端并发送消息,之后关闭客户端
和多线程不同的是,我们发现两个客户端的文件描述符是相同的,这是因为accept在父进程进行,数据传输的任务在子进程执行,父进程不需要使用客户端的文件描述符,所以已经将其关闭了,进而导致下一次返回的文件描述符相同。
UDP的数据传输
UDP通讯也使用socket,但是接收和发送的函数与TCP不一样。由于UDP不存在握手这一步骤,所以在绑定地址之后,服务端不需要listen,客户端也不需要connect,服务端同样不需要accept。只要服务端绑定以后,就可以相互发消息了,由于没有握手过程,两端都不能确定对方是否收到消息,这也是UDP协议不如TCP协议可靠的地方。
UDP的通讯流程相比之下就简单了很多,TCP是通过accept和connect绑定文件描述符进行通信,UDP则是通过目标地址的ip和端口号来通信,使用的是struct sockaddr结构体的信息。在接收对方的数据之后,就可以获得对方的ip地址和端口号等信息,进而向对方回传数据。
服务端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
int main(int argc, char const *argv[])
{
//UDP通讯过程中只涉及一个套接字就是本端套接字
int sock_fd,tmp;
char buf[30];
struct sockaddr_in server_addr,client_addr;
socklen_t client_len = sizeof(client_addr);
sock_fd = socket(AF_INET,SOCK_DGRAM,0);
handle_error("sock",sock_fd);
server_addr.sin_family = AF_INET;
inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
server_addr.sin_port = htons(8888);
tmp = bind(sock_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
handle_error("bind",tmp);
ssize_t cnt = 0;
while (cnt = recvfrom(sock_fd,buf,30,0,(struct sockaddr *)&client_addr,&client_len))
{
printf("%s",buf);
sendto(sock_fd,buf,30,0,(struct sockaddr *)&client_addr,client_len);
memset(buf,0,30);
}
printf("客户端退出\n");
return 0;
}
客户端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
int main(int argc, char const *argv[])
{
int sock_fd,tmp;
sock_fd = socket(AF_INET,SOCK_DGRAM,0);
handle_error("socket",sock_fd);
struct sockaddr_in server_addr;
socklen_t server_len;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
char buf[30];
while (read(STDIN_FILENO,buf,30))
{
sendto(sock_fd,buf,30,0,(struct sockaddr *)&server_addr,sizeof(server_addr));
memset(buf,0,30);
recvfrom(sock_fd,buf,30,0,(struct sockaddr *)&server_addr,&server_len);
printf("%s",buf);
memset(buf,0,30);
}
sendto(sock_fd,"",0,0,(struct sockaddr *)&server_addr,sizeof(server_addr));
return 0;
}
客户端必须先向服务端发送消息,之后服务端才可以获取到客户端的ip地址和端口号。运行结果如下
套接字用于进程间通信
Socket编程原本是为了网络服务的,后来逐渐发展成一种进程间通信的方式:Unix Domain Socket IPC。它允许在同一台主机上运行的进程之间进行高效的数据传输,无需经过网络协议栈,因此具有低延迟和高性能的特点。通过文件系统中的特殊文件(通常是一个套接字文件),进程可以通过套接字(socket)来进行通信,实现双向的数据传输。
和网络通信不同的是,UNIX通信域使用的结构体是struct sockaddr_un,结构体的sun_family固定使用AF_UNIX,第二个成员变为了套接字的地址,也就是声明一个使用本地文件的socket,其他进程可以绑定该文件完成进程间通信。在使用完成之后,服务端需要调用unlink清除该套接字文件,否则下次进行绑定时会提示该地址已经被使用。除此之外,服务端和客户端的程序编写流程和TCP并无区别。
服务端代码
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
void *thread_recv_send(void *argv)
{
int client_fd;
ssize_t cnt;
client_fd = *((int *)argv);
char *buf = (char *)malloc(100);
while (cnt = recv(client_fd,buf,100,0))
{
handle_error("recv",cnt);
cnt = send(client_fd,buf,cnt,0);
handle_error("send",cnt);
printf("客户端%d发来消息,内容为%s",client_fd,buf);
memset(buf,0,100);
}
shutdown(client_fd,SHUT_WR);
printf("有客户端断开连接,文件描述符为%d\n",client_fd);
free(buf);
close(client_fd);
}
int main(int argc, char const *argv[])
{
int server_fd,client_fd,tmp;
struct sockaddr_un server_addr,client_addr;
//创建套接字
server_fd = socket(AF_UNIX,SOCK_STREAM,0);
handle_error("socket",server_fd);
//设置地址
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path,"test.socket");
//绑定套接字
tmp = bind(server_fd,(struct sockaddr*)&server_addr,sizeof(server_addr));
handle_error("bind",tmp);
tmp = listen(server_fd,128);
handle_error("listen",tmp);
while (1)
{
socklen_t client_len = sizeof(client_addr);
client_fd = accept(server_fd,(struct sockaddr*)&client_addr,&client_len);
handle_error("accept",client_fd);
printf("有客户端连接,文件描述符为%d\n",client_fd);
pthread_t thread_id_sock;
pthread_create(&thread_id_sock,NULL,thread_recv_send,(void *)&client_fd);
pthread_detach(thread_id_sock);
}
unlink("test.socket");
return 0;
}
客户端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#define handle_error(cmd,result) \
if(result < 0) \
{ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
void * thread_write(void *argv)
{
int client_fd = *((int *)argv);
unsigned char * buf_sock_write = (unsigned char *)malloc(30);
//客户端同样以ctrl+D为关闭信号,关闭写端
while (read(STDIN_FILENO,buf_sock_write,30))
{
send(client_fd,buf_sock_write,30,0);
memset(buf_sock_write,0,30);
}
printf("客户端收到关闭信号\n");
//关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号
//本端关闭写端,但是仍然可以接收数据包
shutdown(client_fd,SHUT_WR);
free(buf_sock_write);
}
void * thread_read(void *argv)
{
int client_fd = *((int *)argv);
unsigned char * buf_sock_read = (unsigned char *)malloc(30);
//对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程
while (recv(client_fd,buf_sock_read,30,0))
{
printf("%s",buf_sock_read);
memset(buf_sock_read,0,30);
}
printf("服务端关闭连接\n");
free(buf_sock_read);
}
int main(int argc, char const *argv[])
{
int client_fd,tmp;
struct sockaddr_un server_addr,client_addr;
memset(&server_addr,0,sizeof(server_addr));
memset(&client_addr,0,sizeof(client_addr));
//创建套接字
client_fd = socket(AF_UNIX,SOCK_STREAM,0);
handle_error("socket",client_fd);
//设置服务端的地址
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path,"test.socket");
//连接服务端
tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
handle_error("connect",tmp);
printf("成功连接到服务端\n");
pthread_t thread_id_write;
pthread_t thread_id_read;
pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);
pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);
pthread_join(thread_id_write,NULL);
pthread_join(thread_id_read,NULL);
close(client_fd);
return 0;
}
过程解释及运行结果
服务端仍然采用多线程方式支持多个连接,接收客户端数据之后打印在终端并传回客户端。
总结一下,套接字用于进程间通信时,类似于TCP协议下的编程顺序,但是套接字地址结构体变为了struct sockaddr_un 其中的family写为AF_UNIX,第二个成员改为了路径,客户端服务端的路径要一致。创建套接字的第二个参数还是sock_stream和TCP一样。服务端的编程顺序是初始化地址,给地址赋值,创建套接字,绑定套接字,进入监听模式,接受连接(会返回客户端的套接字和地址),发送/接收数据,unlink释放套接字。客户端的编程顺序是初始化地址,给地址赋值,创建套接字,连接客户端,发送/接收数据。