Due to scheduling, the client could attempt to connect to the server before the server starts to accept. Make the client retry multiple times before returning with error.
251 lines
6.3 KiB
C
251 lines
6.3 KiB
C
#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <spawn.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "test.h"
|
|
|
|
#define RESPONSE "ACK"
|
|
#define DEFAULT_MSG "Hello World!\n"
|
|
|
|
int connect_with_server(const char *addr_string, const char *port_string) {
|
|
//"NULL" addr means connectionless, no need to connect to server
|
|
if (strcmp(addr_string, "NULL") == 0) {
|
|
return 0;
|
|
}
|
|
|
|
int ret = 0;
|
|
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sockfd < 0) {
|
|
THROW_ERROR("create socket error");
|
|
}
|
|
|
|
struct sockaddr_in servaddr;
|
|
memset(&servaddr, 0, sizeof(servaddr));
|
|
servaddr.sin_family = AF_INET;
|
|
servaddr.sin_port = htons((uint16_t)strtol(port_string, NULL, 10));
|
|
ret = inet_pton(AF_INET, addr_string, &servaddr.sin_addr);
|
|
if (ret <= 0) {
|
|
close(sockfd);
|
|
THROW_ERROR("inet_pton error");
|
|
}
|
|
|
|
int retry_num = 5;
|
|
while (retry_num > 0) {
|
|
ret = connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
|
|
if (ret == 0) {
|
|
break;
|
|
}
|
|
if (ret < 0 && errno == ECONNREFUSED) {
|
|
// The server maynot be ready to accept. Try again later.
|
|
sleep(1);
|
|
retry_num--;
|
|
continue;
|
|
} else {
|
|
close(sockfd);
|
|
THROW_ERROR("connect error");
|
|
}
|
|
}
|
|
|
|
if (retry_num == 0) {
|
|
close(sockfd);
|
|
THROW_ERROR("Retry connect multiple times. All failure");
|
|
}
|
|
|
|
printf("connected with server");
|
|
return sockfd;
|
|
}
|
|
|
|
int neogotiate_msg(int server_fd, char *buf, int buf_size) {
|
|
if (read(server_fd, buf, buf_size) < 0) {
|
|
THROW_ERROR("read failed");
|
|
}
|
|
|
|
if (write(server_fd, RESPONSE, sizeof(RESPONSE)) < 0) {
|
|
THROW_ERROR("write failed");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int client_send(int server_fd, char *buf) {
|
|
if (send(server_fd, buf, strlen(buf), 0) < 0) {
|
|
THROW_ERROR("send msg error");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int client_sendmsg(int server_fd, char *buf) {
|
|
int ret = 0;
|
|
struct msghdr msg;
|
|
struct iovec iov[1];
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
iov[0].iov_base = buf;
|
|
iov[0].iov_len = strlen(buf);
|
|
msg.msg_iov = iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = 0;
|
|
msg.msg_controllen = 0;
|
|
msg.msg_flags = 0;
|
|
|
|
ret = sendmsg(server_fd, &msg, 0);
|
|
if (ret <= 0) {
|
|
THROW_ERROR("sendmsg failed");
|
|
}
|
|
|
|
msg.msg_iov = NULL;
|
|
msg.msg_iovlen = 0;
|
|
|
|
ret = sendmsg(server_fd, &msg, 0);
|
|
if (ret != 0) {
|
|
THROW_ERROR("empty sendmsg failed");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifdef __GLIBC__
|
|
|
|
int client_sendmmsg(int server_fd, char *buf) {
|
|
int ret = 0;
|
|
struct mmsghdr msg_v[2] = {};
|
|
struct iovec iov[1];
|
|
struct msghdr *msg_ptr = &msg_v[0].msg_hdr;
|
|
|
|
// Set msg0
|
|
msg_ptr->msg_name = NULL;
|
|
msg_ptr->msg_namelen = 0;
|
|
iov[0].iov_base = buf;
|
|
iov[0].iov_len = strlen(buf);
|
|
msg_ptr->msg_iov = iov;
|
|
msg_ptr->msg_iovlen = 1;
|
|
msg_ptr->msg_control = 0;
|
|
msg_ptr->msg_controllen = 0;
|
|
msg_ptr->msg_flags = 0;
|
|
|
|
// Set msg1
|
|
msg_v[1] = msg_v[0];
|
|
msg_ptr = &msg_v[1].msg_hdr;
|
|
msg_ptr->msg_iov = NULL;
|
|
msg_ptr->msg_iovlen = 0;
|
|
|
|
ret = sendmmsg(server_fd, msg_v, 2, 0);
|
|
if (ret != 2 || msg_v[0].msg_len <= 0 || msg_v[1].msg_len != 0) {
|
|
THROW_ERROR("sendmsg failed");
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int client_connectionless_sendmsg(char *buf) {
|
|
int ret = 0;
|
|
struct msghdr msg;
|
|
struct iovec iov[1];
|
|
struct sockaddr_in servaddr;
|
|
memset(&servaddr, 0, sizeof(servaddr));
|
|
|
|
servaddr.sin_family = AF_INET;
|
|
servaddr.sin_port = htons(9900);
|
|
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
msg.msg_name = &servaddr;
|
|
msg.msg_namelen = sizeof(servaddr);
|
|
iov[0].iov_base = buf;
|
|
iov[0].iov_len = strlen(buf);
|
|
msg.msg_iov = iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = 0;
|
|
msg.msg_controllen = 0;
|
|
msg.msg_flags = 0;
|
|
|
|
int server_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (server_fd < 0) {
|
|
THROW_ERROR("create socket error");
|
|
}
|
|
|
|
ret = sendmsg(server_fd, &msg, 0);
|
|
if (ret <= 0) {
|
|
THROW_ERROR("sendmsg failed");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int blocking_recvfrom(int server_fd, char *buf, int buf_size) {
|
|
int flags = fcntl(server_fd, F_GETFL, 0);
|
|
if (flags == -1) {
|
|
THROW_ERROR("fnctl failed");
|
|
}
|
|
flags = flags & ~O_NONBLOCK;
|
|
fcntl(server_fd, F_SETFL, flags);
|
|
if (flags == -1) {
|
|
THROW_ERROR("fnctl failed");
|
|
}
|
|
|
|
// wait for server to exit and the remote end is closed
|
|
sleep(1);
|
|
printf("client blocking recvfrom\n");
|
|
int ret = recvfrom(server_fd, buf, buf_size, 0, NULL, 0);
|
|
if (ret >= 0 || errno != ECONNRESET) {
|
|
THROW_ERROR("recvfrom failed");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, const char *argv[]) {
|
|
if (argc != 3) {
|
|
THROW_ERROR("usage: ./client <ipaddress> <port>\n");
|
|
}
|
|
|
|
int ret = 0;
|
|
const int buf_size = 100;
|
|
char buf[buf_size];
|
|
int port = strtol(argv[2], NULL, 10);
|
|
int server_fd = connect_with_server(argv[1], argv[2]);
|
|
|
|
switch (port) {
|
|
case 8800:
|
|
neogotiate_msg(server_fd, buf, buf_size);
|
|
break;
|
|
case 8801:
|
|
neogotiate_msg(server_fd, buf, buf_size);
|
|
ret = client_send(server_fd, buf);
|
|
break;
|
|
case 8802:
|
|
neogotiate_msg(server_fd, buf, buf_size);
|
|
ret = client_sendmsg(server_fd, buf);
|
|
break;
|
|
#ifdef __GLIBC__
|
|
case 8803:
|
|
neogotiate_msg(server_fd, buf, buf_size);
|
|
ret = client_sendmmsg(server_fd, buf);
|
|
#endif
|
|
case 8804:
|
|
ret = client_connectionless_sendmsg(DEFAULT_MSG);
|
|
break;
|
|
case 8888:
|
|
neogotiate_msg(server_fd, buf, buf_size);
|
|
ret = client_sendmsg(server_fd, buf);
|
|
if (ret < 0) {
|
|
THROW_ERROR("client sendmsg failed");
|
|
}
|
|
ret = blocking_recvfrom(server_fd, buf, buf_size);
|
|
if (ret < 0) {
|
|
THROW_ERROR("client blocking recvfrom failed");
|
|
}
|
|
break;
|
|
default:
|
|
ret = client_send(server_fd, DEFAULT_MSG);
|
|
}
|
|
|
|
close(server_fd);
|
|
return ret;
|
|
}
|