赞
踩
tcp提供客户与服务器的连接,一个TCP客户建立一个与服务器的连接,并与能够服务器交换数据,然后终止连接,提供可靠性。当TCP向另一端发送数据时,它要求对端返回一个确认,如果确认没有收到,TCP自动重传数据,并等待更长时间。
UDP是一个简单的传输层协议,提供无连接的服务,不需要与客户端建立连接。UDP客户端与服务器不必存在长期的关系,缺乏可靠性。协议不保证分组能够最终到达目的地,不保证各个分组按先后顺序跨网络保持不变,也不保证每个分组只到达一次。
Socket编程通常称为“套接字”,用于描述IP地址,端口和系统资源,是通信链的句柄。其使用与文件操作类似。
#include "net_exp.h" int main(int argc, char **argv) { /* 建立服务端套接字 */ int server_sockfd; if ((server_sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { /*(协议族、网络类型,数据传送方式,协议0)*/ perror("socket error"); return 1; } /* 监听端口 */ struct sockaddr_in server_addr; memset(&server_addr, sizeof(server_addr),0); /*(s,c,n)将s所指地址前n比特用常数c填充*/ server_addr.sin_family =AF_INET; /*地址族AF_INET*/ server_addr.sin_addr.s_addr =htonl(INADDR_ANY); /*ip地址*/ server_addr.sin_port =htons(TCP_SERVER_PORT); /*端口号*/ if (bind(server_sockfd,(struct sockaddr_in*)&server_addr,sizeof(server_addr)) == -1) { /*(sockfd,指针指向要绑定给sockfd的协议地址,长度)*/ perror("bind error"); return 1; } if (listen(server_sockfd,1) == -1) { /*(sockfd,指定此套接口排队最大连接数目)*/ perror("listen error"); return 1; }; /* 建立tcp连接 */ int client_sockfd; struct sockaddr_in client_addr; unsigned int client_addr_len = sizeof(struct sockaddr_in); if ((client_sockfd = accept(server_sockfd,(struct sockaddr_in*)&client_addr,&client_addr_len)) == -1) { /*(sockfd,客户端协议地址信息,长度)*/ perror("accept error"); return 1; } printf("accept client %s:%d\n", inet_ntoa(client_addr.sin_addr), client_addr.sin_port); /* 接收数据 */ while(1){ size_t pkt_len; char recv_buf[TCP_BUF_LENGTH]; pkt_len = recv(client_sockfd,recv_buf,sizeof(recv_buf),0); /*(socktfd,缓冲区地址,长度,标志)*/ if (pkt_len == -1) { perror("recv error"); return 1; } if (pkt_len == 0) { /* 连接被远端关闭 */ printf("finish\n"); return 0; } /* 输出接收到的信息 */ recv_buf[pkt_len] = '\0'; printf("[TCP RECEIVER] receive msg[%d bytes]\n", pkt_len); printf("\t%s\n", recv_buf); /* 发送信息 */ char xia[255]; gets(xia); if (send(client_sockfd,xia,sizeof(xia),0) == -1) { /*(socktfd,缓冲区地址,长度,标志)*/ perror("send error"); return 1; } } /* 关闭套接字 */ close(client_sockfd); /*(socktfd)*/ close(server_sockfd); return 0; }
//tcp.sender #include "net_exp.h" int main(int argc, char **argv) { /* 建立套接字 */ int sockfd; if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) { perror("socket error"); return 1; } /* 建立tcp连接 */ struct sockaddr_in server_addr; memset(&server_addr,sizeof(server_addr),0); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr =inet_addr("192.168.245.128"); server_addr.sin_port =htons(TCP_SERVER_PORT); if (connect(sockfd,(struct sockaddr_in*)&server_addr,sizeof(server_addr))) { perror("connect error"); return 1; } /* 发送数据 */ while(1){ char msg[255]; gets(msg); send(sockfd,msg,strlen(msg)+1,0); /* 接收数据 */ char recv_buf[TCP_BUF_LENGTH]; size_t pkt_len = 0; pkt_len = recv(sockfd,recv_buf,sizeof(recv_buf),0); if (pkt_len == -1) { perror("recv error"); return 1; } if (pkt_len == 0) { /* 连接被远端关闭 */ printf("finish\n"); return 0; } /* 输出接收到的信息 */ recv_buf[pkt_len] = '\0'; printf("[TCP SENDER] receive echo msg[%d bytes]\n", pkt_len); printf("\t%s\n", recv_buf); } /* 关闭套接字 */ close(sockfd); return 0; }
面向连接的Client/Server结构中,服务器首先启动,通过调用socket()建立一个套接口,然后调用bind()将该套接口和本地网络地址联系在一起,再调用listen()使套接口做好侦听准备。并规定它的请求队列长度。之后调用accept()来接收连接。客户在建立套接口后就可以调用connect()和服务器建立连接。一旦连接建立,客户机就可以与服务器之间通过调用send()和recv()来发送和接收数据。最后,传输完成后,双方调用close()关闭套接口。
//udp.receiver #include "net_exp.h" int main(int argc, char **argv) { /* 建立套接字 */ int sockfd; if ((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1) { perror("socket error"); return 1; } /* 绑定端口 */ struct sockaddr_in server_addr; memset(&server_addr, sizeof(server_addr),0); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(UDP_SERVER_PORT); if (bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)) == -1) { perror("bind error"); return 1; } /* 接收数据 */ struct sockaddr_in client_addr; int client_addr_len; char recv_buf[UDP_BUF_LENGTH]; size_t pkt_len; while (1) { memset(recv_buf, sizeof(recv_buf), 0); client_addr_len = sizeof(client_addr); pkt_len = recvfrom(sockfd,recv_buf,sizeof(recv_buf)+1,0,(struct sockaddr*)&client_addr,client_addr_len); recv_buf[pkt_len] = '\0'; printf("[UDP_RECEIVER] receive msg[%d bytes]\n", pkt_len); printf("\t%s\n", recv_buf); } /* 关闭套接字 */ close(sockfd); return 0; }
//udp.sender #include "net_exp.h" int main(int argc, char **argv) { /* 建立套接字 */ int socket_fd; if ((socket_fd = socket(AF_INET,SOCK_DGRAM,0)) == -1) { perror("socket error"); return 1; } /* 发送数据 */ struct sockaddr_in server_addr; memset(&server_addr, sizeof(server_addr),0); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr =inet_addr("192.168.245.128"); server_addr.sin_port = htons(UDP_SERVER_PORT); int counter = 0; char send_buf[UDP_BUF_LENGTH]; while (1) { memset(send_buf, sizeof(send_buf), 0); printf("sending data packet with #: %d\n", counter); sprintf(send_buf, "data packet with #: %d.", counter); sendto(socket_fd,send_buf,sizeof(send_buf),0,(struct sockaddr*)&server_addr,sizeof(server_addr)); counter++; if (counter > 10) break; sleep(1); } /* 关闭套接字 */ close(socket_fd); return 0; }
在无连接的Client/Server结构中,服务器使用socket()和bind()建立和联系socket。由于此时socket是无连接的,服务器使用recvfrom()从socket接口接收数据。客户端也调用bind()而不调用connect()。因为并未在两个端口建立点到点的连接,因此sendto()要求程序提供一个参数指明目的地址信息。secvfrom()不需要建立连接,它对到达相连接的协议端口的任何数据做出响应。取出数据报的同时,它将保存发送此数据包的进程的网络地址及包本身。
#ifndef NETEXP_H #define NETEXP_H #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <time.h> #include <pthread.h> #define TCP_SERVER_ADDRESS "127.0.0.1" #define TCP_SERVER_PORT 8001 #define TCP_BUF_LENGTH 1000 #define CONN_NUM 10 #define UDP_SERVER_ADDRESS "127.0.0.1" #define UDP_SERVER_PORT 8002 #define UDP_BUF_LENGTH 1000 #define RDT_SERVER_ADDRESS "127.0.0.1" #define RDT_SEND_LFILE_NAME "data_long_send.mp4" #define RDT_RECV_LFILE_NAME "data_long_recv.mp4" #define RDT_SEND_SFILE_NAME "abc_send.pdf" #define RDT_RECV_SFILE_NAME "abc_recv.pdf" #define RDT_ACK_MSG "SUCC" #define RDT_NACK_MSG "FAIL" #define RDT_RECV_PORT 8003 #define RDT_SEND_PORT 8004 #define RDT_BEGIN_SEQ 1 #define RDT_SENDWIN_LEN 10 #define RDT_PKT_LOSS_RATE 10 #define RDT_TIME_OUT 50000 #define RDT_HEADER_LEN (4 + 4) #define RDT_DATA_LEN 1000 #define RDT_PKT_LEN ( RDT_DATA_LEN + RDT_HEADER_LEN ) #define RDT_CTRL_BEGN 0 #define RDT_CTRL_DATA 1 #define RDT_CTRL_ACK 2 #define RDT_CTRL_END 3 typedef struct _STATE_PKT { struct timeval send_time; int pkt_seq; int pkt_len; int state; //init 0 , sent 1 , acked 2 , timeout 3 , empty 4 char rdt_pkt[RDT_PKT_LEN]; }STATE_PKT; typedef struct _SLD_WIN { //[send_left, send_right) sequence number int win_len; int send_left; int send_right; STATE_PKT rdt_pkts[RDT_SENDWIN_LEN]; pthread_mutex_t lock; }SLD_WIN; int pack_rdt_pkt( char *data_buf, char *rdt_pkt, int data_len, int seq_num, int flag ); int unpack_rdt_pkt( char *data_buf, char *rdt_pkt, int pkt_len, int *seq_num, int *flag ); void udt_sendto( int sock_fd, char *pkt, int pkt_len, int flags, struct sockaddr *recv_addr, int addr_len ); int time_out( struct timeval time_1, struct timeval time_2 ); #endif
linux下在文件夹下打开端口运行make命令,会生成四个可执行文件在build子目录下: tcp_receiver,tcp_sender,udp_receiver,udp_sender
然后在子目录下打开两个端口分别运行receiver和sender,可以实现两机聊天功能。
如果make命令不存在,可以尝试使用gcc。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。