龙空技术网

c/c++linux网络编程----基于TCP协议的客户端--服务器socket实例

Hu先生Linux后台开发 437

前言:

目前同学们对“c语言socket文件传输”大体比较关切,各位老铁们都想要学习一些“c语言socket文件传输”的相关文章。那么小编也在网上汇集了一些有关“c语言socket文件传输””的相关内容,希望姐妹们能喜欢,看官们快快来学习一下吧!

1. socket

socket指的是某一主机的 ip地址和端口号

2. socket常见API

创建socket

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

//个人理解:客户端和服务器之间的通信,其实也是进程间的通信,

//但是这是在网络中的,那么进程在网络中发送数据是要知道对方

//的端口号和ip地址才能将数据发送给对方,所以创建socket

//是存进程的ip和端口号

参数

domain: 表示哪一层的网络协议,有ipv4和ipv6等其他选择

type:表示你要以什么形式进程数据传输;例如:面向数据包还是面向字节流

protocol:一般传0,因为前面两个参数已经确定了要将数据发送给谁

绑定端口号

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr,

socklen_t addrlen);

//关于绑定(个人理解):

//绑定是为了将服务器的ip和端口号写入创建的socket文件里面,

//创建的socket的文件没有东西,此时将自己的套接字信息写入文件;

//如果没有写套接字信息,系统就会默认分配;此时写入是为了固定

//死套接字信息,告诉所有客户端是一个固定的服务器端口号和ip;

//如果让系统自动分配,那么第二次启动时,服务器的ip和端口号不一致

//就会导致客户端在此连接服务器时,找不到服务器

参数

sockfd:刚才创建的套接字

const struct sockaddr* addr:这是一个结构体的指针,用于服务器自己的套接字信息,这样才能将保证服务器的套接字信息是固定的

len:结构的大小

监听

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

int listen(int sockfd, int backlog);

//监听:用于TCP协议进行传输时,服务器需要进行监听,

//因为基于TCP的数据传输是面向连接的,并且是一对多的,

//可能有多个客户端来连接服务器,此时将服务器设定成监听状态,

//谁连接了服务器,服务器就跟谁建立连接,建立连接后,进行数据传输

参数:

sockfd:用于监听的套接字,服务器设置成监听状态,查看有哪个客户端想来连接我

backlog:表示可以连接的客户端个数

接受请求

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

//接受请求:用于服务器获取客户端的连接;这里的返回值也是

//一个文件描述符,指向的也是套接字文件,这里用于客户端和

//服务器端之间进行通信;和上面的监听套接字不一样,监听状态

//要一直存在,查看有没有客户端来连接我

参数

sockfd:监听套接字,这是一个媒介,当客户端来了,创建一个套接字存自己和客户的信息,让监听套接字继续监听

结构体指针:这里的结构体存放的是客户端的ip和端口号;这是给程序员看的,其实也就是给人看的,如果你想知道客户端的ip和端口号,就接受,如果你不关心,可以写NULL

结构体大小的指针:这里是一个传出传入型参数,将客户端的ip和端口号存入,必定要知道大小

收数据

#include <sys/types.h>

#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

//由于TCP是面向连接的,前面也已经知道我连的是哪个客户端了,

//在这里可以直接进行通信,进行收数据和发数据

参数:

sockfd:刚才建立连接时创建的socket文件描述符

buf:你接受的数据存放的缓冲区

len:你期望读多少数据

