简易的c/s端对端通信工具
将接收信息(recv或read)放在死循环中,没收到信息则阻塞,而只要对端有信息发送则会结束阻塞,将信息输出后自动再阻塞;因此,得再加一个线程,来获取键盘的输入流后进行信息发送。
代码中已有相应注释,修改客户端要连接的服务端IP地址,运行服务端后再运行客户端即可连接。
pthread_create(&stPid, NULL, tcp_connect_recv, NULL);
中的tcp_connect_recv
改成udp_connect_recv
,即为udp模式。udp模式下没办法像tcp一样事先通过accept
来获取客户端IP地址和端口,必须接收信息时才获取,所以在收到信息拿到stRemoteAddr
后再开启输入信息的线程工作,并且参数是指针传参。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 23 // 端口号
#define BACKLOG 5 // 最大监听数
char send_buf[4096] = {0};
typedef struct st_tcp_args_t {
int new_fd;
} tcp_args_t;
void * tcp_connect_send(void *args)
{
tcp_args_t *tcp_arg = (tcp_args_t *)args;
while (1) {
scanf("%s", send_buf);
// 发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
// write和send两种方式都可以
write(tcp_arg->new_fd, send_buf, sizeof(send_buf));
//send(tcp_arg->new_fd, send_buf, sizeof(send_buf), 0);
}
return NULL;
}
void * tcp_connect_recv(void *args)
{
int iSocketFD = 0; // socket句柄
// SOCK_STREAM流式套接字,表示tcp
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); // 建立socket
if (iSocketFD < 0) {
printf("创建socket失败:%d\n", errno);
return 0;
}
struct sockaddr_in stLocalAddr = {0}; // 本地地址信息结构图,下面有具体的属性赋值
stLocalAddr.sin_family = AF_INET; // 该属性表示接收本机或其他机器传输
stLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP,INADDR_ANY表示本机IP
stLocalAddr.sin_port = htons(PORT); // 端口号
int flag = 1;
if (setsockopt(iSocketFD, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) {
printf("设置socket选项失败:%d\n", errno);
close(iSocketFD);
}
// 绑定地址结构体和socket
if (bind(iSocketFD, (struct sockaddr *)&stLocalAddr, sizeof(stLocalAddr)) < 0) {
printf("绑定socket失败:%d\n", errno);
close(iSocketFD);
return 0;
}
// 开启监听,第二个参数是最大监听数
if (listen(iSocketFD, BACKLOG) < 0) {
printf("监听socket失败:%d\n", errno);
shutdown(iSocketFD, SHUT_RDWR);
close(iSocketFD);
return 0;
}
printf("开启监听, 监听的tcp socket句柄为:%d\n", iSocketFD);
int new_fd = 0; // 建立连接后的句柄
struct sockaddr_in stRemoteAddr = {0}; // 保存客户端地址信息
socklen_t socklen = sizeof(stRemoteAddr); // 千万不能为0,否则获取不到stRemoteAddr
// 开启accept后会阻塞直到接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
new_fd = accept(iSocketFD, (struct sockaddr *)&stRemoteAddr, &socklen);
if (new_fd < 0) {
printf("接收信息失败:%d\n", errno);
shutdown(iSocketFD, SHUT_RDWR);
close(iSocketFD);
return 0;
} else {
printf("接收成功!连接句柄为:%d\n", new_fd);
printf("确认客户端ip addr is %s\n", inet_ntoa(stRemoteAddr.sin_addr));
printf("确认客户端port:%d \n", ntohs(stRemoteAddr.sin_port));
strncpy(send_buf, "服务器确认连接成功!", sizeof("服务器确认连接成功!"));
send(new_fd, send_buf, sizeof(send_buf), 0);
}
pthread_t child_tid = 0;
tcp_args_t tcp_arg;
tcp_arg.new_fd = new_fd;
pthread_create(&child_tid, NULL, tcp_connect_send, (void *)&tcp_arg);
pthread_detach(child_tid);
int iRecvLen = 0; // 接收成功后的返回值
char recv_buf[4096] = {0};
while (1) {
// recv和read两种方式都可以
//iRecvLen = recv(new_fd, recv_buf, sizeof(recv_buf), 0);
iRecvLen = read(new_fd, recv_buf, sizeof(recv_buf));
// 对端关闭连接 返回0
if (iRecvLen <= 0) {
printf("接收失败或者对端关闭连接:%d\n", errno);
shutdown(iSocketFD, SHUT_RDWR);
close(new_fd);
close(iSocketFD);
return 0;
} else {
printf("收到客户端信息:%s\n", recv_buf);
printf("收到客户端ip addr is %s\n", inet_ntoa(stRemoteAddr.sin_addr));
printf("收到客户端port:%d \n", ntohs(stRemoteAddr.sin_port));
}
}
shutdown(iSocketFD, SHUT_RDWR);
close(new_fd);
close(iSocketFD);
return NULL;
}
typedef struct st_udp_args_t {
int iSocketFD;
// udp过程客户端可能会更新端口号等,需要使用指针
struct sockaddr_in *stRemoteAddr;
} udp_args_t;
void * udp_connect_send(void *args)
{
printf("收到客户端地址信息,服务端可以开始回信了!\n");
udp_args_t *udp_arg = (udp_args_t *)args;
while (1) {
scanf("%s", send_buf);
printf("udp_arg->iSocketFD %d\n", udp_arg->iSocketFD);
sendto(udp_arg->iSocketFD, send_buf, sizeof(send_buf), 0, (struct sockaddr *)(udp_arg->stRemoteAddr), sizeof(*(udp_arg->stRemoteAddr)));
printf("发送客户端ip addr is %s\n", inet_ntoa(udp_arg->stRemoteAddr->sin_addr));
printf("发送客户端port:%d \n", ntohs(udp_arg->stRemoteAddr->sin_port));
}
return NULL;
}
void * udp_connect_recv(void *args)
{
int iSocketFD = 0; // socket句柄
// SOCK_DGRAM数据报式套接字,表示udp
iSocketFD = socket(AF_INET, SOCK_DGRAM, 0); // 建立socket
if (iSocketFD < 0) {
printf("创建socket失败:%d\n", errno);
return 0;
}
struct sockaddr_in stLocalAddr = {0}; // 本地地址信息结构图,下面有具体的属性赋值
stLocalAddr.sin_family = AF_INET; // 该属性表示接收本机或其他机器传输
stLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP,INADDR_ANY表示本机IP
stLocalAddr.sin_port = htons(PORT); // 端口号
// 绑定地址结构体和socket
if (bind(iSocketFD, (struct sockaddr *)&stLocalAddr, sizeof(stLocalAddr)) < 0) {
printf("绑定socket失败:%d\n", errno);
close(iSocketFD);
return 0;
}
printf("绑定udp socket成功,socket句柄为:%d,开始接收消息。。。\n", iSocketFD);
struct sockaddr_in stRemoteAddr = {0}; // 保存客户端地址信息
int iRecvLen = 0; // 接收成功后的返回值
char recv_buf[4096] = {0};
socklen_t socklen = sizeof(stRemoteAddr);
int is_first = 1;
while (1) {
iRecvLen = recvfrom(iSocketFD, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&stRemoteAddr, &socklen);
if (iRecvLen <= 0) {
printf("接收失败或者对端关闭连接:%d\n", errno);
shutdown(iSocketFD, SHUT_RDWR);
close(iSocketFD);
return 0;
} else {
printf("收到客户端信息:%s\n", recv_buf);
printf("收到客户端ip addr is %s\n", inet_ntoa(stRemoteAddr.sin_addr));
printf("收到客户端port:%d \n", ntohs(stRemoteAddr.sin_port));
}
// 首次收到客户端udp报文后才能拿到客户端ip地址和端口,才能有回信条件
if (is_first == 1) {
pthread_t child_tid = 0;
udp_args_t udp_arg;
udp_arg.iSocketFD = iSocketFD;
udp_arg.stRemoteAddr = &stRemoteAddr;
pthread_create(&child_tid, NULL, udp_connect_send, (void *)&udp_arg);
pthread_detach(child_tid);
is_first = 0;
}
}
shutdown(iSocketFD, SHUT_RDWR);
close(iSocketFD);
return NULL;
}
int main(void)
{
pthread_t stPid = 0;
pthread_create(&stPid, NULL, tcp_connect_recv, NULL);
pthread_join(stPid, NULL);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ADDR "172.200.48.54" // 目标地址IP
#define PORT 23 //目标地址端口号
char send_buf[4096] = {0};
typedef struct st_tcp_args_t {
int iSocketFD;
} tcp_args_t;
void * tcp_connect_send(void *args)
{
tcp_args_t *tcp_arg = (tcp_args_t *)args;
while (1) {
scanf("%s", send_buf);
// 发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
// write和send两种方式都可以
write(tcp_arg->iSocketFD, send_buf, sizeof(send_buf));
//send(tcp_arg->iSocketFD, send_buf, sizeof(send_buf), 0);
}
return NULL;
}
void * tcp_connect_recv(void *args)
{
int iSocketFD = 0; // socket句柄
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); // 建立socket
if (iSocketFD < 0) {
printf("创建socket失败:%d\n", errno);
return 0;
}
unsigned int iRemoteAddr = 0;
struct sockaddr_in stRemoteAddr = {0}; // 服务端地址信息
stRemoteAddr.sin_family = AF_INET;
inet_pton(AF_INET, ADDR, &iRemoteAddr);
stRemoteAddr.sin_addr.s_addr = iRemoteAddr;
stRemoteAddr.sin_port = htons(PORT);
// 连接方法:传入句柄,目标地址,和大小
if (connect(iSocketFD, (struct sockaddr *)&stRemoteAddr, sizeof(stRemoteAddr)) < 0) {
printf("连接socket失败:%d\n", errno);
close(iSocketFD);
return 0;
}
printf("连接服务端socket成功!\n");
pthread_t child_tid = 0;
tcp_args_t tcp_arg;
tcp_arg.iSocketFD = iSocketFD;
pthread_create(&child_tid, NULL, tcp_connect_send, (void *)&tcp_arg);
pthread_detach(child_tid);
int iRecvLen = 0; // 接收成功后的返回值
char recv_buf[4096] = {0};
while (1) {
// recv和read两种方式都可以
//iRecvLen = recv(iSocketFD, recv_buf, sizeof(recv_buf), 0);
iRecvLen = read(iSocketFD, recv_buf, sizeof(recv_buf));
// 对端关闭连接 返回0
if (iRecvLen <= 0) {
printf("接收失败或者对端关闭连接:%d\n", errno);
close(iSocketFD);
return 0;
} else {
printf("收到服务端信息:%s\n", recv_buf);
}
}
close(iSocketFD);
return NULL;
}
typedef struct st_udp_args_t {
int iSocketFD;
struct sockaddr_in stRemoteAddr;
} udp_args_t;
void * udp_connect_send(void *args)
{
udp_args_t *udp_arg = (udp_args_t *)args;
while (1) {
scanf("%s", send_buf);
sendto(udp_arg->iSocketFD, send_buf, sizeof(send_buf), 0, (struct sockaddr *)&(udp_arg->stRemoteAddr), sizeof(udp_arg->stRemoteAddr));
printf("服务端ip addr is %s\n", inet_ntoa(udp_arg->stRemoteAddr.sin_addr));
printf("服务端port:%d \n", ntohs(udp_arg->stRemoteAddr.sin_port));
}
return NULL;
}
void * udp_connect_recv(void *args)
{
int iSocketFD = 0; // socket句柄
// SOCK_DGRAM数据报式套接字,表示udp
iSocketFD = socket(AF_INET, SOCK_DGRAM, 0); // 建立socket
if (iSocketFD < 0) {
printf("创建socket失败:%d\n", errno);
return 0;
}
unsigned int iRemoteAddr = 0;
struct sockaddr_in stRemoteAddr = {0}; // 服务端地址信息
stRemoteAddr.sin_family = AF_INET;
inet_pton(AF_INET, ADDR, &iRemoteAddr);
stRemoteAddr.sin_addr.s_addr = iRemoteAddr;
stRemoteAddr.sin_port = htons(PORT);
pthread_t child_tid = 0;
udp_args_t udp_arg;
udp_arg.iSocketFD = iSocketFD;
udp_arg.stRemoteAddr = stRemoteAddr;
pthread_create(&child_tid, NULL, udp_connect_send, (void *)&udp_arg);
pthread_detach(child_tid);
int iRecvLen = 0; // 接收成功后的返回值
char recv_buf[4096] = {0};
socklen_t socklen = 0;
while (1) {
iRecvLen = recvfrom(iSocketFD, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&stRemoteAddr, &socklen);
if (iRecvLen <= 0) {
printf("接收失败或者对端关闭连接:%d\n", errno);
shutdown(iSocketFD, SHUT_RDWR);
close(iSocketFD);
return 0;
} else {
printf("收到服务端信息:%s\n", recv_buf);
}
}
close(iSocketFD);
return NULL;
}
int main(void)
{
pthread_t stPid = 0;
pthread_create(&stPid, NULL, tcp_connect_recv, NULL);
pthread_join(stPid, NULL);
return 0;
}
UDS(Unix domain socket)又叫 IPC socket,用于实现同一主机上的进程间通信。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BACKLOG 5 // 最大监听数
char *socket_path = "server.socket";
char send_buf[4096] = {0};
typedef struct st_uds_args_t {
int new_fd;
} uds_args_t;
void * uds_connect_send(void *args)
{
uds_args_t *uds_arg = (uds_args_t *)args;
while (1) {
scanf("%s", send_buf);
// 发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
send(uds_arg->new_fd, send_buf, sizeof(send_buf), 0);
}
return NULL;
}
void * uds_connect_recv(void *args)
{
int iSocketFD = 0; // socket句柄
// SOCK_STREAM流式套接字,表示tcp
iSocketFD = socket(AF_UNIX, SOCK_STREAM, 0); // 建立socket
if (iSocketFD < 0) {
printf("创建socket失败:%d\n", errno);
return 0;
}
// 网络编程的 socket 地址是 IP 地址加端口号,而 UNIX domain socket 的地址是一个 socket 类型的文件在文件系统中的路径
struct sockaddr_un stLocalAddr = {0};
// 使用 AF_UNIX 会在系统上创建一个 socket 文件,不同进程通过读写这个文件来实现通信。
stLocalAddr.sun_family = AF_UNIX;
// 指定uds路径
strncpy(stLocalAddr.sun_path, socket_path, strlen(socket_path) + 1);
// bind会创建uds文件,如果已存在会bind失败,所以得先删除
unlink(socket_path);
// 绑定地址结构体和socket
if (bind(iSocketFD, (struct sockaddr *)&stLocalAddr, sizeof(stLocalAddr)) < 0) {
printf("绑定socket失败:%d\n", errno);
close(iSocketFD);
return 0;
}
// 开启监听,第二个参数是最大监听数
if (listen(iSocketFD, BACKLOG) < 0) {
printf("监听socket失败:%d\n", errno);
shutdown(iSocketFD, SHUT_RDWR);
close(iSocketFD);
return 0;
}
printf("开启监听, 监听的uds socket句柄为:%d\n", iSocketFD);
int new_fd = 0; // 建立连接后的句柄
struct sockaddr_un stRemoteAddr = {0}; // 保存客户端地址信息
socklen_t socklen = sizeof(stRemoteAddr);
// 开启accept后会阻塞直到接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
new_fd = accept(iSocketFD, (struct sockaddr *)&stRemoteAddr, &socklen);
if (new_fd < 0) {
printf("接收信息失败:%d\n", errno);
shutdown(iSocketFD, SHUT_RDWR);
close(iSocketFD);
return 0;
} else {
printf("接收成功!连接句柄为:%d\n", new_fd);
strncpy(send_buf, "服务器确认连接成功!", sizeof("服务器确认连接成功!"));
send(new_fd, send_buf, sizeof(send_buf), 0);
}
pthread_t child_tid = 0;
uds_args_t uds_arg;
uds_arg.new_fd = new_fd;
pthread_create(&child_tid, NULL, uds_connect_send, (void *)&uds_arg);
pthread_detach(child_tid);
int iRecvLen = 0; // 接收成功后的返回值
char recv_buf[4096] = {0};
while (1) {
iRecvLen = recv(new_fd, recv_buf, sizeof(recv_buf), 0);
// 对端关闭连接 返回0
if (iRecvLen <= 0) {
printf("接收失败或者对端关闭连接:%d\n", errno);
shutdown(iSocketFD, SHUT_RDWR);
close(new_fd);
close(iSocketFD);
return 0;
} else {
printf("收到客户端信息:%s\n", recv_buf);
}
}
shutdown(iSocketFD, SHUT_RDWR);
close(new_fd);
close(iSocketFD);
return NULL;
}
int main(void)
{
pthread_t stPid = 0;
pthread_create(&stPid, NULL, uds_connect_recv, NULL);
pthread_join(stPid, NULL);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
char *server_path = "server.socket";
char send_buf[4096] = {0};
typedef struct st_uds_args_t {
int iSocketFD;
} uds_args_t;
void * uds_connect_send(void *args)
{
uds_args_t *uds_arg = (uds_args_t *)args;
while (1) {
scanf("%s", send_buf);
// 发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
// write和send两种方式都可以
write(uds_arg->iSocketFD, send_buf, sizeof(send_buf));
//send(uds_arg->iSocketFD, send_buf, sizeof(send_buf), 0);
}
return NULL;
}
void * uds_connect_recv(void *args)
{
int iSocketFD = 0; // socket句柄
iSocketFD = socket(AF_UNIX, SOCK_STREAM, 0); // 建立socket
if (iSocketFD < 0) {
printf("创建socket失败:%d\n", errno);
return 0;
}
unsigned int iRemoteAddr = 0;
struct sockaddr_un stRemoteAddr = {0}; // 服务端地址信息
stRemoteAddr.sun_family = AF_UNIX;
strncpy(stRemoteAddr.sun_path, server_path, strlen(server_path) + 1);
// 连接方法:传入句柄,目标地址,和大小
if (connect(iSocketFD, (struct sockaddr *)&stRemoteAddr, sizeof(stRemoteAddr)) < 0) {
printf("连接socket失败:%d\n", errno);
close(iSocketFD);
return 0;
}
printf("连接服务端socket成功!\n");
pthread_t child_tid = 0;
uds_args_t uds_arg;
uds_arg.iSocketFD = iSocketFD;
pthread_create(&child_tid, NULL, uds_connect_send, (void *)&uds_arg);
pthread_detach(child_tid);
int iRecvLen = 0; // 接收成功后的返回值
char recv_buf[4096] = {0};
while (1) {
// recv和read两种方式都可以
//iRecvLen = recv(iSocketFD, recv_buf, sizeof(recv_buf), 0);
iRecvLen = read(iSocketFD, recv_buf, sizeof(recv_buf));
// 对端关闭连接 返回0
if (iRecvLen <= 0) {
printf("接收失败或者对端关闭连接:%d\n", errno);
close(iSocketFD);
return 0;
} else {
printf("收到服务端信息:%s\n", recv_buf);
}
}
close(iSocketFD);
return NULL;
}
int main(void)
{
pthread_t stPid = 0;
pthread_create(&stPid, NULL, uds_connect_recv, NULL);
pthread_join(stPid, NULL);
return 0;
}