一 理论
网络编程(各种编程语言几乎都支持同一种协议,可进行各种设备间的相互通信)TCP/IP
tcp使用的过程,
client端,主动连接方叫client。
server端,被动接收方叫server。
1、TCP先连接(三次握手)
client > server:发送SYN(同步信号),起始编号为 a
server > client:回复SYN ,起始编号为b,包含ack(a + 1)
client > server ack (b + 1)
2、连接通了,双方可以互相收发消息
3、断开连接
client > server:FIN m
server > client :ack m +1
server > client:FIN n
client > server :ack n + 1
二 代码一:单方通信
客户端代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
int main()
{
int st = socket(AF_INET, SOCK_STREAM, 0);//初始化socket
struct sockaddr_in addr;//定义一个IP地址的结构
memset(&addr, 0, sizeof(addr));//将IP地址初始化为0
addr.sin_family = AF_INET;//设置结构地址类型为TCP/IP地址
addr.sin_port = htons(8080);//指定一个端口号为8080,其中htons是将short类型从host字节类型转到net字节类型的转化
addr.sin_addr.s_addr = inet_addr("10.170.40.27");//字符串IP地址转化为整数
if (connect(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)//连接
{
printf("connect failed %s\n", strerror(errno));
return -1;
}
char s[1024];
memset(s, 0, sizeof(s));
strcpy(s, "I'm Apple Lin.");
if (send(st, s, sizeof(s), 0) == -1)//发送数据
{
printf("Sending faled %s\n", strerror(errno));
return -1;
}
printf("sending success!\n");
close(st);//关闭套接字
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
int main()
{
int st = socket(AF_INET, SOCK_STREAM, 0);
int on = 1;
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
{
printf("setsockopt failed %s\n", strerror(errno));
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));//将IP地址初始化为0
addr.sin_family = AF_INET;//设置结构地址类型为TCP/IP地址
addr.sin_port = htons(8080);//指定一个端口号为8080,其中htons是将short类型从host字节类型转到net字节类型的转化
addr.sin_addr.s_addr = htonl(INADDR_ANY);//这个server上所有的IP地址
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
printf("bind failed %s\n", strerror(errno));
return - 1;
}
if (listen(st, 20) == -1)//server开始监听,同时最多监听20个客户
{
printf("listen failed %s\n", strerror(errno));
return -1;
}
printf("statring server...\n");
char s[1024];
int client_st = 0;//client端的socket
struct sockaddr_in client_addr;//客户端的IP地址
// void *p = &client_addr;
int i=0;
for (i = 0; i < 5; ++i)//指定5次
{
memset(&client_addr, 0, sizeof(client_addr));
socklen_t len = sizeof(client_addr);
client_st = accept(st, (struct sockaddr *)&client_addr, &len);
if (client_st == -1)
{
printf("client failed %s\n", strerror(errno));
return -1;
}
while(1)
{
memset(s, 0, sizeof(s));
int rc = recv(client_st, s, sizeof(s), 0);
if(rc > 0)
{
printf("recv is %s\n", s);
}
else if (rc == 0)
{
printf("client socket closed\n");
break;
}
else
{
printf("recv failed %s\n", strerror(errno));
break;
}
}
close(client_st);
}
close(st);
return 0;
}
makefile文件
.SUFFIXES:.c .o
CC=gcc
SRCS1=server.c
SRCS2=client.c
OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=server
EXEC2=client
start:$(OBJS2) $(OBJS1)
$(CC) -o $(EXEC2) $(OBJS2)
$(CC) -o $(EXEC1) $(OBJS1)
@echo "======OK!=========\n"
.c.o:
$(CC) -o $@ -c $<
clean:
rm -r $(OBJS2) $(EXEC2) $(EXEC1) $(OBJS1)
运行结果:
三 双方单线程阻塞式通信
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("Input argument like : client | IP | port\n");
return;
}
int pot = atoi(argv[2]);
int st = socket(AF_INET, SOCK_STREAM, 0);//初始化socket
struct sockaddr_in addr;//定义一个IP地址的结构
memset(&addr, 0, sizeof(addr));//将IP地址初始化为0
addr.sin_family = AF_INET;//设置结构地址类型为TCP/IP地址
addr.sin_port = htons(pot);//指定一个端口号为8080,其中htons是将short类型从host字节类型转到net字节类型的转化
addr.sin_addr.s_addr = inet_addr(argv[1]);//字符串IP地址转化为整数
if (connect(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)//连接
{
printf("connect failed %s\n", strerror(errno));
return -1;
}
char s[1024];
while (1)
{
memset(s, 0, sizeof(s));
read(STDIN_FILENO, s, sizeof(s));
if (send(st, s, sizeof(s), 0) == -1)//发送数据
{
printf("Sending faled %s\n", strerror(errno));
return -1;
}
printf("send success!\n");
//接受来自server的消息
memset(s, 0, sizeof(s));
if (recv(st, s, sizeof(s), 0) > 0)//如果接收到消息,就打印该消息,否则退出通话
printf("Server say: %s\n", s);
else
break;
}
close(st);//关闭套接字
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("Input argument like this: server | port\n");
return -1;
}
int pot = atoi(argv[1]);
int st = socket(AF_INET, SOCK_STREAM, 0);
int on = 1;
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//地址重用
{
printf("setsockopt failed %s\n", strerror(errno));
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));//将IP地址初始化为0
addr.sin_family = AF_INET;//设置结构地址类型为TCP/IP地址
addr.sin_port = htons(pot);//指定一个端口号为8080,其中htons是将short类型从host字节类型转到net字节类型的转化
addr.sin_addr.s_addr = htonl(INADDR_ANY);//这个server上所有的IP地址
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
printf("bind failed %s\n", strerror(errno));
return - 1;
}
if (listen(st, 20) == -1)//server开始监听,同时最多监听20个客户
{
printf("listen failed %s\n", strerror(errno));
return -1;
}
printf("statring server...\n");
char s[1024];
int client_st = 0;//client端的socket
struct sockaddr_in client_addr;//客户端的IP地址
// void *p = &client_addr;
while (1)
{
memset(&client_addr, 0, sizeof(client_addr));
socklen_t len = sizeof(client_addr);
client_st = accept(st, (struct sockaddr *)&client_addr, &len);//向客户端回复消息
if (client_st == -1)
{
printf("client failed %s\n", strerror(errno));
return -1;
}
while(1)
{
memset(s, 0, sizeof(s));
int rc = recv(client_st, s, sizeof(s), 0);
if(rc > 0)
{
printf("%s say: %s\n", inet_ntoa(client_addr.sin_addr), s);//inet_ntoa将IP地址转化为字符串
memset(s, 0, sizeof(s));
read(STDIN_FILENO, s, sizeof(s));
send(client_st, s, strlen(s), 0);
printf("send success!\n");
}
else if (rc == 0)
{
printf("client socket closed\n");
break;
}
else
{
printf("recv failed %s\n", strerror(errno));
break;
}
}
close(client_st);
}
close(st);
return 0;
}
四 增加多线程且限制用户数量为1
client.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void *recvsocket(void *arg)
{
int st = *(int *)arg;
char s[1024];
while (1)
{
memset(s, 0, sizeof(s));
int rv = recv(st, s, sizeof(s), 0);
if ( rv <= 0)
{
printf("recv failed!\n");
break;
}
printf("Message: %s\n", s);
}
return NULL;
}
void *sendsocket(void *arg)
{
int st = *(int *)arg;
char s[1024];
while (1)
{
memset(s, 0, sizeof(s));
read(STDIN_FILENO, s, sizeof(s));
send(st, s, strlen(s), 0);
}
return NULL;
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("Input argument like : client | IP | port\n");
return;
}
int pot = atoi(argv[2]);
int st = socket(AF_INET, SOCK_STREAM, 0);//初始化socket
struct sockaddr_in addr;//定义一个IP地址的结构
memset(&addr, 0, sizeof(addr));//将IP地址初始化为0
addr.sin_family = AF_INET;//设置结构地址类型为TCP/IP地址
addr.sin_port = htons(pot);//指定一个端口号为8080,其中htons是将short类型从host字节类型转到net字节类型的转化
addr.sin_addr.s_addr = inet_addr(argv[1]);//字符串IP地址转化为整数
if (connect(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)//连接
{
printf("connect failed %s\n", strerror(errno));
return -1;
}
printf("clinet beging...\n");
pthread_t thrd1, thrd2;
pthread_create(&thrd1, NULL, recvsocket, &st);
pthread_create(&thrd2, NULL, sendsocket, &st);
pthread_join(thrd1, NULL);
pthread_join(thrd2, NULL);
close(st);//关闭套接字
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
struct ps
{
int st;
pthread_t *thr;
};
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int status = 0;
void *recvsocket(void *arg)//接收client端socket数据的线程
{
struct ps *p = (struct ps *)arg;
int st = p->st;
char s[1024];
while (1)
{
memset(s, 0, sizeof(s));
int rv = recv(st, s, sizeof(s), 0);
if (rv <= 0)
{
printf("recv failed!\n");
break;
}
printf("Message: %s\n", s);
}
pthread_mutex_lock(&mutex);
status = 0;
pthread_mutex_unlock(&mutex);
pthread_cancel(*(p->thr));//被cancel掉的线程内部没有使用锁。
return NULL;
}
void *sendsocket(void *arg)
{
int st = *(int *)arg;
char s[1024];
while (1)
{
memset(s, 0, sizeof(s));
read(STDIN_FILENO, s, sizeof(s));
send(st, s, strlen(s), 0);
}
return NULL;
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("Input argument like this: server | port\n");
return -1;
}
int pot = atoi(argv[1]);
int st = socket(AF_INET, SOCK_STREAM, 0);
int on = 1;
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//地址重用
{
printf("setsockopt failed %s\n", strerror(errno));
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));//将IP地址初始化为0
addr.sin_family = AF_INET;//设置结构地址类型为TCP/IP地址
addr.sin_port = htons(pot);//指定一个端口号为8080,其中htons是将short类型从host字节类型转到net字节类型的转化
addr.sin_addr.s_addr = htonl(INADDR_ANY);//这个server上所有的IP地址
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
printf("bind failed %s\n", strerror(errno));
return - 1;
}
if (listen(st, 20) == -1)//server开始监听,同时最多监听20个客户
{
printf("listen failed %s\n", strerror(errno));
return -1;
}
printf("statring server...\n");
char s[1024];
int client_st = 0;//client端的socket
struct sockaddr_in client_addr;//客户端的IP地址
// void *p = &client_addr;
pthread_t thrd1, thrd2;
while (1)
{
memset(&client_addr, 0, sizeof(client_addr));
socklen_t len = sizeof(client_addr);
client_st = accept(st, (struct sockaddr *)&client_addr, &len);//向客户端回复消息
pthread_mutex_lock(&mutex);//为全局变量加一个互斥锁,防止与线程函数同时读写变量的冲突
status++;
pthread_mutex_unlock(&mutex);//解锁
if (status > 1)//代表这是第二个socket连接
{
close(client_st);
continue;
}
if (client_st == -1)
{
printf("client failed %s\n", strerror(errno));
return -1;
}
printf("accept by %s\n", inet_ntoa(client_addr.sin_addr));
struct ps ps1;
ps1.st = client_st;
ps1.thr = &thrd2;
pthread_create(&thrd1, NULL, recvsocket, &ps1);
pthread_detach(thrd1);//设置线程为可分离
pthread_create(&thrd2, NULL, sendsocket, &client_st);
pthread_detach(thrd2);//设置线程为可分离
}
close(st);
return 0;
}
makefile
.SUFFIXES:.c .o
CC=gcc
SRCS1=server.c
SRCS2=client.c
OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=server
EXEC2=client
start:$(OBJS2) $(OBJS1)
$(CC) -o $(EXEC2) $(OBJS2) -lpthread
$(CC) -o $(EXEC1) $(OBJS1) -lpthread
@echo "======OK!=========\n"
.c.o:
$(CC) -o $@ -c $<
clean:
rm -r $(OBJS2) $(EXEC2) $(EXEC1) $(OBJS1)
五 传输文件跨平台(windows && Linux)
client.c
#include <stdio.h>
#include <stdlib.h>
#include "pub.h"
int main(int arg, char *args[])
{
if (arg < 4)//如果参数小于3个,main函数退出
{
printf("Input argumnet: source | IP | Port | resource\n");
return EXIT_FAILURE;
}
int iport = atoi(args[2]);//将第二个参数转化为端口号
if (iport == 0)//如果端口号为0,main函数退出
{
printf("port %d is invalid\n", iport);
return EXIT_FAILURE;
}
printf("%s send begin\n", args[3]);
if (send_work(args[1], iport, args[3]) == 1)//将第一个参数做为IP地址,第二个参数做为端口号,第三个参数做为要发送的文件名传递给send_work函数
printf("%s send success\n", args[3]);
else
printf("%s send fail\n", args[3]);
return EXIT_SUCCESS;
}
pub.h
#ifndef PUB_H_
#define PUB_H_
int send_work(const char *hostname, int port, const char *filename);
int recv_work(int port);
#endif PUB_H
#ifdef WIN
#include <WinSock2.h>
#else
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define SOCKET int
#endif
#include <stdio.h>
#include "pub.h"
#define BUFSIZE 262144 //256k
void getfilename(const char *filename, char *name)//从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.txt
{
int len = strlen(filename);
int i;
for (i = (len - 1); i >= 0; i--)
{
if ((filename[i] == '\\') || (filename[i] == '/'))
{
break;
}
}
strcpy(name, &filename[i + 1]);
return;
}
SOCKET init_socket()//初始化socket
{
//如果是windows,执行如下代码
#ifdef WIN
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return -1;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
#endif
return 0;
}
SOCKET socket_connect(const char *hostname, int port)//连接到指定的主机和端口号
{
if (init_socket() == -1)
return 0;
SOCKET st = socket(AF_INET, SOCK_STREAM, 0);//建立TCP socket
if (st == 0)
return 0;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);//指定port为要连接的端口号
addr.sin_addr.s_addr = inet_addr(hostname);//指定hostname为要连接的IP地址
if (connect(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
printf("connect to %s:%d failed %s\n", hostname, port, strerror(errno));
return 0;//连接失败,返回0
}
else
{
return st;//连接成功,返回socket描述符
}
}
SOCKET socket_create(int port)//在port指定的端口上建立server端socket
{
if (init_socket() == -1)
return 0;
SOCKET st = socket(AF_INET, SOCK_STREAM, 0);//建立TCP socket
if (st == 0)
return 0;//如果建立socket失败,返回0
#ifdef WIN
const char on = 0;
#else
int on = 0;
#endif
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
{
printf("setsockopt failed %s\n", strerror(errno));
return 0;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)//server端socket,所以需要绑定IP地址
{
printf("bind failed %s\n", strerror(errno));
return 0;
}
if (listen(st, 20) == -1)
{
printf("listen failed %s\n", strerror(errno));
return 0;
}
printf("listen %d success\n", port);
return st;//server端socket建立成功,返回server端socket描述符
}
SOCKET socket_accept(SOCKET listen_st)//server端socket开始accept的函数
{
struct sockaddr_in client_addr;
#ifdef WIN
int len = 0;
#else
unsigned int len = 1;
#endif
len = sizeof(client_addr);
memset(&client_addr, 0, sizeof(client_addr));
SOCKET client_st = accept(listen_st, (struct sockaddr *) &client_addr,
&len);//accept阻塞,直到有client连接到server才返回
if (client_st == -1)
{
printf("accept failed %s\n", strerror(errno));
return 0;
}
else
{
printf("accept by %s\n", inet_ntoa(client_addr.sin_addr));
return client_st;//有client连接到server,返回client的socket描述符
}
}
int send_work(const char *hostname, int port, const char *filename)//向hostname指定的IP地址发送filename指定的文件
{
SOCKET st = socket_connect(hostname, port);//连接到hostname指定的IP地址和port指定的端口号
if (st == 0)//连接失败,函数返回
return 0;
FILE *fd = fopen(filename, "rb");//以只读方式打开filename指定的文件
if (fd == NULL)//如果文件打开失败,函数返回
{
printf("open %s failed %s\n", filename, strerror(errno));
return 0;
}
char *buf = malloc(BUFSIZE);//申请一个缓冲区,存放接收到的文件内容
memset(buf, 0, BUFSIZE);
getfilename(filename, buf);//从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.txt
size_t rc = send(st, buf, strlen(buf), 0);//客户端第一次给server端发送的数据为要传递的文件名称,将解析完成后的文件名通过socket发送给server端
if (rc <= 0)
{
if (rc < 0)
printf("send failed %s\n", strerror(errno));
else
printf("socket disconnect\n");
}
else
{
memset(buf, 0, BUFSIZE);
if (recv(st, buf, BUFSIZE, 0) <= 0)//接收来自server端的回复
{
if (rc < 0)
printf("recv failed %s\n", strerror(errno));
else
printf("socket disconnect\n");
}
else
{
if (strncmp(buf, "OK", 2) == 0)//如果收到来自服务端的回复,代表服务端认可,可以发送文件了
{
while (1)
{
memset(buf, 0, BUFSIZE);
rc = fread(buf, 1, BUFSIZE, fd);//循环读取文件,直到读到文件尾,循环break
if (rc <= 0)
{
if (rc < 0)
printf("fread failed %s\n", strerror(errno));
break;
}
else
{
rc = send(st, buf, rc, 0);//将从文件中读到的数据,通过socket发送到server端,其中rc为从文件中读到的数据大小
if (rc <= 0)//如果发送失败,代表TCP连接出错,循环break
{
if (rc < 0)
printf("send failed %s\n", strerror(errno));
else
printf("socket disconnect\n");
break;
}
}
}
}
}
}
fclose(fd);
free(buf);
#ifdef WIN
closesocket(st);
WSACleanup();
#else
close(st);
#endif
return 1;
}
int recv_work(int port)//server端socket在port指定的端口上listen,接收来自client发送的文件
{
SOCKET listen_st = socket_create(port);//建立server端socket,在port指定端口listen
if (listen_st == 0)//如果创建服务端socket失败,函数返回0
return 0;
SOCKET st = socket_accept(listen_st);//如果有client连接到达,socket_accept函数返回client的socket描述符
if (st == 0)
return 0;
char *buf = malloc(BUFSIZE);//建立接收文件数据缓冲区
FILE *fd = NULL;
memset(buf, 0, BUFSIZE);
size_t rc = recv(st, buf, BUFSIZE, 0);//接收来自client的数据,客户端第一次要发送的文件名称
if (rc <= 0)
{
if (rc < 0)
printf("recv failed %s\n", strerror(errno));
else
printf("socket disconnect\n");
}
else
{
printf("receiving %s\n", buf);
fd = fopen(buf, "wb");//以只写方式打开文件
if (fd == NULL)
{
printf("open %s failed %s\n", buf, strerror(errno));
}
else
{
memset(buf, 0, BUFSIZE);
strcpy(buf, "OK");
rc = send(st, buf, strlen(buf), 0);//回复客户端,同意接收文件
if (rc <= 0)
{
if (rc < 0)
printf("send failed %s\n", strerror(errno));
else
printf("socket disconnect\n");
}
while (1)
{
memset(buf, 0, BUFSIZE);
rc = recv(st, buf, BUFSIZE, 0);//循环接收来自client的数据,数据为发送文件的内容
if (rc <= 0)//如果client连接断开,代表文件传递完成,或者网络意外中断,循环break
{
if (rc < 0)
printf("recv failed %s\n", strerror(errno));
else
printf("socket disconnect\n");
break;
}
else
{
fwrite(buf, 1, rc, fd);//将从client端收到的内容写入文件
}
}
}
}
if (fd)
fclose(fd);//关闭打开的文件
free(buf);
#ifdef WIN
closesocket(st);
closesocket(listen_st);
WSACleanup();
#else
close(st);
close(listen_st);
#endif
return 1;
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include "pub.h"
int main(int arg, char *args[])
{
if (arg < 2)//如果没有参数,main函数退出
{
printf("Input like: source | port\n");
return EXIT_FAILURE;
}
int iport = atoi(args[1]);//将第一个参数转化为端口号,server端socket要在这个端口号上listen
if (iport == 0)
{
printf("port %d is invalid\n", iport);
return EXIT_FAILURE;
}
printf("recv is begin\n");
if (recv_work(iport) == 1)//server端socket在port指定的端口上listen,接收来自client发送的文件
printf("recv success\n");
else
printf("recv fail\n");
return EXIT_SUCCESS;
}
Linux下的makefile
.SUFFIXES: .c .o
CC=gcc
SERVERSRCS=server.c\
pub.c
CLIENTSRCS=client.c\
pub.c
SERVEROBJS=$(SERVERSRCS:.c=.o)
CLIENTOBJS=$(CLIENTSRCS:.c=.o)
SERVEREXEC=server
CLIENTEXEC=client
all:$(SERVEROBJS) $(CLIENTOBJS)
$(CC) -o $(SERVEREXEC) $(SERVEROBJS)
$(CC) -o $(CLIENTEXEC) $(CLIENTOBJS)
@echo '-------------ok--------------'
.c.o:
$(CC) -Wall -o $@ -c $<
clean:
rm -f $(SERVEROBJS)
rm -f $(CLIENTOBJS)
rm -f core*
.SUFFIXES: .c .o
CC=gcc
SERVERSRCS=server.c\
pub.c
CLIENTSRCS=client.c\
pub.c
SERVEROBJS=$(SERVERSRCS:.c=.o)
CLIENTOBJS=$(CLIENTSRCS:.c=.o)
SERVEREXEC=server.exe
CLIENTEXEC=client.exe
all:$(SERVEROBJS) $(CLIENTOBJS)
$(CC) -static -o $(SERVEREXEC) $(SERVEROBJS) -lWs2_32
$(CC) -static -o $(CLIENTEXEC) $(CLIENTOBJS) -lWs2_32
@echo '-------------ok--------------'
.c.o:
$(CC) -Wall -DWIN -o $@ -c $<
clean:
rm -f $(SERVEROBJS)
rm -f $(CLIENTOBJS)
rm -f core*
六 Qt客户端代码(基于TCP)
TCPDialog.h
#ifndef TCPDIALOG_H
#define TCPDIALOG_H
#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QTcpSocket>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
private:
QLabel *label1, *label2;
QLineEdit *edit1, *edit2;
QPushButton *btn1;
QTcpSocket *tcpsocket;
QString filename;
private slots:
void on_clicked1();
void dataReceived();
};
#endif // TCPDIALOG_H
#include "TcpDialog.h"
#include <QMessageBox>
#include <QGridLayout>
#include <QByteArray>
#include <QHostAddress>
#include <QFileDialog>
#define BUFSIZE 262144 //256k
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
tcpsocket = new QTcpSocket(this);
setWindowTitle(tr("tcp send file client"));//设置窗口标题
label1 = new QLabel;
label1->setText(tr("Destination &IP"));
label2 = new QLabel;
label2->setText(tr("Destination &Port"));
edit1 = new QLineEdit;
edit2 = new QLineEdit;
label1->setBuddy(edit1);
label2->setBuddy(edit2);
btn1 = new QPushButton(tr("&Send file"));
QGridLayout *layout1 = new QGridLayout(this);
layout1->addWidget(label1, 0, 0);//加入label1控件
layout1->addWidget(edit1, 0, 1);//加入edit1控件
layout1->addWidget(label2, 1, 0);//加入label2控件
layout1->addWidget(edit2, 1, 1);//加入edit2控件
layout1->addWidget(btn1, 2, 0);//加入btn1控件
connect(btn1, SIGNAL(clicked()), this, SLOT(on_clicked1()));
connect(tcpsocket, SIGNAL(readyRead()), this, SLOT(dataReceived()));
}
Dialog::~Dialog()
{
}
void getfilename(const char *filename, char *name)//从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.txt
{
int len = strlen(filename);
int i;
for (i = (len - 1); i >= 0; i--)
{
if ((filename[i] == '\\') || (filename[i] == '/'))//为了兼容linux与windows的路径,同时解析/与\分隔的路径名称
{
break;
}
}
strcpy(name, &filename[i + 1]);
return;
}
void Dialog::on_clicked1()//当Send File按钮点击时,会进入选取文件目录
{
filename = QFileDialog::getOpenFileName(this, tr("Open File..."));
if (!filename.isEmpty())
{
tcpsocket->close();
QHostAddress serverIP;
serverIP.setAddress(edit1->text());
tcpsocket->connectToHost(serverIP , edit2->text().toInt());//连接到server端
char buf[1024];
memset(buf, 0, sizeof(buf));
//从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.txt
getfilename(filename.toStdString().data(), buf);
tcpsocket->write(buf, strlen(buf));//将文件名称发送到server端
}
}
void Dialog::dataReceived()//接收socket消息的槽函数
{
char *buf = (char *)malloc(BUFSIZE);//申请一个缓冲区,存放接收到的文件内容
while(tcpsocket->bytesAvailable() > 0)
{
memset(buf, 0, BUFSIZE);
tcpsocket->read(buf, BUFSIZE);
}
if (strncmp(buf, "OK", 2) == 0)//server端回复同意接收文件
{
FILE *fd = fopen(filename.toStdString().data(), "rb");//以只读方式打开filename指定的文件
if (fd)//如果文件成功打开
{
while (1)
{
memset(buf, 0, BUFSIZE);
size_t rc = fread(buf, 1, BUFSIZE, fd);//循环读取文件,直到读到文件尾,循环break
if (rc <= 0)
{
break;//文件尾,循环break
} else
{
tcpsocket->write(buf, rc);//将从文件中读到的数据,通过socket发送到server端,其中rc为从文件中读到的数据大小
if (rc <= 0)//如果发送失败,代表TCP连接出错,循环break
{
break;
}
}
}
}
fclose(fd);
}
tcpsocket->close();//文件发送完成后关闭socket
free(buf);
}
#include "TcpDialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
运行结果:
因为他们都遵从同一种协议,所以可以跨平台,跨编程语言,只要协议相同即可!!!