1.TCP服务端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SERV_PORT 5001
//本机合法IP地址
#define SERV_IP "192.168.9.30 "
int main()
{
//1.创建流式套接字
int listenFd = socket( AF_INET, SOCK_STREAM, 0);
if( listenFd < 0 ){
perror("socket error");
exit(1);
}
printf( "socket ok!\n" );
//2.绑定本机IP地址和端口号
struct sockaddr_in sin;
memset( &sin, 0, sizeof(sin) );
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
sin.sin_addr.s_addr = inet_addr(SERV_IP);
int ret = bind(listenFd, (struct sockaddr *)&sin, sizeof(sin) );
if( ret < 0 ){
perror( "bind" );
exit(1);
}
printf("bind ok!\n");
//3.设置监听套接字
ret = listen(listenFd, 5);
if(ret < 0){
perror("listen");
exit(1);
}
printf("listen ok!\n");
//4.等待客户端连接
int connFd = accept(listenFd, NULL, NULL );
if(connFd < 0){
perror("accept");
exit(1);
}
printf("accept ok!\n");
//5.接受/发送消息
char buf[BUFSIZ];
while(1){
memset( buf, 0, sizeof(buf) );
ret = recv( connFd, buf, sizeof(buf)-1, 0 );
if( ret < 0 ){ //出错
perror( "recv" );
continue;
}
if( 0 == ret){ //对端关闭套接字
printf("client closes the socket!\n");
break;
}
printf("recv:%s\n", buf);
}
//6.关闭套接字
close(listenFd);
close(connFd);
return 0;
}
1.1TCP服务端优化
1.等待连接加循环,修改关闭套接字
2.地址端口快速重用,不用等MSL,就可重连
int on = 1;
setsockopt ( listenFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
setsockopt()
设置 SO_REUSEADDR
选项。该选项允许在 bind()
函数中使用已被其他 socket 使用的地址.
具体来说,当一个 socket 被关闭后,它所占用的端口号将会被系统保留一段时间,以确保任何可能延迟到达的数据都能够被传递完毕。
如果在这段时间内有一个新的 socket 尝试绑定到同样的端口上,就会出现 “Address already in use” 错误。
第二个参数SOL_SOCKET
指定了要设置的 socket 层面的选项,
"socket options level", "socket 选项级别" 的意思。
第三个参数 SO_REUSEADDR
指定了要设置的选项名称,
第四个参数 &on
是一个指向选项值的指针,用于设置选项的值为 1(打开选项)
最后一个参数 sizeof(on)
是选项值的大小。
返回值为 0,则表示操作成功;
返回值为 -1,则表示操作失败,可以使用 errno 变量来查看具体的错误信息。
3.IP地址不用改
4.显示连接的客户端的地址
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SERV_PORT 5001
int main()
{
//1.创建流式套接字
int listenFd = socket( AF_INET, SOCK_STREAM, 0);
if( listenFd < 0 ){
perror("socket error");
exit(1);
}
printf( "socket ok!\n" );
//优化2:允许地址和端口号快速重用
int on = 1;
setsockopt( listenFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
//2.绑定本机IP地址和端口号
struct sockaddr_in sin;
memset( &sin, 0, sizeof(sin) );
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
//优化3:IP地址绑到0.0.0.0(全0监听,即本机所有网卡所有IP)
sin.sin_addr.s_addr = htonl( INADDR_ANY );
int ret = bind(listenFd, (struct sockaddr *)&sin, sizeof(sin) );
if( ret < 0 ){
perror( "bind error" );
exit(1);
}
printf("bind ok!\n");
//3.设置监听套接字
ret = listen(listenFd, 5);
if(ret < 0){
perror("listen error");
exit(1);
}
printf("listen ok!\n");
//优化4:显示客户端的地址
struct sockaddr_in cin; //定义客户端地址结构
socklen_t len;
//4.等待客户端连接
while(1){//优化1:允许多个客户端接入
memset( &cin, 0, sizeof(cin) );
len = sizeof(sin);
int connFd = accept(listenFd, (struct sockaddr *)&cin, &len );
if(connFd < 0){
perror("accept error");
exit(1);
}
printf("accept (%s:%d) ok!\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port) );
//5.接受/发送消息
char buf[BUFSIZ];
while(1){
memset( buf, 0, sizeof(buf) );
ret = recv( connFd, buf, sizeof(buf)-1, 0 );
if( ret < 0 ){ //出错
perror( "recv error" );
continue;
}
if( 0 == ret){ //对端关闭套接字
printf("client closes the socket!\n");
break;
}
//消息处理
printf("message from client:%s\n", buf);
//发送消息
printf("server send message:");
memset(buf, 0, sizeof(buf));
if(fgets(buf, sizeof(buf), stdin) == NULL ){
printf("fgets error\n");
exit(1);
}
send( connFd, buf, strlen(buf), 0 );
}
close(connFd);
}
//6.关闭套接字
close(listenFd);
return 0;
}
2.TCP客户端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define QUIT_STR "quit"
void usage( const char* s ){
printf("\n %s serv_ip, serv_port" , s);
printf("\n serv_ip:server ip address");
printf("\n serv_port:server port (5000 < serv_port < 65536)");
}
int main( int argc, char* argv[] )
{
//参数检测
if(argc != 3){
usage( argv[0] );
exit(1);
}
int port = atoi(argv[2]);
if(port <= 5000 || port > 65535 ){
usage( argv[0]);
exit(1);
}
//1.socket 创建流式套接字
int sockFd = socket(AF_INET, SOCK_STREAM, 0);
if( sockFd < 0 ){
perror("socket error");
exit(1);
}
printf("socket ok!\n");
//2.bind(可选)一般不绑定IP地址和端口号,由操作系统自动选定
//3.connect连接服务器
struct sockaddr_in sin;
memset( &sin, 0, sizeof(sin) );
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = inet_addr(argv[1]);
int ret = connect(sockFd, (struct sockaddr*)&sin, sizeof(sin) );
if( ret < 0 ){
perror("connect error");
exit(1);
}
printf("connect ok\n");
//4.send/recv收发消息
char buf[BUFSIZ];
while(1){
printf("client send message:");
memset(buf, 0, sizeof(buf));
if(fgets(buf, sizeof(buf), stdin) == NULL ){
printf("fgets error\n");
continue;
}
//输入quit,客户端退出
if( strncmp(buf, QUIT_STR, strlen(QUIT_STR)) == 0 ){
printf("input quit, client exit!\n");
break;
}
send( sockFd, buf, strlen(buf), 0);
//客户端接受消息
memset( buf, 0, sizeof(buf) );
ret = recv( sockFd, buf, sizeof(buf)-1, 0 );
if( ret < 0 ){ //出错
perror( "recv error" );
exit(1);
}
if( 0 == ret){ //对端关闭套接字
printf("server closes the socket!\n");
break;
}
//消息处理
printf("message from server:%s\n", buf);
}
//5.close关闭套接字
close(sockFd);
return 0;
}
客户端和服务器实现了:
客户端发消息->服务器收消息->服务器发消息->客户端收消息