网络编程概述
本文将介绍网络编程中的一些基础知识,包括TCP和UDP的区别、端口号的作用、字节序的概念,以及通过socket编程实现客户端和服务器之间的通信。我们还会展示一些示例代码,帮助理解这些概念的实际应用。
1. TCP/UDP的区别,端口号的作用
1.1 TCP/UDP的区别
TCP(传输控制协议) 和 UDP(用户数据报协议) 是互联网协议套件中的两个核心传输层协议。它们之间的主要区别如下:
-
连接方式:
- TCP是面向连接的(如打电话要先拨号建立连接),需要在传输数据之前建立连接。
- UDP是无连接的,即发送数据之前,不需要建立连接。
-
可靠性:
- TCP提供可靠的服务,保证数据无差错、不丢失、不重复且按序到达。
- UDP尽最大努力交付,不保证可靠交付。
-
数据传输:
- TCP面向字节流,将数据视为一连串无结构的字节流。
- UDP面向报文,支持面向报文的传输方式。
-
通信方式:
- 每一条TCP连接只能是点到点的。
- UDP支持一对一、一对多、多对一和多对多的交互通信。
-
首部开销:
- TCP首部开销较大,为20字节。
- UDP首部开销小,只有8字节。
-
信道类型:
- TCP的逻辑通信信道是全双工的可靠信道。
- UDP则是不可靠信道。
1.2 端口的作用
IP地址标识网络中的主机,但一台主机可以提供多个网络服务(如Web服务、FTP服务等)。为了区分不同的服务,使用"IP地址+端口号"来标识具体的服务。
- 端口号 提供了访问通道。服务器通常通过知名端口号来识别。例如,FTP服务器的TCP端口号为21,Telnet服务器的TCP端口号为23,TFTP服务器的UDP端口号为69。
2. 字节序
- 字节序 指的是多字节数据在内存中的存储顺序。常见的字节序有两种:大端字节序(Big-endian)和小端字节序(Little-endian)。
- x86系列CPU通常采用小端字节序(Little-endian)。
- 网络字节序通常采用大端字节序(Big-endian)。
3. Socket编程实现双方消息发送
3.1 Socket服务器实现不断接收客户端信息
通过Socket编程实现一个简单的服务器不断接收客户端信息,并处理数据。以下是一个简单的Socket服务器和客户端示例代码。
server.c
cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int s_fd;
int c_fd;
int n_read;
char readBuf[128];
char *msg = "I got your message";
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3) {
printf("param isn't right\n");
return -1;
}
// 1. 创建socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1) {
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &s_addr.sin_addr);
// 2. 绑定地址
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
// 3. 监听端口
listen(s_fd, 10);
// 4. 接受客户端连接
int clen = sizeof(struct sockaddr_in);
while (1) {
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if (c_fd == -1) {
perror("accept");
continue;
}
printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
if (fork() == 0) {
// 5. 读取客户端数据
n_read = read(c_fd, readBuf, 128);
if (n_read == -1) {
perror("read");
} else {
printf("get message: %d, %s\n", n_read, readBuf);
}
// 6. 发送响应
write(c_fd, msg, strlen(msg));
break; // 子进程完成一次后退出,父进程继续接收数据
}
}
return 0;
}
client.c
cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int c_fd;
int n_read;
char readBuf[128];
char *msg = "from client msg";
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3) {
printf("param isn't right\n");
return -1;
}
// 1. 创建socket
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1) {
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &c_addr.sin_addr);
// 2. 连接服务器
if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1) {
perror("connect");
exit(-1);
}
// 3. 发送数据
int n_write = write(c_fd, msg, strlen(msg));
if (n_write == -1) {
perror("write");
}
// 4. 读取响应
n_read = read(c_fd, readBuf, 128);
if (n_read == -1) {
perror("read");
} else {
printf("get message from server: %d, %s\n", n_read, readBuf);
}
return 0;
}
4. 一个服务器对应一个客户端作双方交互信息
服务端创建子进程不断接收客户端数据,再创建一个子进程发送数据,做不同的事情;客户端创建子进程发送数据,父进程读取数据;一个进程一个while(1)
。
server.c
cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int s_fd;
int c_fd;
int n_read;
char readBuf[128];
int mark = 0;
char msg[128] = {0};
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3) {
printf("param isn't right\n");
return -1;
}
// 1. 创建socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1) {
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &s_addr.sin_addr);
// 2. 绑定地址
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
// 3. 监听端口
listen(s_fd, 10);
// 4. 接受客户端连接
int clen = sizeof(struct sockaddr_in);
while (1) {
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if (c_fd == -1) {
perror("accept");
continue;
}
printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
mark++;
if (fork() == 0) {
if (fork() == 0) {
while (1) {
// 6. 发送响应
memset(msg, 0, sizeof(msg));
sprintf(msg, "welcome %d client", mark);
write(c_fd, msg, strlen(msg));
sleep(3);
}
}
// 5. 读取客户端数据
while (1) {
memset(readBuf, 0, sizeof(readBuf));
n_read = read(c_fd, readBuf, 128);
if (n_read == -1) {
perror("read");
} else {
printf("\nget: %s\n", readBuf);
}
}
break;
}
}
return 0;
}
client.c
cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int c_fd;
int n_read;
char readBuf[128];
char msg[128] = {0};
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3) {
printf("param isn't right\n");
return -1;
}
// 1. 创建socket
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1) {
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &c_addr.sin_addr);
// 2. 连接服务器
if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1) {
perror("connect");
exit(-1);
}
while (1) {
if (fork() == 0) {
while (1) {
// 3. 发送数据
memset(msg, 0, sizeof(msg));
printf("input: ");
gets(msg);
write(c_fd, msg, strlen(msg));
}
}
while (1) {
// 4. 读取响应
memset(readBuf, 0, sizeof(readBuf));
n_read = read(c_fd, readBuf, 128);
if (n_read == -1) {
perror("read");
} else {
printf("\nget: %s\n", readBuf);
}
}
}
return 0;
}
5. 服务器与多个客户端聊天,服务器自动回复
server.c
cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv) {
int s_fd;
int c_fd;
int n_read;
int mark = 0;
char buf[128] = {0};
char msg[128] = {0};
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3) {
printf("params no ok\n");
exit(-1);
}
// 1. 创建socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1) {
perror("socket");
exit(-1);
}
// 2. 绑定地址
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &s_addr.sin_addr);
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
// 3. 监听端口
listen(s_fd, 10);
int len = sizeof(struct sockaddr_in);
while (1) {
// 4. 接受客户端连接
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &len);
if (c_fd == -1) {
perror("accept");
continue;
}
mark++;
printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
if (fork() == 0) {
if (fork() == 0) {
// 5. 发送自动回复消息
while (1) {
memset(msg, 0, sizeof(msg));
sprintf(msg, "welcome No %d client", mark);
write(c_fd, msg, strlen(msg));
sleep(2);
}
}
// 6. 读取客户端消息
while (1) {
memset(buf, 0, sizeof(buf));
n_read = read(c_fd, buf, sizeof(buf));
if (n_read == -1) {
perror("read");
} else {
printf("read client: %s\n", buf);
}
}
}
}
close(s_fd);
return 0;
}
client.c
cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv) {
int c_fd;
int n_read;
char buf[128] = {0};
char msg[128] = {0};
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3) {
printf("params is no ok\n");
exit(-1);
}
// 1. 创建socket
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1) {
perror("socket");
exit(-1);
}
// 2. 绑定地址
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &c_addr.sin_addr);
// 3. 连接服务器
if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1) {
perror("connect");
exit(-1);
}
while (1) {
if (fork() == 0) {
while (1) {
// 4. 发送消息
memset(msg, 0, sizeof(msg));
printf("input: ");
gets(msg);
write(c_fd, msg, strlen(msg));
}
}
while (1) {
// 5. 接收消息
memset(buf, 0, sizeof(buf));
n_read = read(c_fd, buf, sizeof(buf));
if (n_read == -1) {
perror("read");
} else {
printf("read server: %s\n", buf);
}
}
}
close(c_fd);
return 0;
}
6. FTP云盘
FTP云盘功能实现,包含以下功能:
lls
: 列出本地的文件lcd
: 进入本地的文件路径cd
: 进入服务端的路径ls
: 列出服务端的文件quit
: 退出客户端get
: 获取服务端的文件put
: 上传客户端的文件
server.c
cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
int get_cmd_type(char *cmd) {
if (strcmp("ls", cmd) == 0) return LS;
if (strcmp("pwd", cmd) == 0) return PWD;
if (strcmp("quit", cmd) == 0) return QUIT;
if (strstr(cmd, "cd")) return CD;
if (strstr(cmd, "get")) return GET;
if (strstr(cmd, "put")) return PUT;
return -1;
}
char* getDir(char *cmd) {
char *p = NULL;
p = strtok(cmd, " ");
p = strtok(NULL, " ");
return p;
}
void cmdHandler(struct MSG msg, int c_fd) {
int ret;
int fd;
int p_fd;
FILE *fp;
char *filename = NULL;
char *file = NULL;
char *filename2 = NULL;
char dataBuf[1024] = {0};
printf("%s\n", msg.cmd);
ret = get_cmd_type(msg.cmd);
printf("ret=%d\n", ret);
switch (ret) {
case LS:
case PWD:
fp = popen(msg.cmd, "r");
fread(msg.cmd, sizeof(msg.cmd), 1, fp);
write(c_fd, &msg, sizeof(msg));
break;
case CD:
filename = getDir(msg.cmd);
printf("filename: %s\n", filename);
chdir(filename);
break;
case GET:
file = getDir(msg.cmd);
if (access(file, F_OK) == -1) {
strcpy(msg.cmd, "NO this file");
write(c_fd, &msg, sizeof(msg));
} else {
msg.type = 8;
fd = open(file, O_RDWR);
read(fd, dataBuf, sizeof(dataBuf));
close(fd);
strcpy(msg.cmd, dataBuf);
write(c_fd, &msg, sizeof(msg));
printf("success\n");
}
break;
case PUT:
filename2 = getDir(msg.cmd);
printf("filename: %s\n", filename2);
p_fd = open(filename2, O_RDWR | O_CREAT, 0666);
write(p_fd, msg.dataBuf, strlen(msg.dataBuf));
printf("write success\n");
close(p_fd);
break;
case QUIT:
printf("client out\n");
exit(-1);
break;
}
}
int main(int argc, char **argv) {
int s_fd;
int c_fd;
int n_read;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
struct MSG msg;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3) {
printf("params is no ok\n");
exit(-1);
}
// 1. 创建socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1) {
perror("socket");
exit(-1);
}
// 2. 绑定地址
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &s_addr.sin_addr);
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
// 3. 监听端口
listen(s_fd, 10);
int len = sizeof(struct sockaddr_in);
while (1) {
// 4. 接受客户端连接
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &len);
if (c_fd == -1) {
perror("accept");
continue;
}
printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
if (fork() == 0) {
while (1) {
memset(&msg.cmd, 0, sizeof(msg));
n_read = read(c_fd, &msg, sizeof(msg));
if (n_read == 0) {
printf("client out");
break;
} else if (n_read > 0) {
cmdHandler(msg, c_fd);
}
}
}
}
close(s_fd);
close(c_fd);
return 0;
}
client.c
cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
int get_cmd_type(char *cmd) {
if (strcmp(cmd, "ls") == 0) return LS;
if (strcmp(cmd, "pwd") == 0) return PWD;
if (strcmp(cmd, "quit") == 0) return QUIT;
if (strstr(cmd, "lcd")) return LCD;
if (strstr(cmd, "cd")) return CD;
if (strstr(cmd, "lls")) return LLS;
if (strstr(cmd, "get")) return GET;
if (strstr(cmd, "put")) return PUT;
return -1;
}
char* getDir(char *cmd) {
char *p = NULL;
p = strtok(cmd, " ");
p = strtok(NULL, " ");
return p;
}
int cmd_handler(struct MSG msg, int c_fd) {
int ret;
int fd;
char *filename = NULL;
char *dir = NULL;
char buf[32] = {0};
ret = get_cmd_type(msg.cmd);
switch (ret) {
case LS:
case PWD:
case CD:
case GET:
write(c_fd, &msg, sizeof(msg));
break;
case LLS:
system("ls");
break;
case LCD:
filename = getDir(msg.cmd);
printf("filename: %s\n", filename);
chdir(filename);
break;
case PUT:
strcpy(buf, msg.cmd);
dir = getDir(buf);
printf("dir: %s\n", dir);
if (access(dir, F_OK) == -1) {
printf("%s no exsit\n", dir);
} else {
fd = open(dir, O_RDWR);
read(fd, msg.dataBuf, strlen(msg.dataBuf));
close(fd);
write(c_fd, &msg, sizeof(msg));
printf("ok\n");
}
break;
case QUIT:
write(c_fd, &msg, sizeof(msg));
close(c_fd);
exit(-1);
break;
}
return ret;
}
void cmd_serverMsg_handler(struct MSG msg, int c_fd) {
int n_read;
int fd;
char *file = NULL;
struct MSG getmsg;
n_read = read(c_fd, &getmsg, sizeof(getmsg));
if (n_read == 0) {
printf("server quit\n");
}
if (getmsg.type == 8) {
file = getDir(msg.cmd);
fd = open(file, O_RDWR | O_CREAT, 0600);
printf("open success\n");
write(fd, getmsg.cmd, strlen(getmsg.cmd));
} else {
printf("-------------------------------------\n");
printf("%s\n", getmsg.cmd);
printf("-------------------------------------\n");
}
}
int main(int argc, char **argv) {
int c_fd;
int ret;
struct sockaddr_in c_addr;
struct MSG msg;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if (argc != 3) {
printf("params is no good\n");
exit(-1);
}
// 1. 创建socket
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1) {
perror("socket");
exit(-1);
}
// 2. 连接服务器
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &c_addr.sin_addr);
if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1) {
perror("connect");
exit(-1);
}
printf("connect....\n");
// 3. 处理命令
while (1) {
memset(msg.cmd, 0, sizeof(msg));
gets(msg.cmd);
ret = cmd_handler(msg, c_fd);
if (ret == CD || ret == LLS || ret == LCD || ret == QUIT || ret == PUT) {
continue;
}
if (ret == -1) {
printf("this is command no exist\n");
continue;
}
cmd_serverMsg_handler(msg, c_fd);
}
close(c_fd);
return 0;
}
config.h
cpp
#define LS 0
#define PWD 1
#define CD 2
#define GET 3
#define PUT 4
#define LLS 5
#define LCD 6
#define QUIT 7
struct MSG {
int type;
char cmd[1024];
char dataBuf[1024];
};
通过上述代码示例,展示了如何通过socket编程实现客户端和服务器之间的通信,包括简单的消息传递、多客户端处理以及文件传输等功能。希望这些示例代码能帮助理解和应用网络编程中的一些基本概念和技术。