简单的TCP服务器/客户端程序
server
/*================================================================
# File Name: tcp_server.c
# Author: rjm
# mail: rjm96@foxmail.com
# Created Time: 2018年05月06日 星期日 11时24分35秒
================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define ADDR_FAMILY AF_INET
#define PORT_NUM 8080
//定义一个结构体保存线程创建所执行函数需要的参数
typedef struct Arg
{
int sockfd;
struct sockaddr_in addr;
}Arg;
void* service(void* ptr)
{
Arg* arg = (Arg*)ptr;
char buf_ip[1024];
//清空buf_ip
buf_ip[0] = 0;
//inet_ntop转换client_sock的ip
if(inet_ntop(AF_INET, &arg->addr.sin_addr, buf_ip, sizeof(buf_ip)) == NULL)
{
perror("inet_ntop");
return NULL;
}
while(1)
{
//定义buf缓冲区
char buf[1024];
//清空
buf[0] = 0;
//从client_sock读取数据到buf
ssize_t read_ret = read(arg->sockfd, buf, sizeof(buf));
if(read_ret < 0)
{
perror("read");
break;
}
if(read_ret == 0)
{
printf("[client %s:%d] leave! \n", buf_ip, arg->addr.sin_port);
break;
}
printf("[client %s:%d] say: %s\n", buf_ip, arg->addr.sin_port, buf);
//将buf中的数据写到client_sock
//将消息回显给用户
int write_ret = write(arg->sockfd, buf, sizeof(buf));
if(write_ret < 0)
{
perror("write");
break;
}
buf[strlen(buf)-1] = '\0';
}
free(arg);
arg = NULL;
return NULL;
}
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("Usage: ./server [ip]\n");
return -1;
}
//1,打开socket 得到sock
int sock = socket(AF_INET, SOCK_STREAM, 0);//ipv4, tcp
if(sock < 0)
{
perror("socket");
return 1;
}
//2,定义两个sockaddr_in结构体的对象
struct sockaddr_in server_socket;
struct sockaddr_in client_socket;
//3,结构体初始化
server_socket.sin_family = ADDR_FAMILY;
server_socket.sin_port = htons(PORT_NUM);
server_socket.sin_addr.s_addr = inet_addr(argv[1]);
//4,绑定(sock和server_sock绑定)
int bind_ret = bind(sock, (struct sockaddr*)&server_socket, sizeof(struct sockaddr_in));
if(bind_ret < 0)
{
perror("bind");
return 2;
}
//5,监听
int listen_ret = listen(sock, 5);
if(listen_ret < 0)
{
perror("listen");
return 3;
}
//走到这说明绑定和监听成功
printf("Wecome to Online Chat Server\n");
//6,循环, 获得client_sock
while(1)
{
socklen_t addrlen = sizeof(struct sockaddr_in);
//获得client_sock;
int client_sock = accept(sock, (struct sockaddr*)&client_sock, &addrlen);
//定义buf_ip缓冲区
char buf_ip[1024];
//清空buf_ip
buf_ip[0] = 0;
//inet_ntop转换client_sock的ip
if(inet_ntop(AF_INET, &client_socket.sin_addr, buf_ip, sizeof(buf_ip)) == NULL)
{
perror("inet_ntop");
return 4;
}
printf("[client %s:%d] connected\n",
buf_ip, client_socket.sin_port);
//7,循环服务
/*====================================多进程版本===========================================
int pid = fork();
if(pid == 0)
{
int id = fork();
if(id == 0)
{
//孙子进程
while(1)
{
//定义buf缓冲区
char buf[1024];
//清空
buf[0] = 0;
//从client_sock读取数据到buf
ssize_t read_ret = read(client_sock, buf, sizeof(buf));
if(read_ret < 0)
{
perror("read");
break;
}
if(read_ret == 0)
{
printf("[client %s:%d] leave! \n", buf_ip, client_socket.sin_port);
break;
}
printf("[client %s:%d] say: %s\n", buf_ip, client_socket.sin_port, buf);
//将buf中的数据写到client_sock
//将消息回显给用户
int write_ret = write(client_sock, buf, sizeof(buf));
if(write_ret < 0)
{
perror("write");
break;
}
buf[strlen(buf)-1] = '\0';
}
}
}
*/
//====================================多线程版本===========================================
pthread_t tid;
Arg* arg = (Arg*)malloc(sizeof(Arg));
arg->sockfd = client_sock;
arg->addr = client_socket;
int pthread_ret = pthread_create(&tid, NULL, service, (void*)arg);
if(pthread_ret < 0)
{
perror("pthread_create");
return 5;
}
pthread_detach(tid);
}
//8,服务完毕 关闭sock
close(sock);
return 0;
}
client
/*================================================================
# File Name: tcp_client.c
# Author: rjm
# mail: rjm96@foxmail.com
# Created Time: 2018年05月06日 星期日 11时24分19秒
================================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <unistd.h>
#include <string.h>
#define ADDR_FAMILY AF_INET
#define PORT_NUM 8080
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("Usage: ./client [ip]\n");
return 1;
}
printf("Welcome to Online Chat Client\n");
//1, 打开socket
int sock = socket(AF_INET, SOCK_STREAM, 0); //ipv4, tcp
if(sock < 0)
{
perror("socket");
return 2;
}
//2, 定义buf缓冲区 清空
char buf[1024];
buf[0] = 0;
//3, 定义sockaddr_in对象server_sock
struct sockaddr_in server_sock;
//3, bzero() server_sock
bzero(&server_sock, sizeof(struct sockaddr_in));
//4, 初始化server_sock
server_sock.sin_family = ADDR_FAMILY;
server_sock.sin_port = htons(PORT_NUM);
server_sock.sin_addr.s_addr = inet_addr(argv[1]);
//5, 连接server
int connect_ret = connect(sock, (struct sockaddr*)&server_sock, sizeof(struct sockaddr_in));
if(connect_ret < 0)
{
perror("connect");
return 3;
}
//6, 循环发送消息
while(1)
{
printf("Please Enter: ");
fflush(stdout);
//fgets()到buf
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = 0;
//比较quit 退出
if(strcmp(buf, "quit") == 0)
{
printf("bye!\n");
break;
}
//把buf中的数据写到sock
int write_ret = write(sock, buf, sizeof(buf));
if(write_ret < 0)
{
perror("write");
return 4;
}
//把sock中的数据读到buf
int read_ret = read(sock, buf, sizeof(buf));
printf("server echo: %s\n", buf);
}
//7, 结束关闭sock
close(sock);
return 0;
}
测试效果
UDP 与 TCP 对比
- 可靠传输 vs 不可靠传输
- 有连接 vs 无连接
- 字节流 vs 数据报
TCP的优点
- 可靠,稳定
TCP的可靠体现在TCP在
传递数据之前,会有三次握手来建立连接,
而且在数据传递时,有确认、窗口、重传、拥塞控制机制,
在数据传完后,还会断开连接用来节约系统资源。
TCP的缺点
- 慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,
而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,
事实上,每个连接都会占用系统的CPU、内存等硬件资源。
而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。
UDP的优点
快,比TCP稍安全 UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。
但UDP也是无法避免攻击的,比如:UDP Flood攻击……
UDP的缺点
不可靠,不稳定 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。
什么时候应该使用TCP:
- 当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。 在日常生活中,常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输 …………
什么时候应该使用UDP:
- 当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 比如,日常生活中,常见使用UDP协议的应用如下: QQ语音 QQ视频 TFTP ……
有些应用场景对可靠性要求不高会用到UPD,比如长视频,要求要有一定的速率