[C语言] Socket编程简单例子/Client+Server

一、原理

=====1.基于TCP协议的服务器端程序流程:=======
1)创建套接字(socket)
2)绑定套接字(bind)
3)将套接字设为监听,准备接收客户请求(listen)
4)等待客户请求的到来,当请求到来后,接受请求,返回一个对应于此次连接的套接字(accept)
5)用返回的套接字与客户端进行通信(send/recv)
6)返回,等待另一客户请求
7)关闭套接字
====2.基于TCP协议的客户端程序流程:=======
1)创建套接字(socket)
2)向服务器发出连接请求(connect)
3)和服务器端进行通信(send/recv)
4)关闭套接字
在服务器端调用accept函数时,程序就会等待客户端调用connect函数发出连接请求,然后接收请求,于是双方就建立了连接,之后,服务器端和客户端就可以利用send和recv函数进行通信了。

#define StartThread(thrFun) CloseHandle(CreateThread(NULL,0,thrFun,NULL,0,NULL))

DWORD WINAPI _FuncThread(LPVOID param); //供参考的全局线程函数


======3.基于UDP的服务器端编写======
1)创建套接字(socket)
2)绑定(bind)
3)等待接收数据(recvfrom)
4)关闭套接字
4.基于UDP的客户端编写
1)创建套接字(socket)
2)向服务器发送数据(sendto)
3)关闭套接字
在所有的套接字编程中第一步都是加载套接字库(WSAStartup)
对于每一个WSAStartup函数的成功调用,在最后都要对应一个WSACleanUp调用。

//#pragma warning(disable:4996) //提高兼容性

二、源码:

1.[WIN32] TCP服务器端:

#include <Winsock2.h>
#include <stdio.h>

int main()
{
 WORD wVersionRequested;
 WSADATA wsaData;
 int err;
 
 wVersionRequested = MAKEWORD( 1, 1 );
 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 ) {
  return -1;
 }
 

 if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
  WSACleanup( );
  return -1; 
 }
 SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);

 SOCKADDR_IN addrSrv;
 addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
 addrSrv.sin_family=AF_INET;
 addrSrv.sin_port=htons(6000);

 bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

 listen(sockSrv,5);

 SOCKADDR_IN addrClient;


 while(1)
 { //循环短连接

  int len=sizeof(SOCKADDR);
  SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
  char sendBuf[500];
  sprintf(sendBuf,"Welcome %s link in....",
   inet_ntoa(addrClient.sin_addr));
  send(sockConn,sendBuf,strlen(sendBuf)+1,0);
  char recvBuf[500]={0};
  len=recv(sockConn,recvBuf,500,0);
  printf("\nRecv[%d] %s\n",len,recvBuf);
  closesocket(sockConn);
 }

  WSACleanup( );

   return 0 ;
}

 

2.[WIN32] TCP客户端:

#include <Winsock2.h>
#include <stdio.h>

int main()
{
 WORD wVersionRequested;
 WSADATA wsaData;
 int err;
 
 wVersionRequested = MAKEWORD( 1, 1 );
 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 ) {
  return -1;
 }
 

 if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
  WSACleanup( );
  return -1; 
 }
 SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);

 SOCKADDR_IN addrSrv;
 addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
 addrSrv.sin_family=AF_INET;
 addrSrv.sin_port=htons(6000);
 connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

 char recvBuf[100];
 recv(sockClient,recvBuf,100,0);
 printf("%s\n",recvBuf);
 send(sockClient,"This is lisi",strlen("This is lisi")+1,0);

 closesocket(sockClient);
 WSACleanup();

  return 0;
}

3.[WIN32] UDP服务器端

#include <Winsock2.h>
#include <stdio.h>

void main()
{
 WORD wVersionRequested;
 WSADATA wsaData;
 int err;
 
 wVersionRequested = MAKEWORD( 1, 1 );
 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 ) {
  return;
 }
 

 if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
  WSACleanup( );
  return; 
 }

 SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);
 SOCKADDR_IN addrSrv;
 addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
 addrSrv.sin_family=AF_INET;
 addrSrv.sin_port=htons(6000);

 bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

 SOCKADDR_IN addrClient;
 int len=sizeof(SOCKADDR);
 char recvBuf[100];

 recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
 printf("%s\n",recvBuf);
 closesocket(sockSrv);
 WSACleanup();
}

4.[WIN32] UDP客户端

#include <Winsock2.h>
#include <stdio.h>

