流程:
服务端等待连接,然后开启新线程处理这个连接,处理过程为:先读取一个整数,表示后续需要读的数据长度,然后继续读取这么长的数据,成功后返回一个整数,告诉客户端读到了多少数据。
客户端:主动连接,先发送一个整数,表示后序要发送的数据长度,然后发送这么多数据,然后读取服务器的相应数据(也是一个整数),判断相应是否和预期一致。
出现问题:在2台机器上测试,如果拔掉网线,超时时间总是我设定的2倍,偶尔也会出现3倍;
但是第一次出错后,后面如果继续发送数据,则在指定的超时时间出错返回。
(如果不是拔掉网线,而是直接终止程序,一切正常,总能立刻出错返回,和预期一样)
代码如下:
公共代码:
//#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <arpa/inet.h>
#ifndef _SOCKET_H
#define _SOCKET_H
const static int TIMEOUT = 5; /* second */
#define unlikely(x) (x)
static int connect_to(char *ip, unsigned int port)
{
int fd, ret, value = 1;
struct sockaddr_in addr;
struct linger linger_opt = {1, 0};
struct timeval timeout_opt = {TIMEOUT, 0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) {
ret = -1;
goto err;
}
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
ret = -1;
goto err;
}
ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_opt,
sizeof(linger_opt));
if (ret < 0)
goto err_close;
ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
if (ret < 0)
goto err_close;
ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout_opt,
sizeof(timeout_opt));
if (ret < 0)
goto err_close;
ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout_opt,
sizeof(timeout_opt));
if (ret < 0)
goto err_close;
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0)
goto err_close;
return fd;
err_close:
close(fd);
err:
fprintf(stderr, "Failed errno:%d(%m)\n", errno);
return ret;
}
static int64_t net_read(int fd, void *buf, int64_t count)
{
char *p = buf;
int64_t sum = 0;
while(count > 0) {
int64_t loaded = 0;
while(1) {
loaded = read(fd, p, count);
if (unlikely(loaded < 0) && (errno == EINTR))
continue;
break;
}
if (unlikely(loaded < 0))
return -1;
if (unlikely(loaded == 0))
return sum;
count -= loaded;
p += loaded;
sum += loaded;
}
return sum;
}
static int64_t net_write(int fd, void *buf, int64_t count)
{
char *p = buf;
int64_t sum = 0;
while (count > 0) {
int64_t written = 0;
while (1) {
written = write(fd, p, count);
if (unlikely(written < 0) && (errno == EINTR))
continue;
break;
}
if (unlikely(written < 0))
return -1;
if (unlikely(written == 0))
return -1;
count -= written;
p += written;
sum += written;
}
return sum;
}
#endif
服务端代码如下:
#include "htime.h"
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include "mysocket.h"
int init_server(int port);
void *recv_work(void *data);
void sig_handler(int signo);
int main(int argc, char *argv[])
{
if(argc != 2)
{
fprintf(stderr, "usage: server port\n");
return 0;
}
int fd = init_server(atoi(argv[1]));
if(fd < 0)
{
fprintf(stderr, "server start failed\n");
return 0;
}
int client;
while(1)
{
fprintf(stderr, "%s waiting for client...\n", gettime());
client = accept(fd, NULL, NULL);
if(client == -1)
{
fprintf(stderr, "%s accept failed : %m\n", gettime());
continue;
}
pthread_t pid;
pthread_create(&pid, NULL, recv_work, &client);
}
}
void *recv_work(void *data)
{
int fd = *(int*)data, ret, len=0;
fprintf(stderr, "client fd: %d\n", fd);
char *buf = NULL;
while(1)
{
ret = net_read(fd, &len, sizeof(int));
fprintf(stderr, "get info from client: %d(%d byte)[%m]\n", len, ret);
if(ret == 0) {
fprintf(stderr, "%s read 0, client seems to be dead...\n", gettime());
break;
} else if(ret < 0) {
fprintf(stderr, "ret=%d, errno=%d:%m\n",ret, errno);
if (errno == EINTR) continue;
if (errno == EAGAIN)
fprintf(stderr, "would block or time out\n");
} else {
buf = malloc(len); if(!buf) {fprintf(stderr, "OOM\n"); break;}
ret = net_read(fd, buf, len);
fprintf(stderr, "get data from client ret:%d ", ret);
if ( ret > 20 ) {
char t[20] = {};
memcpy(t, buf, 20);
fprintf(stderr, "(%s)\n", t);
}
ret = net_write(fd, &ret, sizeof(int));
// free(buf);
// buf = NULL;
}
}
fprintf(stderr, "close client fd: %d\n", fd);
pthread_detach(pthread_self());
pthread_exit(NULL);
}
void sig_handler(int signo)
{
fprintf(stderr, "receivec sig: %d\n", signo);
}
int init_server(int port)
{
struct sockaddr_in ser;
int fd, value = 1;
struct linger linger_opt = {1, 0};
ser.sin_family = AF_INET;
ser.sin_port = htons(port);
ser.sin_addr.s_addr = INADDR_ANY;
bzero(&(ser.sin_zero), 8);
fd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_opt,
sizeof(linger_opt));
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
if(bind(fd, (struct sockaddr*)&ser, sizeof(struct sockaddr))== -1)
{
fprintf(stderr, "bind error\n");
return -1;
}
if (listen(fd, 20) == -1){
fprintf(stderr, "listening failed\n");
return -1;
}
signal(SIGPIPE, sig_handler);
return fd;
}
客户端代码如下,:
#include "htime.h"
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include "mysocket.h"
void *recv_work(void *data);
void sig_handler(int signo);
int main(int argc, char *argv[])
{
int p1=8888, p2=8889;
int fd;
if (argc == 2)
fd = connect_to(argv[1], p1);
else
fd = connect_to("10.254.4.23", p1);
if(fd < 0)
{
// fprintf(stderr, "server start failed\n");
return 0;
}
signal(SIGPIPE, sig_handler);
int MAX = 1024*1024*10;
char *buf = malloc(MAX);// 20MB
if (buf == NULL) { fprintf(stderr, "OOM\n"); return 0;}
int ret;
while(1)
{
fprintf(stderr, "press ENTER to continue...");
getchar();
strcpy(buf,gettime());
printf("%s\n", gettime());
ret = net_write(fd, &MAX, sizeof(int));
if (ret != sizeof(int)) {
fprintf(stderr, "%s write failed pos1: ret=%d\n", gettime(), ret);
// continue;
}
ret = net_write(fd, buf, MAX);
if (ret != MAX) {
fprintf(stderr, "%s write failed pos2: ret=%d\n", gettime(), ret);
// continue;
}
int rsp = -1;//
ret = net_read(fd, &rsp, sizeof(int));
fprintf(stderr, "%s ", gettime());
if (rsp != MAX) {
fprintf(stderr, "rsp error: ret=%d,rsp=%d,req=%d \n", ret, rsp, MAX);
} else {
fprintf(stderr, "req & rsp is ok(data length: %d) \n", rsp);
}
}
}
void sig_handler(int signo)
{
fprintf(stderr, "receivec sig: %d\n", signo);
}