flags:0表示阻塞式接收数据,非0表示非阻塞时收数据`

发数据

#include <sys/types.h>

#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

//和收数据是相对的,这里不在解释

建立连接

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr,

socklen_t addrlen);

//这里的连接指的是客户端主动去连接服务器,因为服务器只有一个,

//客户端有多个,客户端要主动去连服务器才可以建立连接

参数:

sockfd:和服务器创建socket文件的作用是一样的,不在多做解释

存ip地址和端口号:既然我要去连接服务器,就要知道连接的哪个服务器,不能乱连

结构体长度:ip和端口号存在结构体中,将结构体的首地址传过去,必定要知道大小

3. TCP协议的传输特点

面向连接:服务器一旦和客户端建立连接,就需要为此分配资源,建立连接关系,当断开连接时,需要释放资源

面向字节流:以字节来读取和写入,字节数的大小完全取决于自己

可靠传输:由于服务器已经和客户端建立了通信,就会保证数据能够传输成功,不会存在丢数据的现象

4. 客户端和服务器传输流程图5. 基于多线程的客户端服务器传输

服务器端:

#include <stdio.h>

#include <unistd.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <sys/types.h>

#include <stdlib.h>

#include <string.h>

#include <pthread.h>

pthread_t tid;

void* ProcessWorker(void* arg)

{

int fd = *(int*)arg;

char buf[128];

while(1){//不断从客户端拿数据,在发回给客户端

ssize_t s = read(fd, buf, sizeof(buf));

if(s < 0){

perror("read");

break;

}

else if(s == 0){

printf("client quit...\n");

break;

}

buf[s] = 0;

printf("client# %s\n",buf);

send(fd, buf, strlen(buf), 0);

}

close(fd);

}

// ./server ip port

int main(int argc, char* argv[])

{

if(argc != 3){

printf("Usage ./server [ip] [port]\n");

return 1;

}

//创建套接字

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

if(sockfd < 0){

perror("socket");

return 2;

}

//绑定端口号

struct sockaddr_in server;

server.sin_family = AF_INET;

server.sin_addr.s_addr = inet_addr(argv[1]);

server.sin_port = htons(atoi(argv[2]));

if(bind(sockfd, (struct sockaddr*)&server, sizeof(server)) < 0){//绑定失败

perror("bind");

return 3;

}

//监听

if(listen(sockfd, 5) < 0){

perror("listen");

return 4;

}

struct sockaddr_in client;//用于接收到客户端的ip和端口号

socklen_t len = sizeof(client);

char buf[128];

for(;;){//服务器一直运行

int clientfd = accept(sockfd, (struct sockaddr*)&client, &len);

if(clientfd < 0){//当没有接收到客户端,尝试重新接收

perror("accept");

continue;

}

printf("Get connet [%s] [%d]\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));

//创建线程,主线程去监听,新线程去处理

pthread_create(&tid, NULL, ProcessWorker, (void*)&clientfd);

//在这里不能关闭文件描述符,因为主线程和新线程看到的是一个文件描述符,当把文件描述符关闭后,新线程无法获取从客户端发送的请求或消息

pthread_detach(tid);//并将新线程分离出去

}

return 0;

}

服务器端:

#include <stdio.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <string.h>

// ./client ip地址 端口号

int main(int argc,char* argv[])

{

//进程 ip地址 端口号

if(argc != 3){

perror("Usage ./server [ip] [port]\n");

return 1;

}

//ipv4 面向字节流 创建socket

int sock = socket(AF_INET, SOCK_STREAM,0);

if(sock < 0){

perror("socket");

return 2;

}

struct sockaddr_in server;//绑定的是服务器的ip和端口号

server.sin_family = AF_INET;//协议族

server.sin_addr.s_addr = inet_addr(argv[1]);//将ip专程4字节ip地址,在转成大端字节序

server.sin_port = htons(atoi(argv[2]));

//连接服务器

int ret = connect(sock,(struct sockaddr*)&server,sizeof(server));

if(ret < 0){//连接失败

perror("connect");

return 3;

}

//表示连接成功

printf("Connect success...\n");

char buf[1024];

while(1){

printf("client: ");

fflush(stdout);

ssize_t s = read(0, buf, sizeof(buf)-1);

if(s > 0){//读取数据

buf[s-1] = 0;

write(sock, buf, strlen(buf));

}

else{

break;

}

ssize_t rd = read(sock, buf, sizeof(buf)-1);

if(rd > 0){

buf[rd] = 0;

printf("server# %s\n",buf);

}

}

close(sock);

return 0;

}

6. 基于多进程的客户端服务器传输

#include <stdio.h>

#include <unistd.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <sys/types.h>

#include <stdlib.h>

#include <string.h>

// ./server ip port

int main(int argc, char* argv[])

{

if(argc != 3){

printf("Usage ./server [ip] [port]\n");

return 1;

}

//创建套接字

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

if(sockfd < 0){

perror("socket");

return 2;

}

//绑定端口号

struct sockaddr_in server;

server.sin_family = AF_INET;

server.sin_addr.s_addr = inet_addr(argv[1]);

server.sin_port = htons(atoi(argv[2]));

if(bind(sockfd, (struct sockaddr*)&server, sizeof(server)) < 0){//绑定失败

perror("bind");

return 3;

}

//监听

if(listen(sockfd, 5) < 0){

perror("listen");

return 4;

}

struct sockaddr_in client;//用于接收到客户端的ip和端口号

socklen_t len = sizeof(client);

char buf[128];

for(;;){//服务器一直运行

int clientfd = accept(sockfd, (struct sockaddr*)&client, &len);

if(clientfd < 0){//当没有接收到客户端,尝试重新接受

perror("accept");

continue;

}

printf("Get connet [%s] [%d]\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));

pid_t pid = fork();

if(pid == 0){//子进程创建孙子进程,然后退出

if( fork() == 0){//创建的子进程,去处理客户端发送过来的消息

//1.从客户端接受消息,在将其发送回去

while(1){//子进程不断从客户端读数据,在将其发回去

ssize_t s = recv(clientfd, buf, sizeof(buf)-1, 0);

if(s > 0){

buf[s] = 0;

printf("client# %s\n",buf);

ssize_t ret = send(clientfd, buf, strlen(buf), 0);

if(ret < 0){

perror("send");

return 1;

}

}

else if(s == 0){//客户端退出

printf("client quit...\n");

break;

}

else{

break;

}

}

exit(0);//自己退出

}

else if(pid > 0){//回收子进程,继续监听有没有客户端

waitpid(pid, NULL, 0);

close(clientfd);

}

}

}

return 0;

}

更多linux免费视频资料获取 后台私信【架构】

标签: #c语言socket文件传输