void main()
{
 WORD wVersionRequested;
 WSADATA wsaData;
 int err;
 
 wVersionRequested = MAKEWORD( 1, 1 );
 
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 ) {
  return;
 }
 

 if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 ) {
  WSACleanup( );
  return; 
 }

 SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
 SOCKADDR_IN addrSrv;
 addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
 addrSrv.sin_family=AF_INET;
 addrSrv.sin_port=htons(6000);

 sendto(sockClient,"Hello",strlen("Hello")+1,0,
  (SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
 closesocket(sockClient);
 WSACleanup();
}

//C语言做post请求demo
BOOL PostHttp_Login(SOCKET sock,CString &tokenOutStr)
{
	CString postData;
	CString str, contentStr;
	tokenOutStr = "";

	postData = "POST /api/auth/SignIn HTTP/1.1\r\n";
	str.Format("Host: %s:%d\r\n",HOST_ADDR,HOST_PORT );
	postData += str; //网址+端口
	postData += "User-Agent: python-requests/2.28.2\r\n";
	postData += "Accept-Encoding: gzip, deflate\r\n";
	postData += "Accept: */*\r\n";
	postData += "Connection: keep-alive\r\n";
	postData += "Content-Type: application/json\r\n";

	contentStr.Format("{\"USER\":\"%s\",\"Pwd\":\"%s\"}", g_str_account, g_str_key);
	str.Format("Content-Length: %d\r\n\r\n", contentStr.GetLength());

	postData += str;
	postData += contentStr;

	send(sock, postData, postData.GetLength(), 0);

	Println("POST_API:>\r\n%s\r\n==============\r\n",postData);

	char recvBuf[1024*5] = { 0 };
	int len = 0;
	int tryTm = 0;
	CString retStr;

	while (1) {
		len = recv(sock, recvBuf, sizeof(recvBuf), 0);
		Println("recv_len[%d]", len);
		if (len <= 0)
		{
			Sleep(100);
			if (tryTm++ > 30)//超时设置
			{
				AddTextln("Post ret fail!");
				return FALSE;
				
			}
			continue;
		}
		else
		{
			recvBuf[len] = 0;
			Println("[RECV](%d):%s",len,recvBuf);
		}
		recvBuf[len] = 0;
		retStr = "";
		EasyJson_GetString(recvBuf,"Token", retStr);
		if (retStr != "")
		{
			tokenOutStr = retStr;
			Println("GotToken(%d)=[%s]", len, retStr);
			break;
		}
		else
		{
			Println("JSON_Err(%d):[%s]", len,recvBuf);
			return FALSE;
		}
		
	}

	return TRUE;
}

[Linux]  TCP 服务端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>

#define SERVER_PORT (8080)

void dump_hex(char *buf,int len,char *info_str)
{
  printf("%s(%d): ",info_str,len);
  for(int i=0;i<len;i++)
  {
  	printf("%02x ",(uint8_t)buf[i]);
  }
  printf("\r\n");
}

int thread_server() {
    // 创建套接字
    int server_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (server_sock == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }


// 在server_sock绑定bind之前,设置其端口复用
	int opt = 1;
	setsockopt( server_sock, SOL_SOCKET,SO_REUSEADDR, 
		 (const void *)&opt, sizeof(opt) );
					   
    // 设置服务器地址信息
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    // 绑定套接字到服务器地址
    if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 监听连接请求
    if (listen(server_sock, 5) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

loop:
    printf("Server port: %d,listenning...\n",SERVER_PORT );

    // 接受客户端连接请求
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
    if (client_sock == -1) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    printf("Client connected,todo recv work.\n");

    // 在这里可以进行与客户端的数据收发交互
    int sz = 0;
    long total = 0;
    char rxBuf[1024];
    while(1)
    {
        //阻塞式接收client数据
        sz=recv(client_sock,rxBuf,sizeof(rxBuf),0);
        if(sz<=0)
        {
           break;
        }
        total+=sz;
        dump_hex(rxBuf,sz,"SrvRecv");
        printf("total recv = %ld bytes.\n",total);
        usleep(10*1000);
    }        
    close(client_sock);// 关闭client套接字
    printf("accept client again.\n");
    goto loop;
    
    
    close(server_sock);//关闭server套接字

    return 0;
}


int main() {
 pthread_t tid_a;
 pthread_create(&tid_a, NULL, thread_server, NULL);
 while(1)
 {
   //stop here
   sleep(1) ;
 }
 return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值